From 00c35d58007ff4c29db23030acf9da4113aa0f0a Mon Sep 17 00:00:00 2001 From: Solaris-star <820622658@qq.com> Date: Tue, 21 Jul 2026 13:20:19 +0800 Subject: [PATCH 1/2] fix(types): allow missing name on function_call_arguments.done events Live Responses API streaming may omit `name` on `response.function_call_arguments.done` payloads. The generated model required `name: str`, so strict ResponseStreamEvent union validation failed (and typed access was misleading after lenient construct). Make `name` optional on the responses / beta responses done events and add unit coverage for missing and present values. Fixes #3472 --- ...onse_function_call_arguments_done_event.py | 11 +++-- ...onse_function_call_arguments_done_event.py | 12 +++-- ...onse_function_call_arguments_done_event.py | 49 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/test_response_function_call_arguments_done_event.py 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..bdda249db7 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.""" @@ -35,5 +32,13 @@ class BetaResponseFunctionCallArgumentsDoneEvent(BaseModel): type: Literal["response.function_call_arguments.done"] + name: Optional[str] = None + """The name of the function that was called. + + The live Responses API may omit this field on + `response.function_call_arguments.done` events; correlate via `item_id` when + absent. + """ + agent: Optional[Agent] = None """The agent that owns this multi-agent streaming event.""" 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..5810dac169 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,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel @@ -16,9 +17,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.""" @@ -26,3 +24,11 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel): """The sequence number of this event.""" type: Literal["response.function_call_arguments.done"] + + name: Optional[str] = None + """The name of the function that was called. + + The live Responses API may omit this field on + `response.function_call_arguments.done` events; correlate via `item_id` when + absent. + """ diff --git a/tests/test_response_function_call_arguments_done_event.py b/tests/test_response_function_call_arguments_done_event.py new file mode 100644 index 0000000000..8595a92eae --- /dev/null +++ b/tests/test_response_function_call_arguments_done_event.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import pydantic +import pytest + +from openai.types.beta.beta_response_function_call_arguments_done_event import ( + BetaResponseFunctionCallArgumentsDoneEvent, +) +from openai.types.responses.response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent, +) +from openai.types.responses.response_stream_event import ResponseStreamEvent + + +PAYLOAD_WITHOUT_NAME = { + "type": "response.function_call_arguments.done", + "arguments": "{\"city\": \"Paris\"}", + "item_id": "fc_test_item", + "output_index": 2, + "sequence_number": 10, +} + + +def test_function_call_arguments_done_accepts_missing_name() -> None: + """Live Responses API may omit `name` on done events (issue #3472).""" + event = ResponseFunctionCallArgumentsDoneEvent.model_validate(PAYLOAD_WITHOUT_NAME) + assert event.type == "response.function_call_arguments.done" + assert event.item_id == "fc_test_item" + assert event.name is None + assert event.arguments == '{"city": "Paris"}' + + +def test_function_call_arguments_done_keeps_name_when_present() -> None: + event = ResponseFunctionCallArgumentsDoneEvent.model_validate( + {**PAYLOAD_WITHOUT_NAME, "name": "get_weather"} + ) + assert event.name == "get_weather" + + +def test_response_stream_event_union_accepts_done_without_name() -> None: + # Strict union validation previously failed with missing: name. + event = pydantic.TypeAdapter(ResponseStreamEvent).validate_python(PAYLOAD_WITHOUT_NAME) + assert isinstance(event, ResponseFunctionCallArgumentsDoneEvent) + assert event.name is None + + +def test_beta_function_call_arguments_done_accepts_missing_name() -> None: + event = BetaResponseFunctionCallArgumentsDoneEvent.model_validate(PAYLOAD_WITHOUT_NAME) + assert event.name is None From c8f011004b530fe857e887c08988392ae6c54045 Mon Sep 17 00:00:00 2001 From: Solaris-star <820622658@qq.com> Date: Mon, 10 Aug 2026 11:05:10 +0800 Subject: [PATCH 2/2] test: cover optional function name on Pydantic v1 --- ...onse_function_call_arguments_done_event.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/test_response_function_call_arguments_done_event.py b/tests/test_response_function_call_arguments_done_event.py index 8595a92eae..c24df557b3 100644 --- a/tests/test_response_function_call_arguments_done_event.py +++ b/tests/test_response_function_call_arguments_done_event.py @@ -1,20 +1,18 @@ from __future__ import annotations -import pydantic -import pytest - +from openai._compat import model_parse +from openai._models import validate_type +from openai.types.responses.response_stream_event import ResponseStreamEvent from openai.types.beta.beta_response_function_call_arguments_done_event import ( BetaResponseFunctionCallArgumentsDoneEvent, ) from openai.types.responses.response_function_call_arguments_done_event import ( ResponseFunctionCallArgumentsDoneEvent, ) -from openai.types.responses.response_stream_event import ResponseStreamEvent - PAYLOAD_WITHOUT_NAME = { "type": "response.function_call_arguments.done", - "arguments": "{\"city\": \"Paris\"}", + "arguments": '{"city": "Paris"}', "item_id": "fc_test_item", "output_index": 2, "sequence_number": 10, @@ -23,7 +21,7 @@ def test_function_call_arguments_done_accepts_missing_name() -> None: """Live Responses API may omit `name` on done events (issue #3472).""" - event = ResponseFunctionCallArgumentsDoneEvent.model_validate(PAYLOAD_WITHOUT_NAME) + event = model_parse(ResponseFunctionCallArgumentsDoneEvent, PAYLOAD_WITHOUT_NAME) assert event.type == "response.function_call_arguments.done" assert event.item_id == "fc_test_item" assert event.name is None @@ -31,19 +29,17 @@ def test_function_call_arguments_done_accepts_missing_name() -> None: def test_function_call_arguments_done_keeps_name_when_present() -> None: - event = ResponseFunctionCallArgumentsDoneEvent.model_validate( - {**PAYLOAD_WITHOUT_NAME, "name": "get_weather"} - ) + event = model_parse(ResponseFunctionCallArgumentsDoneEvent, {**PAYLOAD_WITHOUT_NAME, "name": "get_weather"}) assert event.name == "get_weather" def test_response_stream_event_union_accepts_done_without_name() -> None: # Strict union validation previously failed with missing: name. - event = pydantic.TypeAdapter(ResponseStreamEvent).validate_python(PAYLOAD_WITHOUT_NAME) + event = validate_type(type_=ResponseStreamEvent, value=PAYLOAD_WITHOUT_NAME) assert isinstance(event, ResponseFunctionCallArgumentsDoneEvent) assert event.name is None def test_beta_function_call_arguments_done_accepts_missing_name() -> None: - event = BetaResponseFunctionCallArgumentsDoneEvent.model_validate(PAYLOAD_WITHOUT_NAME) + event = model_parse(BetaResponseFunctionCallArgumentsDoneEvent, PAYLOAD_WITHOUT_NAME) assert event.name is None