Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/openai/types/responses/response.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import List, Union, Optional
from typing import Any, Dict, List, Union, Optional
from typing_extensions import Literal, TypeAlias

from .tool import Tool
Expand Down Expand Up @@ -319,3 +319,31 @@ def output_text(self) -> str:
texts.append(content.text)

return "".join(texts)

def output_as_input(self) -> List[Dict[str, Any]]:
"""Return the response output items as a list of input-ready dicts for use in a subsequent turn.

This is the recommended way to build multi-turn conversations manually.
It calls ``as_input()`` on items that have that method (e.g.
:class:`ResponseOutputMessage`, :class:`ResponseReasoningItem`,
:class:`ResponseFunctionToolCall`), stripping output-only fields like
``status`` and ``encrypted_content`` that the API rejects as input.
For all other output item types, ``to_dict()`` is used as a fallback.

Example::

conversation: list[dict] = []
for msg in ["What is the capital of France?", "And Germany?"]:
conversation.append({"role": "user", "content": msg})
response = client.responses.create(
model="o4-mini", input=conversation, max_output_tokens=200,
)
conversation.extend(response.output_as_input())
"""
result: List[Dict[str, Any]] = []
for item in self.output:
if hasattr(item, "as_input"):
result.append(item.as_input())
else:
result.append(item.to_dict())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip output-only fields in output_as_input fallback

The new fallback path serializes unhandled output variants with item.to_dict() unchanged, which can still emit output-only keys and trigger the same 400 errors this change is trying to prevent. For example, ResponseToolSearchCall includes created_by (response_tool_search_call.py:30), but the corresponding input shape ToolSearchCall does not accept that field (response_input_item_param.py:153-170), so response.output_as_input() can produce invalid follow-up input whenever such items appear in response.output.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chatgpt-codex-connector
pls check again, I fixed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sanitize fallback items before returning input-ready output

output_as_input() currently uses item.to_dict() for every output variant that does not implement as_input(), which leaks output-only fields for some union members. For example, ResponseFunctionToolCallOutputItem can include created_by, but the corresponding input shape (FunctionCallOutput) does not define that field, so replaying response.output_as_input() into responses.create(input=...) can still produce 400 unknown-parameter failures for non-covered item types. This makes the new helper unreliable outside the three explicitly handled classes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip output-only fields before using fallback serialization

output_as_input() still appends raw item.to_dict() for any output item without as_input(), which means output-only fields leak back into next-turn input. This is still reproducible for items like ResponseToolSearchOutputItem, ResponseFunctionShellToolCallOutput, and ResponseApplyPatchToolCallOutput that include created_by, while their input counterparts (ResponseToolSearchOutputItemParam, ShellCallOutput, ApplyPatchCallOutput) do not define that field; in those tool-enabled conversations, conversation.extend(response.output_as_input()) can still trigger 400 unknown-parameter errors despite this fix.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return result
11 changes: 10 additions & 1 deletion src/openai/types/responses/response_function_tool_call.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import Optional
from typing import Any, Dict, Optional
from typing_extensions import Literal

from ..._models import BaseModel
Expand Down Expand Up @@ -39,3 +39,12 @@ class ResponseFunctionToolCall(BaseModel):
One of `in_progress`, `completed`, or `incomplete`. Populated when items are
returned via API.
"""

def as_input(self) -> Dict[str, Any]:
"""Return a dict representation of this item suitable for use as input in a subsequent response.

This strips output-only fields (``status``) that the API does not accept as input.
"""
data = self.to_dict()
data.pop("status", None)
return data
11 changes: 10 additions & 1 deletion src/openai/types/responses/response_output_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import List, Union, Optional
from typing import Any, Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias

from ..._utils import PropertyInfo
Expand Down Expand Up @@ -42,3 +42,12 @@ class ResponseOutputMessage(BaseModel):
sending follow-up requests, preserve and resend phase on all assistant messages
— dropping it can degrade performance. Not used for user messages.
"""

def as_input(self) -> Dict[str, Any]:
"""Return a dict representation of this item suitable for use as input in a subsequent response.

This strips output-only fields that the API does not accept as input.
"""
data = self.to_dict()
data.pop("status", None)
return data
13 changes: 12 additions & 1 deletion src/openai/types/responses/response_reasoning_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import List, Optional
from typing import Any, Dict, List, Optional
from typing_extensions import Literal

from ..._models import BaseModel
Expand Down Expand Up @@ -60,3 +60,14 @@ class ResponseReasoningItem(BaseModel):
One of `in_progress`, `completed`, or `incomplete`. Populated when items are
returned via API.
"""

def as_input(self) -> Dict[str, Any]:
"""Return a dict representation of this item suitable for use as input in a subsequent response.

This strips output-only fields (``status``, ``encrypted_content``) that the
API does not accept as input.
"""
data = self.to_dict()
data.pop("status", None)
data.pop("encrypted_content", None)
return data