diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 26b25a25e8..49df20d2b8 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.20.0"
+ ".": "2.21.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 6c725eaa30..1d0140eb64 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 148
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-47ef7e0aaa2f2052404041650c9b4d7d8c9c51c45ef2cb081548f329c3f81a6a.yml
-openapi_spec_hash: 0207b30cf74121a12c1647e25463cee9
-config_hash: 8dca0f2dc2706c07cf2f8d0ed4dc062e
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0db5326a0fb6a30ffad9242c72872c3388ef927e8a4549ddd20aec3420541209.yml
+openapi_spec_hash: 9523fe30739802e15c88f4e7aac44e7f
+config_hash: 948733484caf41e71093c6582dbc319c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39ed325e8e..6f558b3626 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
# Changelog
+## 2.21.0 (2026-02-13)
+
+Full Changelog: [v2.20.0...v2.21.0](https://github.com/openai/openai-python/compare/v2.20.0...v2.21.0)
+
+### Features
+
+* **api:** container network_policy and skills ([d19de2e](https://github.com/openai/openai-python/commit/d19de2ee5c74413f9dc52684b650df1898dee82b))
+
+
+### Bug Fixes
+
+* **structured outputs:** resolve memory leak in parse methods ([#2860](https://github.com/openai/openai-python/issues/2860)) ([6dcbe21](https://github.com/openai/openai-python/commit/6dcbe211f12f8470db542a5cb95724cb933786dd))
+* **webhooks:** preserve method visibility for compatibility checks ([44a8936](https://github.com/openai/openai-python/commit/44a8936d580b770f23fae79659101a27eadafad6))
+
+
+### Chores
+
+* **internal:** fix lint error on Python 3.14 ([534f215](https://github.com/openai/openai-python/commit/534f215941f504443d63509e872409a0b1236452))
+
+
+### Documentation
+
+* split `api.md` by standalone resources ([96e41b3](https://github.com/openai/openai-python/commit/96e41b398a110212ddec71436b2439343bea87d4))
+* update comment ([63def23](https://github.com/openai/openai-python/commit/63def23b7acd5c6dacf03337fe1bd08439d1dba8))
+
## 2.20.0 (2026-02-10)
Full Changelog: [v2.19.0...v2.20.0](https://github.com/openai/openai-python/compare/v2.19.0...v2.20.0)
diff --git a/api.md b/api.md
index 54fa94da5f..da521d3418 100644
--- a/api.md
+++ b/api.md
@@ -419,30 +419,7 @@ Methods:
- client.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch
- client.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch
-# Webhooks
-
-Types:
-
-```python
-from openai.types.webhooks import (
- BatchCancelledWebhookEvent,
- BatchCompletedWebhookEvent,
- BatchExpiredWebhookEvent,
- BatchFailedWebhookEvent,
- EvalRunCanceledWebhookEvent,
- EvalRunFailedWebhookEvent,
- EvalRunSucceededWebhookEvent,
- FineTuningJobCancelledWebhookEvent,
- FineTuningJobFailedWebhookEvent,
- FineTuningJobSucceededWebhookEvent,
- RealtimeCallIncomingWebhookEvent,
- ResponseCancelledWebhookEvent,
- ResponseCompletedWebhookEvent,
- ResponseFailedWebhookEvent,
- ResponseIncompleteWebhookEvent,
- UnwrapWebhookEvent,
-)
-```
+# [Webhooks](src/openai/resources/webhooks/api.md)
Methods:
@@ -727,362 +704,11 @@ Methods:
- client.uploads.parts.create(upload_id, \*\*params) -> UploadPart
-# Responses
-
-Types:
-
-```python
-from openai.types.responses import (
- ApplyPatchTool,
- CompactedResponse,
- ComputerTool,
- ContainerAuto,
- ContainerNetworkPolicyAllowlist,
- ContainerNetworkPolicyDisabled,
- ContainerNetworkPolicyDomainSecret,
- ContainerReference,
- CustomTool,
- EasyInputMessage,
- FileSearchTool,
- FunctionShellTool,
- FunctionTool,
- InlineSkill,
- InlineSkillSource,
- LocalEnvironment,
- LocalSkill,
- Response,
- ResponseApplyPatchToolCall,
- ResponseApplyPatchToolCallOutput,
- ResponseAudioDeltaEvent,
- ResponseAudioDoneEvent,
- ResponseAudioTranscriptDeltaEvent,
- ResponseAudioTranscriptDoneEvent,
- ResponseCodeInterpreterCallCodeDeltaEvent,
- ResponseCodeInterpreterCallCodeDoneEvent,
- ResponseCodeInterpreterCallCompletedEvent,
- ResponseCodeInterpreterCallInProgressEvent,
- ResponseCodeInterpreterCallInterpretingEvent,
- ResponseCodeInterpreterToolCall,
- ResponseCompactionItem,
- ResponseCompactionItemParam,
- ResponseCompletedEvent,
- ResponseComputerToolCall,
- ResponseComputerToolCallOutputItem,
- ResponseComputerToolCallOutputScreenshot,
- ResponseContainerReference,
- ResponseContent,
- ResponseContentPartAddedEvent,
- ResponseContentPartDoneEvent,
- ResponseConversationParam,
- ResponseCreatedEvent,
- ResponseCustomToolCall,
- ResponseCustomToolCallInputDeltaEvent,
- ResponseCustomToolCallInputDoneEvent,
- ResponseCustomToolCallOutput,
- ResponseError,
- ResponseErrorEvent,
- ResponseFailedEvent,
- ResponseFileSearchCallCompletedEvent,
- ResponseFileSearchCallInProgressEvent,
- ResponseFileSearchCallSearchingEvent,
- ResponseFileSearchToolCall,
- ResponseFormatTextConfig,
- ResponseFormatTextJSONSchemaConfig,
- ResponseFunctionCallArgumentsDeltaEvent,
- ResponseFunctionCallArgumentsDoneEvent,
- ResponseFunctionCallOutputItem,
- ResponseFunctionCallOutputItemList,
- ResponseFunctionShellCallOutputContent,
- ResponseFunctionShellToolCall,
- ResponseFunctionShellToolCallOutput,
- ResponseFunctionToolCall,
- ResponseFunctionToolCallItem,
- ResponseFunctionToolCallOutputItem,
- ResponseFunctionWebSearch,
- ResponseImageGenCallCompletedEvent,
- ResponseImageGenCallGeneratingEvent,
- ResponseImageGenCallInProgressEvent,
- ResponseImageGenCallPartialImageEvent,
- ResponseInProgressEvent,
- ResponseIncludable,
- ResponseIncompleteEvent,
- ResponseInput,
- ResponseInputAudio,
- ResponseInputContent,
- ResponseInputFile,
- ResponseInputFileContent,
- ResponseInputImage,
- ResponseInputImageContent,
- ResponseInputItem,
- ResponseInputMessageContentList,
- ResponseInputMessageItem,
- ResponseInputText,
- ResponseInputTextContent,
- ResponseItem,
- ResponseLocalEnvironment,
- ResponseMcpCallArgumentsDeltaEvent,
- ResponseMcpCallArgumentsDoneEvent,
- ResponseMcpCallCompletedEvent,
- ResponseMcpCallFailedEvent,
- ResponseMcpCallInProgressEvent,
- ResponseMcpListToolsCompletedEvent,
- ResponseMcpListToolsFailedEvent,
- ResponseMcpListToolsInProgressEvent,
- ResponseOutputAudio,
- ResponseOutputItem,
- ResponseOutputItemAddedEvent,
- ResponseOutputItemDoneEvent,
- ResponseOutputMessage,
- ResponseOutputRefusal,
- ResponseOutputText,
- ResponseOutputTextAnnotationAddedEvent,
- ResponsePrompt,
- ResponseQueuedEvent,
- ResponseReasoningItem,
- ResponseReasoningSummaryPartAddedEvent,
- ResponseReasoningSummaryPartDoneEvent,
- ResponseReasoningSummaryTextDeltaEvent,
- ResponseReasoningSummaryTextDoneEvent,
- ResponseReasoningTextDeltaEvent,
- ResponseReasoningTextDoneEvent,
- ResponseRefusalDeltaEvent,
- ResponseRefusalDoneEvent,
- ResponseStatus,
- ResponseStreamEvent,
- ResponseTextConfig,
- ResponseTextDeltaEvent,
- ResponseTextDoneEvent,
- ResponseUsage,
- ResponseWebSearchCallCompletedEvent,
- ResponseWebSearchCallInProgressEvent,
- ResponseWebSearchCallSearchingEvent,
- SkillReference,
- Tool,
- ToolChoiceAllowed,
- ToolChoiceApplyPatch,
- ToolChoiceCustom,
- ToolChoiceFunction,
- ToolChoiceMcp,
- ToolChoiceOptions,
- ToolChoiceShell,
- ToolChoiceTypes,
- WebSearchPreviewTool,
- WebSearchTool,
-)
-```
-
-Methods:
-
-- client.responses.create(\*\*params) -> Response
-- client.responses.retrieve(response_id, \*\*params) -> Response
-- client.responses.delete(response_id) -> None
-- client.responses.cancel(response_id) -> Response
-- client.responses.compact(\*\*params) -> CompactedResponse
-
-## InputItems
-
-Types:
-
-```python
-from openai.types.responses import ResponseItemList
-```
-
-Methods:
-
-- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem]
-
-## InputTokens
-
-Types:
-
-```python
-from openai.types.responses import InputTokenCountResponse
-```
-
-Methods:
-
-- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse
-
-# Realtime
-
-Types:
-
-```python
-from openai.types.realtime import (
- AudioTranscription,
- ConversationCreatedEvent,
- ConversationItem,
- ConversationItemAdded,
- ConversationItemCreateEvent,
- ConversationItemCreatedEvent,
- ConversationItemDeleteEvent,
- ConversationItemDeletedEvent,
- ConversationItemDone,
- ConversationItemInputAudioTranscriptionCompletedEvent,
- ConversationItemInputAudioTranscriptionDeltaEvent,
- ConversationItemInputAudioTranscriptionFailedEvent,
- ConversationItemInputAudioTranscriptionSegment,
- ConversationItemRetrieveEvent,
- ConversationItemTruncateEvent,
- ConversationItemTruncatedEvent,
- ConversationItemWithReference,
- InputAudioBufferAppendEvent,
- InputAudioBufferClearEvent,
- InputAudioBufferClearedEvent,
- InputAudioBufferCommitEvent,
- InputAudioBufferCommittedEvent,
- InputAudioBufferDtmfEventReceivedEvent,
- InputAudioBufferSpeechStartedEvent,
- InputAudioBufferSpeechStoppedEvent,
- InputAudioBufferTimeoutTriggered,
- LogProbProperties,
- McpListToolsCompleted,
- McpListToolsFailed,
- McpListToolsInProgress,
- NoiseReductionType,
- OutputAudioBufferClearEvent,
- RateLimitsUpdatedEvent,
- RealtimeAudioConfig,
- RealtimeAudioConfigInput,
- RealtimeAudioConfigOutput,
- RealtimeAudioFormats,
- RealtimeAudioInputTurnDetection,
- RealtimeClientEvent,
- RealtimeConversationItemAssistantMessage,
- RealtimeConversationItemFunctionCall,
- RealtimeConversationItemFunctionCallOutput,
- RealtimeConversationItemSystemMessage,
- RealtimeConversationItemUserMessage,
- RealtimeError,
- RealtimeErrorEvent,
- RealtimeFunctionTool,
- RealtimeMcpApprovalRequest,
- RealtimeMcpApprovalResponse,
- RealtimeMcpListTools,
- RealtimeMcpProtocolError,
- RealtimeMcpToolCall,
- RealtimeMcpToolExecutionError,
- RealtimeMcphttpError,
- RealtimeResponse,
- RealtimeResponseCreateAudioOutput,
- RealtimeResponseCreateMcpTool,
- RealtimeResponseCreateParams,
- RealtimeResponseStatus,
- RealtimeResponseUsage,
- RealtimeResponseUsageInputTokenDetails,
- RealtimeResponseUsageOutputTokenDetails,
- RealtimeServerEvent,
- RealtimeSession,
- RealtimeSessionCreateRequest,
- RealtimeToolChoiceConfig,
- RealtimeToolsConfig,
- RealtimeToolsConfigUnion,
- RealtimeTracingConfig,
- RealtimeTranscriptionSessionAudio,
- RealtimeTranscriptionSessionAudioInput,
- RealtimeTranscriptionSessionAudioInputTurnDetection,
- RealtimeTranscriptionSessionCreateRequest,
- RealtimeTruncation,
- RealtimeTruncationRetentionRatio,
- ResponseAudioDeltaEvent,
- ResponseAudioDoneEvent,
- ResponseAudioTranscriptDeltaEvent,
- ResponseAudioTranscriptDoneEvent,
- ResponseCancelEvent,
- ResponseContentPartAddedEvent,
- ResponseContentPartDoneEvent,
- ResponseCreateEvent,
- ResponseCreatedEvent,
- ResponseDoneEvent,
- ResponseFunctionCallArgumentsDeltaEvent,
- ResponseFunctionCallArgumentsDoneEvent,
- ResponseMcpCallArgumentsDelta,
- ResponseMcpCallArgumentsDone,
- ResponseMcpCallCompleted,
- ResponseMcpCallFailed,
- ResponseMcpCallInProgress,
- ResponseOutputItemAddedEvent,
- ResponseOutputItemDoneEvent,
- ResponseTextDeltaEvent,
- ResponseTextDoneEvent,
- SessionCreatedEvent,
- SessionUpdateEvent,
- SessionUpdatedEvent,
- TranscriptionSessionUpdate,
- TranscriptionSessionUpdatedEvent,
-)
-```
-
-## ClientSecrets
+# [Responses](src/openai/resources/responses/api.md)
-Types:
-
-```python
-from openai.types.realtime import (
- RealtimeSessionClientSecret,
- RealtimeSessionCreateResponse,
- RealtimeTranscriptionSessionCreateResponse,
- RealtimeTranscriptionSessionTurnDetection,
- ClientSecretCreateResponse,
-)
-```
-
-Methods:
-
-- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse
-
-## Calls
-
-Methods:
-
-- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent
-- client.realtime.calls.accept(call_id, \*\*params) -> None
-- client.realtime.calls.hangup(call_id) -> None
-- client.realtime.calls.refer(call_id, \*\*params) -> None
-- client.realtime.calls.reject(call_id, \*\*params) -> None
-
-# Conversations
-
-Types:
-
-```python
-from openai.types.conversations import (
- ComputerScreenshotContent,
- Conversation,
- ConversationDeleted,
- ConversationDeletedResource,
- Message,
- SummaryTextContent,
- TextContent,
- InputTextContent,
- OutputTextContent,
- RefusalContent,
- InputImageContent,
- InputFileContent,
-)
-```
-
-Methods:
-
-- client.conversations.create(\*\*params) -> Conversation
-- client.conversations.retrieve(conversation_id) -> Conversation
-- client.conversations.update(conversation_id, \*\*params) -> Conversation
-- client.conversations.delete(conversation_id) -> ConversationDeletedResource
-
-## Items
-
-Types:
-
-```python
-from openai.types.conversations import ConversationItem, ConversationItemList
-```
-
-Methods:
+# [Realtime](src/openai/resources/realtime/api.md)
-- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList
-- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem
-- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem]
-- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation
+# [Conversations](src/openai/resources/conversations/api.md)
# Evals
diff --git a/pyproject.toml b/pyproject.toml
index 326c715f08..fe2e394592 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "2.20.0"
+version = "2.21.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -84,7 +84,7 @@ format = { chain = [
# run formatting again to fix any inconsistencies when imports are stripped
"format:ruff",
]}
-"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
+"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'"
"format:ruff" = "ruff format"
"lint" = { chain = [
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 440a8a45c8..0399bbf742 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -63,7 +63,6 @@
from .resources.models import Models, AsyncModels
from .resources.videos import Videos, AsyncVideos
from .resources.batches import Batches, AsyncBatches
- from .resources.webhooks import Webhooks, AsyncWebhooks
from .resources.beta.beta import Beta, AsyncBeta
from .resources.chat.chat import Chat, AsyncChat
from .resources.embeddings import Embeddings, AsyncEmbeddings
@@ -74,6 +73,7 @@
from .resources.skills.skills import Skills, AsyncSkills
from .resources.uploads.uploads import Uploads, AsyncUploads
from .resources.realtime.realtime import Realtime, AsyncRealtime
+ from .resources.webhooks.webhooks import Webhooks, AsyncWebhooks
from .resources.responses.responses import Responses, AsyncResponses
from .resources.containers.containers import Containers, AsyncContainers
from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning
diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py
index 17bb9306aa..98901c0446 100644
--- a/src/openai/_module_client.py
+++ b/src/openai/_module_client.py
@@ -11,7 +11,6 @@
from .resources.models import Models
from .resources.videos import Videos
from .resources.batches import Batches
- from .resources.webhooks import Webhooks
from .resources.beta.beta import Beta
from .resources.chat.chat import Chat
from .resources.embeddings import Embeddings
@@ -22,6 +21,7 @@
from .resources.skills.skills import Skills
from .resources.uploads.uploads import Uploads
from .resources.realtime.realtime import Realtime
+ from .resources.webhooks.webhooks import Webhooks
from .resources.responses.responses import Responses
from .resources.containers.containers import Containers
from .resources.fine_tuning.fine_tuning import FineTuning
diff --git a/src/openai/_utils/_compat.py b/src/openai/_utils/_compat.py
index dd703233c5..2c70b299ce 100644
--- a/src/openai/_utils/_compat.py
+++ b/src/openai/_utils/_compat.py
@@ -26,7 +26,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool:
else:
import types
- return tp is Union or tp is types.UnionType
+ return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap]
def is_typeddict(tp: Type[Any]) -> bool:
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 4987d1ca18..0d4ef7b71c 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.20.0" # x-release-please-version
+__version__ = "2.21.0" # x-release-please-version
diff --git a/src/openai/lib/_parsing/__init__.py b/src/openai/lib/_parsing/__init__.py
index 4d454c3a20..08591f43f4 100644
--- a/src/openai/lib/_parsing/__init__.py
+++ b/src/openai/lib/_parsing/__init__.py
@@ -6,7 +6,6 @@
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,
- solve_response_format_t as solve_response_format_t,
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 7903732a4a..7a1bded1de 100644
--- a/src/openai/lib/_parsing/_completions.py
+++ b/src/openai/lib/_parsing/_completions.py
@@ -138,7 +138,7 @@ def parse_chat_completion(
choices.append(
construct_type_unchecked(
- type_=cast(Any, ParsedChoice)[solve_response_format_t(response_format)],
+ type_=ParsedChoice[ResponseFormatT],
value={
**choice.to_dict(),
"message": {
@@ -153,15 +153,12 @@ def parse_chat_completion(
)
)
- return cast(
- ParsedChatCompletion[ResponseFormatT],
- construct_type_unchecked(
- type_=cast(Any, ParsedChatCompletion)[solve_response_format_t(response_format)],
- value={
- **chat_completion.to_dict(),
- "choices": choices,
- },
- ),
+ return construct_type_unchecked(
+ type_=ParsedChatCompletion[ResponseFormatT],
+ value={
+ **chat_completion.to_dict(),
+ "choices": choices,
+ },
)
@@ -201,20 +198,6 @@ def maybe_parse_content(
return None
-def solve_response_format_t(
- response_format: type[ResponseFormatT] | ResponseFormatParam | Omit,
-) -> type[ResponseFormatT]:
- """Return the runtime type for the given response format.
-
- If no response format is given, or if we won't auto-parse the response format
- then we default to `None`.
- """
- if has_rich_response_format(response_format):
- return response_format
-
- return cast("type[ResponseFormatT]", _default_response_format)
-
-
def has_parseable_input(
*,
response_format: type | ResponseFormatParam | Omit,
diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py
index 4bed171df7..5676eb0b63 100644
--- a/src/openai/lib/_parsing/_responses.py
+++ b/src/openai/lib/_parsing/_responses.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import json
-from typing import TYPE_CHECKING, Any, List, Iterable, cast
+from typing import TYPE_CHECKING, List, Iterable, cast
from typing_extensions import TypeVar, assert_never
import pydantic
@@ -12,7 +12,7 @@
from ..._compat import PYDANTIC_V1, model_parse_json
from ..._models import construct_type_unchecked
from .._pydantic import is_basemodel_type, is_dataclass_like_type
-from ._completions import solve_response_format_t, type_to_response_format_param
+from ._completions import type_to_response_format_param
from ...types.responses import (
Response,
ToolParam,
@@ -56,7 +56,6 @@ def parse_response(
input_tools: Iterable[ToolParam] | Omit | None,
response: Response | ParsedResponse[object],
) -> ParsedResponse[TextFormatT]:
- solved_t = solve_response_format_t(text_format)
output_list: List[ParsedResponseOutputItem[TextFormatT]] = []
for output in response.output:
@@ -69,7 +68,7 @@ def parse_response(
content_list.append(
construct_type_unchecked(
- type_=cast(Any, ParsedResponseOutputText)[solved_t],
+ type_=ParsedResponseOutputText[TextFormatT],
value={
**item.to_dict(),
"parsed": parse_text(item.text, text_format=text_format),
@@ -79,7 +78,7 @@ def parse_response(
output_list.append(
construct_type_unchecked(
- type_=cast(Any, ParsedResponseOutputMessage)[solved_t],
+ type_=ParsedResponseOutputMessage[TextFormatT],
value={
**output.to_dict(),
"content": content_list,
@@ -123,15 +122,12 @@ def parse_response(
else:
output_list.append(output)
- return cast(
- ParsedResponse[TextFormatT],
- construct_type_unchecked(
- type_=cast(Any, ParsedResponse)[solved_t],
- value={
- **response.to_dict(),
- "output": output_list,
- },
- ),
+ return construct_type_unchecked(
+ type_=ParsedResponse[TextFormatT],
+ value={
+ **response.to_dict(),
+ "output": output_list,
+ },
)
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index c4610e2120..5f072cafbd 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -33,7 +33,6 @@
maybe_parse_content,
parse_chat_completion,
get_input_tool_by_name,
- solve_response_format_t,
parse_function_tool_arguments,
)
from ...._streaming import Stream, AsyncStream
@@ -663,7 +662,7 @@ def _content_done_events(
# type variable, e.g. `ContentDoneEvent[MyModelType]`
cast( # pyright: ignore[reportUnnecessaryCast]
"type[ContentDoneEvent[ResponseFormatT]]",
- cast(Any, ContentDoneEvent)[solve_response_format_t(response_format)],
+ cast(Any, ContentDoneEvent),
),
type="content.done",
content=choice_snapshot.message.content,
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index 9c0b74b804..fb1887a7d5 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -1346,12 +1346,10 @@ def list(
limit: Number of Chat Completions to retrieve.
- 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.
+ metadata:
+ A list of metadata keys to filter the Chat Completions by. Example:
- Keys are strings with a maximum length of 64 characters. Values are strings with
- a maximum length of 512 characters.
+ `metadata[key1]=value1&metadata[key2]=value2`
model: The model used to generate the Chat Completions.
@@ -2832,12 +2830,10 @@ def list(
limit: Number of Chat Completions to retrieve.
- 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.
+ metadata:
+ A list of metadata keys to filter the Chat Completions by. Example:
- Keys are strings with a maximum length of 64 characters. Values are strings with
- a maximum length of 512 characters.
+ `metadata[key1]=value1&metadata[key2]=value2`
model: The model used to generate the Chat Completions.
diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py
index 0cbb400d4a..216097d9c8 100644
--- a/src/openai/resources/containers/containers.py
+++ b/src/openai/resources/containers/containers.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from typing import Iterable
from typing_extensions import Literal
import httpx
@@ -61,6 +62,8 @@ def create(
expires_after: container_create_params.ExpiresAfter | Omit = omit,
file_ids: SequenceNotStr[str] | Omit = omit,
memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit,
+ network_policy: container_create_params.NetworkPolicy | Omit = omit,
+ skills: Iterable[container_create_params.Skill] | 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,
@@ -80,6 +83,10 @@ def create(
memory_limit: Optional memory limit for the container. Defaults to "1g".
+ network_policy: Network access policy for the container.
+
+ skills: An optional list of skills referenced by id or inline data.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -96,6 +103,8 @@ def create(
"expires_after": expires_after,
"file_ids": file_ids,
"memory_limit": memory_limit,
+ "network_policy": network_policy,
+ "skills": skills,
},
container_create_params.ContainerCreateParams,
),
@@ -143,6 +152,7 @@ def list(
*,
after: str | Omit = omit,
limit: int | Omit = omit,
+ name: str | 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.
@@ -164,6 +174,8 @@ def list(
limit: A limit on the number of objects to be returned. Limit can range between 1 and
100, and the default is 20.
+ name: Filter results by container name.
+
order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
order and `desc` for descending order.
@@ -187,6 +199,7 @@ def list(
{
"after": after,
"limit": limit,
+ "name": name,
"order": order,
},
container_list_params.ContainerListParams,
@@ -261,6 +274,8 @@ async def create(
expires_after: container_create_params.ExpiresAfter | Omit = omit,
file_ids: SequenceNotStr[str] | Omit = omit,
memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit,
+ network_policy: container_create_params.NetworkPolicy | Omit = omit,
+ skills: Iterable[container_create_params.Skill] | 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,
@@ -280,6 +295,10 @@ async def create(
memory_limit: Optional memory limit for the container. Defaults to "1g".
+ network_policy: Network access policy for the container.
+
+ skills: An optional list of skills referenced by id or inline data.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -296,6 +315,8 @@ async def create(
"expires_after": expires_after,
"file_ids": file_ids,
"memory_limit": memory_limit,
+ "network_policy": network_policy,
+ "skills": skills,
},
container_create_params.ContainerCreateParams,
),
@@ -343,6 +364,7 @@ def list(
*,
after: str | Omit = omit,
limit: int | Omit = omit,
+ name: str | 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.
@@ -364,6 +386,8 @@ def list(
limit: A limit on the number of objects to be returned. Limit can range between 1 and
100, and the default is 20.
+ name: Filter results by container name.
+
order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
order and `desc` for descending order.
@@ -387,6 +411,7 @@ def list(
{
"after": after,
"limit": limit,
+ "name": name,
"order": order,
},
container_list_params.ContainerListParams,
diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py
index a472cfc9f3..62659a5c3d 100644
--- a/src/openai/resources/containers/files/files.py
+++ b/src/openai/resources/containers/files/files.py
@@ -96,10 +96,11 @@ def create(
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- # 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 {})}
+ if files:
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
f"/containers/{container_id}/files",
body=maybe_transform(body, file_create_params.FileCreateParams),
@@ -309,10 +310,11 @@ async def create(
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- # 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 {})}
+ if files:
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
f"/containers/{container_id}/files",
body=await async_maybe_transform(body, file_create_params.FileCreateParams),
diff --git a/src/openai/resources/conversations/api.md b/src/openai/resources/conversations/api.md
new file mode 100644
index 0000000000..9e9181a367
--- /dev/null
+++ b/src/openai/resources/conversations/api.md
@@ -0,0 +1,42 @@
+# Conversations
+
+Types:
+
+```python
+from openai.types.conversations import (
+ ComputerScreenshotContent,
+ Conversation,
+ ConversationDeleted,
+ ConversationDeletedResource,
+ Message,
+ SummaryTextContent,
+ TextContent,
+ InputTextContent,
+ OutputTextContent,
+ RefusalContent,
+ InputImageContent,
+ InputFileContent,
+)
+```
+
+Methods:
+
+- client.conversations.create(\*\*params) -> Conversation
+- client.conversations.retrieve(conversation_id) -> Conversation
+- client.conversations.update(conversation_id, \*\*params) -> Conversation
+- client.conversations.delete(conversation_id) -> ConversationDeletedResource
+
+## Items
+
+Types:
+
+```python
+from openai.types.conversations import ConversationItem, ConversationItemList
+```
+
+Methods:
+
+- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList
+- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem
+- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem]
+- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation
diff --git a/src/openai/resources/realtime/api.md b/src/openai/resources/realtime/api.md
new file mode 100644
index 0000000000..1a178384db
--- /dev/null
+++ b/src/openai/resources/realtime/api.md
@@ -0,0 +1,137 @@
+# Realtime
+
+Types:
+
+```python
+from openai.types.realtime import (
+ AudioTranscription,
+ ConversationCreatedEvent,
+ ConversationItem,
+ ConversationItemAdded,
+ ConversationItemCreateEvent,
+ ConversationItemCreatedEvent,
+ ConversationItemDeleteEvent,
+ ConversationItemDeletedEvent,
+ ConversationItemDone,
+ ConversationItemInputAudioTranscriptionCompletedEvent,
+ ConversationItemInputAudioTranscriptionDeltaEvent,
+ ConversationItemInputAudioTranscriptionFailedEvent,
+ ConversationItemInputAudioTranscriptionSegment,
+ ConversationItemRetrieveEvent,
+ ConversationItemTruncateEvent,
+ ConversationItemTruncatedEvent,
+ ConversationItemWithReference,
+ InputAudioBufferAppendEvent,
+ InputAudioBufferClearEvent,
+ InputAudioBufferClearedEvent,
+ InputAudioBufferCommitEvent,
+ InputAudioBufferCommittedEvent,
+ InputAudioBufferDtmfEventReceivedEvent,
+ InputAudioBufferSpeechStartedEvent,
+ InputAudioBufferSpeechStoppedEvent,
+ InputAudioBufferTimeoutTriggered,
+ LogProbProperties,
+ McpListToolsCompleted,
+ McpListToolsFailed,
+ McpListToolsInProgress,
+ NoiseReductionType,
+ OutputAudioBufferClearEvent,
+ RateLimitsUpdatedEvent,
+ RealtimeAudioConfig,
+ RealtimeAudioConfigInput,
+ RealtimeAudioConfigOutput,
+ RealtimeAudioFormats,
+ RealtimeAudioInputTurnDetection,
+ RealtimeClientEvent,
+ RealtimeConversationItemAssistantMessage,
+ RealtimeConversationItemFunctionCall,
+ RealtimeConversationItemFunctionCallOutput,
+ RealtimeConversationItemSystemMessage,
+ RealtimeConversationItemUserMessage,
+ RealtimeError,
+ RealtimeErrorEvent,
+ RealtimeFunctionTool,
+ RealtimeMcpApprovalRequest,
+ RealtimeMcpApprovalResponse,
+ RealtimeMcpListTools,
+ RealtimeMcpProtocolError,
+ RealtimeMcpToolCall,
+ RealtimeMcpToolExecutionError,
+ RealtimeMcphttpError,
+ RealtimeResponse,
+ RealtimeResponseCreateAudioOutput,
+ RealtimeResponseCreateMcpTool,
+ RealtimeResponseCreateParams,
+ RealtimeResponseStatus,
+ RealtimeResponseUsage,
+ RealtimeResponseUsageInputTokenDetails,
+ RealtimeResponseUsageOutputTokenDetails,
+ RealtimeServerEvent,
+ RealtimeSession,
+ RealtimeSessionCreateRequest,
+ RealtimeToolChoiceConfig,
+ RealtimeToolsConfig,
+ RealtimeToolsConfigUnion,
+ RealtimeTracingConfig,
+ RealtimeTranscriptionSessionAudio,
+ RealtimeTranscriptionSessionAudioInput,
+ RealtimeTranscriptionSessionAudioInputTurnDetection,
+ RealtimeTranscriptionSessionCreateRequest,
+ RealtimeTruncation,
+ RealtimeTruncationRetentionRatio,
+ ResponseAudioDeltaEvent,
+ ResponseAudioDoneEvent,
+ ResponseAudioTranscriptDeltaEvent,
+ ResponseAudioTranscriptDoneEvent,
+ ResponseCancelEvent,
+ ResponseContentPartAddedEvent,
+ ResponseContentPartDoneEvent,
+ ResponseCreateEvent,
+ ResponseCreatedEvent,
+ ResponseDoneEvent,
+ ResponseFunctionCallArgumentsDeltaEvent,
+ ResponseFunctionCallArgumentsDoneEvent,
+ ResponseMcpCallArgumentsDelta,
+ ResponseMcpCallArgumentsDone,
+ ResponseMcpCallCompleted,
+ ResponseMcpCallFailed,
+ ResponseMcpCallInProgress,
+ ResponseOutputItemAddedEvent,
+ ResponseOutputItemDoneEvent,
+ ResponseTextDeltaEvent,
+ ResponseTextDoneEvent,
+ SessionCreatedEvent,
+ SessionUpdateEvent,
+ SessionUpdatedEvent,
+ TranscriptionSessionUpdate,
+ TranscriptionSessionUpdatedEvent,
+)
+```
+
+## ClientSecrets
+
+Types:
+
+```python
+from openai.types.realtime import (
+ RealtimeSessionClientSecret,
+ RealtimeSessionCreateResponse,
+ RealtimeTranscriptionSessionCreateResponse,
+ RealtimeTranscriptionSessionTurnDetection,
+ ClientSecretCreateResponse,
+)
+```
+
+Methods:
+
+- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse
+
+## Calls
+
+Methods:
+
+- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent
+- client.realtime.calls.accept(call_id, \*\*params) -> None
+- client.realtime.calls.hangup(call_id) -> None
+- client.realtime.calls.refer(call_id, \*\*params) -> None
+- client.realtime.calls.reject(call_id, \*\*params) -> None
diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md
new file mode 100644
index 0000000000..6eb1b999fa
--- /dev/null
+++ b/src/openai/resources/responses/api.md
@@ -0,0 +1,175 @@
+# Responses
+
+Types:
+
+```python
+from openai.types.responses import (
+ ApplyPatchTool,
+ CompactedResponse,
+ ComputerTool,
+ ContainerAuto,
+ ContainerNetworkPolicyAllowlist,
+ ContainerNetworkPolicyDisabled,
+ ContainerNetworkPolicyDomainSecret,
+ ContainerReference,
+ CustomTool,
+ EasyInputMessage,
+ FileSearchTool,
+ FunctionShellTool,
+ FunctionTool,
+ InlineSkill,
+ InlineSkillSource,
+ LocalEnvironment,
+ LocalSkill,
+ Response,
+ ResponseApplyPatchToolCall,
+ ResponseApplyPatchToolCallOutput,
+ ResponseAudioDeltaEvent,
+ ResponseAudioDoneEvent,
+ ResponseAudioTranscriptDeltaEvent,
+ ResponseAudioTranscriptDoneEvent,
+ ResponseCodeInterpreterCallCodeDeltaEvent,
+ ResponseCodeInterpreterCallCodeDoneEvent,
+ ResponseCodeInterpreterCallCompletedEvent,
+ ResponseCodeInterpreterCallInProgressEvent,
+ ResponseCodeInterpreterCallInterpretingEvent,
+ ResponseCodeInterpreterToolCall,
+ ResponseCompactionItem,
+ ResponseCompactionItemParam,
+ ResponseCompletedEvent,
+ ResponseComputerToolCall,
+ ResponseComputerToolCallOutputItem,
+ ResponseComputerToolCallOutputScreenshot,
+ ResponseContainerReference,
+ ResponseContent,
+ ResponseContentPartAddedEvent,
+ ResponseContentPartDoneEvent,
+ ResponseConversationParam,
+ ResponseCreatedEvent,
+ ResponseCustomToolCall,
+ ResponseCustomToolCallInputDeltaEvent,
+ ResponseCustomToolCallInputDoneEvent,
+ ResponseCustomToolCallOutput,
+ ResponseError,
+ ResponseErrorEvent,
+ ResponseFailedEvent,
+ ResponseFileSearchCallCompletedEvent,
+ ResponseFileSearchCallInProgressEvent,
+ ResponseFileSearchCallSearchingEvent,
+ ResponseFileSearchToolCall,
+ ResponseFormatTextConfig,
+ ResponseFormatTextJSONSchemaConfig,
+ ResponseFunctionCallArgumentsDeltaEvent,
+ ResponseFunctionCallArgumentsDoneEvent,
+ ResponseFunctionCallOutputItem,
+ ResponseFunctionCallOutputItemList,
+ ResponseFunctionShellCallOutputContent,
+ ResponseFunctionShellToolCall,
+ ResponseFunctionShellToolCallOutput,
+ ResponseFunctionToolCall,
+ ResponseFunctionToolCallItem,
+ ResponseFunctionToolCallOutputItem,
+ ResponseFunctionWebSearch,
+ ResponseImageGenCallCompletedEvent,
+ ResponseImageGenCallGeneratingEvent,
+ ResponseImageGenCallInProgressEvent,
+ ResponseImageGenCallPartialImageEvent,
+ ResponseInProgressEvent,
+ ResponseIncludable,
+ ResponseIncompleteEvent,
+ ResponseInput,
+ ResponseInputAudio,
+ ResponseInputContent,
+ ResponseInputFile,
+ ResponseInputFileContent,
+ ResponseInputImage,
+ ResponseInputImageContent,
+ ResponseInputItem,
+ ResponseInputMessageContentList,
+ ResponseInputMessageItem,
+ ResponseInputText,
+ ResponseInputTextContent,
+ ResponseItem,
+ ResponseLocalEnvironment,
+ ResponseMcpCallArgumentsDeltaEvent,
+ ResponseMcpCallArgumentsDoneEvent,
+ ResponseMcpCallCompletedEvent,
+ ResponseMcpCallFailedEvent,
+ ResponseMcpCallInProgressEvent,
+ ResponseMcpListToolsCompletedEvent,
+ ResponseMcpListToolsFailedEvent,
+ ResponseMcpListToolsInProgressEvent,
+ ResponseOutputAudio,
+ ResponseOutputItem,
+ ResponseOutputItemAddedEvent,
+ ResponseOutputItemDoneEvent,
+ ResponseOutputMessage,
+ ResponseOutputRefusal,
+ ResponseOutputText,
+ ResponseOutputTextAnnotationAddedEvent,
+ ResponsePrompt,
+ ResponseQueuedEvent,
+ ResponseReasoningItem,
+ ResponseReasoningSummaryPartAddedEvent,
+ ResponseReasoningSummaryPartDoneEvent,
+ ResponseReasoningSummaryTextDeltaEvent,
+ ResponseReasoningSummaryTextDoneEvent,
+ ResponseReasoningTextDeltaEvent,
+ ResponseReasoningTextDoneEvent,
+ ResponseRefusalDeltaEvent,
+ ResponseRefusalDoneEvent,
+ ResponseStatus,
+ ResponseStreamEvent,
+ ResponseTextConfig,
+ ResponseTextDeltaEvent,
+ ResponseTextDoneEvent,
+ ResponseUsage,
+ ResponseWebSearchCallCompletedEvent,
+ ResponseWebSearchCallInProgressEvent,
+ ResponseWebSearchCallSearchingEvent,
+ SkillReference,
+ Tool,
+ ToolChoiceAllowed,
+ ToolChoiceApplyPatch,
+ ToolChoiceCustom,
+ ToolChoiceFunction,
+ ToolChoiceMcp,
+ ToolChoiceOptions,
+ ToolChoiceShell,
+ ToolChoiceTypes,
+ WebSearchPreviewTool,
+ WebSearchTool,
+)
+```
+
+Methods:
+
+- client.responses.create(\*\*params) -> Response
+- client.responses.retrieve(response_id, \*\*params) -> Response
+- client.responses.delete(response_id) -> None
+- client.responses.cancel(response_id) -> Response
+- client.responses.compact(\*\*params) -> CompactedResponse
+
+## InputItems
+
+Types:
+
+```python
+from openai.types.responses import ResponseItemList
+```
+
+Methods:
+
+- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem]
+
+## InputTokens
+
+Types:
+
+```python
+from openai.types.responses import InputTokenCountResponse
+```
+
+Methods:
+
+- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse
diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py
new file mode 100644
index 0000000000..371906299b
--- /dev/null
+++ b/src/openai/resources/webhooks/__init__.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .webhooks import Webhooks as _Webhooks, AsyncWebhooks as _AsyncWebhooks
+
+
+class Webhooks(_Webhooks):
+ pass
+
+
+class AsyncWebhooks(_AsyncWebhooks):
+ pass
+
+__all__ = ["Webhooks", "AsyncWebhooks"]
diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md
new file mode 100644
index 0000000000..8e3c312eb0
--- /dev/null
+++ b/src/openai/resources/webhooks/api.md
@@ -0,0 +1,24 @@
+# Webhooks
+
+Types:
+
+```python
+from openai.types.webhooks import (
+ BatchCancelledWebhookEvent,
+ BatchCompletedWebhookEvent,
+ BatchExpiredWebhookEvent,
+ BatchFailedWebhookEvent,
+ EvalRunCanceledWebhookEvent,
+ EvalRunFailedWebhookEvent,
+ EvalRunSucceededWebhookEvent,
+ FineTuningJobCancelledWebhookEvent,
+ FineTuningJobFailedWebhookEvent,
+ FineTuningJobSucceededWebhookEvent,
+ RealtimeCallIncomingWebhookEvent,
+ ResponseCancelledWebhookEvent,
+ ResponseCompletedWebhookEvent,
+ ResponseFailedWebhookEvent,
+ ResponseIncompleteWebhookEvent,
+ UnwrapWebhookEvent,
+)
+```
diff --git a/src/openai/resources/webhooks.py b/src/openai/resources/webhooks/webhooks.py
similarity index 96%
rename from src/openai/resources/webhooks.py
rename to src/openai/resources/webhooks/webhooks.py
index 3e13d3faae..8d99568aad 100644
--- a/src/openai/resources/webhooks.py
+++ b/src/openai/resources/webhooks/webhooks.py
@@ -9,12 +9,12 @@
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 ..types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent
+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 ...types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent
__all__ = ["Webhooks", "AsyncWebhooks"]
diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py
index 32bd3f5c0a..d93da834a3 100644
--- a/src/openai/types/chat/completion_list_params.py
+++ b/src/openai/types/chat/completion_list_params.py
@@ -18,13 +18,9 @@ class CompletionListParams(TypedDict, total=False):
"""Number of Chat Completions to retrieve."""
metadata: Optional[Metadata]
- """Set of 16 key-value pairs that can be attached to an object.
+ """A list of metadata keys to filter the Chat Completions by. Example:
- 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.
+ `metadata[key1]=value1&metadata[key2]=value2`
"""
model: str
diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py
index 47101ecdb6..63d28f3955 100644
--- a/src/openai/types/container_create_params.py
+++ b/src/openai/types/container_create_params.py
@@ -2,11 +2,16 @@
from __future__ import annotations
-from typing_extensions import Literal, Required, TypedDict
+from typing import Union, Iterable
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
from .._types import SequenceNotStr
+from .responses.inline_skill_param import InlineSkillParam
+from .responses.skill_reference_param import SkillReferenceParam
+from .responses.container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam
+from .responses.container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam
-__all__ = ["ContainerCreateParams", "ExpiresAfter"]
+__all__ = ["ContainerCreateParams", "ExpiresAfter", "NetworkPolicy", "Skill"]
class ContainerCreateParams(TypedDict, total=False):
@@ -22,6 +27,12 @@ class ContainerCreateParams(TypedDict, total=False):
memory_limit: Literal["1g", "4g", "16g", "64g"]
"""Optional memory limit for the container. Defaults to "1g"."""
+ network_policy: NetworkPolicy
+ """Network access policy for the container."""
+
+ skills: Iterable[Skill]
+ """An optional list of skills referenced by id or inline data."""
+
class ExpiresAfter(TypedDict, total=False):
"""Container expiration time in seconds relative to the 'anchor' time."""
@@ -33,3 +44,8 @@ class ExpiresAfter(TypedDict, total=False):
"""
minutes: Required[int]
+
+
+NetworkPolicy: TypeAlias = Union[ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam]
+
+Skill: TypeAlias = Union[SkillReferenceParam, InlineSkillParam]
diff --git a/src/openai/types/container_create_response.py b/src/openai/types/container_create_response.py
index 0ebcc04062..34bc56ad13 100644
--- a/src/openai/types/container_create_response.py
+++ b/src/openai/types/container_create_response.py
@@ -1,11 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["ContainerCreateResponse", "ExpiresAfter"]
+__all__ = ["ContainerCreateResponse", "ExpiresAfter", "NetworkPolicy"]
class ExpiresAfter(BaseModel):
@@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel):
"""The number of minutes after the anchor before the container expires."""
+class NetworkPolicy(BaseModel):
+ """Network access policy for the container."""
+
+ type: Literal["allowlist", "disabled"]
+ """The network policy mode."""
+
+ allowed_domains: Optional[List[str]] = None
+ """Allowed outbound domains when `type` is `allowlist`."""
+
+
class ContainerCreateResponse(BaseModel):
id: str
"""Unique identifier for the container."""
@@ -50,3 +60,6 @@ class ContainerCreateResponse(BaseModel):
memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None
"""The memory limit configured for the container."""
+
+ network_policy: Optional[NetworkPolicy] = None
+ """Network access policy for the container."""
diff --git a/src/openai/types/container_list_params.py b/src/openai/types/container_list_params.py
index 4821a87d18..01ec43af32 100644
--- a/src/openai/types/container_list_params.py
+++ b/src/openai/types/container_list_params.py
@@ -23,6 +23,9 @@ class ContainerListParams(TypedDict, total=False):
Limit can range between 1 and 100, and the default is 20.
"""
+ name: str
+ """Filter results by container name."""
+
order: Literal["asc", "desc"]
"""Sort order by the `created_at` timestamp of the objects.
diff --git a/src/openai/types/container_list_response.py b/src/openai/types/container_list_response.py
index 8f39548201..bf572acd6b 100644
--- a/src/openai/types/container_list_response.py
+++ b/src/openai/types/container_list_response.py
@@ -1,11 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["ContainerListResponse", "ExpiresAfter"]
+__all__ = ["ContainerListResponse", "ExpiresAfter", "NetworkPolicy"]
class ExpiresAfter(BaseModel):
@@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel):
"""The number of minutes after the anchor before the container expires."""
+class NetworkPolicy(BaseModel):
+ """Network access policy for the container."""
+
+ type: Literal["allowlist", "disabled"]
+ """The network policy mode."""
+
+ allowed_domains: Optional[List[str]] = None
+ """Allowed outbound domains when `type` is `allowlist`."""
+
+
class ContainerListResponse(BaseModel):
id: str
"""Unique identifier for the container."""
@@ -50,3 +60,6 @@ class ContainerListResponse(BaseModel):
memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None
"""The memory limit configured for the container."""
+
+ network_policy: Optional[NetworkPolicy] = None
+ """Network access policy for the container."""
diff --git a/src/openai/types/container_retrieve_response.py b/src/openai/types/container_retrieve_response.py
index 9ba3e18c3a..b5a6d350ff 100644
--- a/src/openai/types/container_retrieve_response.py
+++ b/src/openai/types/container_retrieve_response.py
@@ -1,11 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["ContainerRetrieveResponse", "ExpiresAfter"]
+__all__ = ["ContainerRetrieveResponse", "ExpiresAfter", "NetworkPolicy"]
class ExpiresAfter(BaseModel):
@@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel):
"""The number of minutes after the anchor before the container expires."""
+class NetworkPolicy(BaseModel):
+ """Network access policy for the container."""
+
+ type: Literal["allowlist", "disabled"]
+ """The network policy mode."""
+
+ allowed_domains: Optional[List[str]] = None
+ """Allowed outbound domains when `type` is `allowlist`."""
+
+
class ContainerRetrieveResponse(BaseModel):
id: str
"""Unique identifier for the container."""
@@ -50,3 +60,6 @@ class ContainerRetrieveResponse(BaseModel):
memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None
"""The memory limit configured for the container."""
+
+ network_policy: Optional[NetworkPolicy] = None
+ """Network access policy for the container."""
diff --git a/tests/api_resources/test_containers.py b/tests/api_resources/test_containers.py
index cf173c7fd5..321d7778b0 100644
--- a/tests/api_resources/test_containers.py
+++ b/tests/api_resources/test_containers.py
@@ -39,6 +39,14 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
},
file_ids=["string"],
memory_limit="1g",
+ network_policy={"type": "disabled"},
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
)
assert_matches_type(ContainerCreateResponse, container, path=["response"])
@@ -114,6 +122,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None:
container = client.containers.list(
after="after",
limit=0,
+ name="name",
order="asc",
)
assert_matches_type(SyncCursorPage[ContainerListResponse], container, path=["response"])
@@ -199,6 +208,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
},
file_ids=["string"],
memory_limit="1g",
+ network_policy={"type": "disabled"},
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
)
assert_matches_type(ContainerCreateResponse, container, path=["response"])
@@ -274,6 +291,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N
container = await async_client.containers.list(
after="after",
limit=0,
+ name="name",
order="asc",
)
assert_matches_type(AsyncCursorPage[ContainerListResponse], container, path=["response"])
diff --git a/tests/api_resources/webhooks/__init__.py b/tests/api_resources/webhooks/__init__.py
new file mode 100644
index 0000000000..fd8019a9a1
--- /dev/null
+++ b/tests/api_resources/webhooks/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index afad5a1391..85bab4f095 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -50,13 +50,13 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[NoneType](
+ParsedChatCompletion(
choices=[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I
@@ -120,13 +120,13 @@ class Location(BaseModel):
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[Location](
+ParsedChatCompletion(
choices=[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":65,"units":"f"}',
@@ -191,13 +191,13 @@ class Location(BaseModel):
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[Location](
+ParsedChatCompletion(
choices=[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":65,"units":"f"}',
@@ -266,11 +266,11 @@ class ColorDetection(BaseModel):
assert print_obj(completion.choices[0], monkeypatch) == snapshot(
"""\
-ParsedChoice[ColorDetection](
+ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[ColorDetection](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"color":"red","hex_color_code":"#FF0000"}',
@@ -317,11 +317,11 @@ class Location(BaseModel):
assert print_obj(completion.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":64,"units":"f"}',
@@ -332,11 +332,11 @@ class Location(BaseModel):
tool_calls=None
)
),
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=1,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":65,"units":"f"}',
@@ -347,11 +347,11 @@ class Location(BaseModel):
tool_calls=None
)
),
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=2,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":63.0,"units":"f"}',
@@ -397,13 +397,13 @@ class CalendarEvent:
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[CalendarEvent](
+ParsedChatCompletion(
choices=[
- ParsedChoice[CalendarEvent](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[CalendarEvent](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}',
@@ -462,11 +462,11 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m
assert print_obj(completion.choices[0], monkeypatch) == snapshot(
"""\
-ParsedChoice[Query](
+ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Query](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -576,11 +576,11 @@ class Location(BaseModel):
assert print_obj(completion.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -627,11 +627,11 @@ class GetWeatherArgs(BaseModel):
assert print_obj(completion.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -701,11 +701,11 @@ class GetStockPrice(BaseModel):
assert print_obj(completion.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -784,11 +784,11 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch:
assert print_obj(completion.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -866,13 +866,13 @@ class Location(BaseModel):
assert isinstance(message.parsed.city, str)
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[Location](
+ParsedChatCompletion(
choices=[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":58,"units":"f"}',
@@ -943,13 +943,13 @@ class Location(BaseModel):
assert isinstance(message.parsed.city, str)
assert print_obj(completion, monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[Location](
+ParsedChatCompletion(
choices=[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":65,"units":"f"}',
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 548416dfe2..eb3a0973ac 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -63,11 +63,11 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I
@@ -84,7 +84,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
)
assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot(
"""\
-ContentDoneEvent[NoneType](
+ContentDoneEvent(
content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend
checking a reliable weather website or a weather app.",
parsed=None,
@@ -140,13 +140,13 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
assert print_obj(listener.stream.get_final_completion(), monkeypatch) == snapshot(
"""\
-ParsedChatCompletion[Location](
+ParsedChatCompletion(
choices=[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":61,"units":"f"}',
@@ -181,7 +181,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
)
assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot(
"""\
-ContentDoneEvent[Location](
+ContentDoneEvent(
content='{"city":"San Francisco","temperature":61,"units":"f"}',
parsed=Location(city='San Francisco', temperature=61.0, units='f'),
type='content.done'
@@ -320,11 +320,11 @@ class Location(BaseModel):
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":65,"units":"f"}',
@@ -335,11 +335,11 @@ class Location(BaseModel):
tool_calls=None
)
),
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=1,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":61,"units":"f"}',
@@ -350,11 +350,11 @@ class Location(BaseModel):
tool_calls=None
)
),
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=2,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='{"city":"San Francisco","temperature":59,"units":"f"}',
@@ -426,11 +426,11 @@ class Location(BaseModel):
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -495,7 +495,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=ChoiceLogprobs(
@@ -505,7 +505,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp
],
refusal=None
),
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='Foo!',
@@ -563,7 +563,7 @@ class Location(BaseModel):
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\
[
- ParsedChoice[Location](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=ChoiceLogprobs(
@@ -617,7 +617,7 @@ class Location(BaseModel):
ChatCompletionTokenLogprob(bytes=[46], logprob=-0.57687104, token='.', top_logprobs=[])
]
),
- message=ParsedChatCompletionMessage[Location](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -660,11 +660,11 @@ class GetWeatherArgs(BaseModel):
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[object](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[object](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -693,11 +693,11 @@ class GetWeatherArgs(BaseModel):
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -765,11 +765,11 @@ class GetStockPrice(BaseModel):
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[object](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[object](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -874,11 +874,11 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch:
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[object](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[object](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -926,11 +926,11 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content='\\n {\\n "location": "San Francisco, CA",\\n "weather": {\\n "temperature": "18°C",\\n
@@ -987,11 +987,11 @@ def test_allows_non_strict_tools_but_no_parsing(
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='tool_calls',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content=None,
@@ -1047,11 +1047,11 @@ def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]:
assert print_obj(state.get_final_completion().choices, monkeypatch) == snapshot(
"""\
[
- ParsedChoice[NoneType](
+ ParsedChoice(
finish_reason='stop',
index=0,
logprobs=None,
- message=ParsedChatCompletionMessage[NoneType](
+ message=ParsedChatCompletionMessage(
annotations=None,
audio=None,
content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I
diff --git a/tests/lib/utils.py b/tests/lib/utils.py
index e6b6a29434..0efdfca968 100644
--- a/tests/lib/utils.py
+++ b/tests/lib/utils.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-import inspect
+import re
from typing import Any, Iterable
from typing_extensions import TypeAlias
@@ -28,27 +28,7 @@ def __repr_args__(self: pydantic.BaseModel) -> ReprArgs:
string = rich_print_str(obj)
- # we remove all `fn_name..` occurrences
- # so that we can share the same snapshots between
- # pydantic v1 and pydantic v2 as their output for
- # generic models differs, e.g.
- #
- # v2: `ParsedChatCompletion[test_parse_pydantic_model..Location]`
- # v1: `ParsedChatCompletion[Location]`
- return clear_locals(string, stacklevel=2)
-
-
-def get_caller_name(*, stacklevel: int = 1) -> str:
- frame = inspect.currentframe()
- assert frame is not None
-
- for i in range(stacklevel):
- frame = frame.f_back
- assert frame is not None, f"no {i}th frame"
-
- return frame.f_code.co_name
-
-
-def clear_locals(string: str, *, stacklevel: int) -> str:
- caller = get_caller_name(stacklevel=stacklevel + 1)
- return string.replace(f"{caller}..", "")
+ # Pydantic v1 and v2 have different implementations of __repr__ and print out
+ # generics differently, so we strip out generic type parameters to ensure
+ # consistent snapshot tests across both versions
+ return re.sub(r"([A-Za-z_]\w*)\[[^\[\]]+\](?=\()", r"\1", string)