Skip to content

Commit 48cdae4

Browse files
authored
fix: materialize iterable input history in ItemHelpers.input_to_new_input_list (#2496)
1 parent 84fa471 commit 48cdae4

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

src/agents/items.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
ValidToolOutputPydanticModelsTypeAdapter,
5858
)
5959
from .usage import Usage
60+
from .util._json import _to_dump_compatible
6061

6162
if TYPE_CHECKING:
6263
from .agent import Agent
@@ -531,7 +532,7 @@ def input_to_new_input_list(
531532
"role": "user",
532533
}
533534
]
534-
return input.copy()
535+
return cast(list[TResponseInputItem], _to_dump_compatible(input))
535536

536537
@classmethod
537538
def text_message_outputs(cls, items: list[RunItem]) -> str:

tests/test_items_helpers.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
ResponseFunctionWebSearch,
2222
)
2323
from openai.types.responses.response_function_web_search_param import ResponseFunctionWebSearchParam
24+
from openai.types.responses.response_input_item_param import ResponseInputItemParam
2425
from openai.types.responses.response_output_message import ResponseOutputMessage
2526
from openai.types.responses.response_output_message_param import ResponseOutputMessageParam
2627
from openai.types.responses.response_output_refusal import ResponseOutputRefusal
@@ -435,7 +436,7 @@ def test_to_input_items_for_reasoning() -> None:
435436

436437

437438
def test_input_to_new_input_list_copies_the_ones_produced_by_pydantic() -> None:
438-
# Given a list of message dictionaries, ensure the returned list is a deep copy.
439+
"""Validated input items should be copied and made JSON dump compatible."""
439440
original = ResponseOutputMessageParam(
440441
id="a75654dc-7492-4d1c-bce0-89e8312fbdd7",
441442
content=[
@@ -450,17 +451,21 @@ def test_input_to_new_input_list_copies_the_ones_produced_by_pydantic() -> None:
450451
status="completed",
451452
type="message",
452453
)
453-
original_json = json.dumps(original)
454-
output_item = TypeAdapter(ResponseOutputMessageParam).validate_json(original_json)
455-
new_list = ItemHelpers.input_to_new_input_list([output_item])
454+
validated = TypeAdapter(list[ResponseInputItemParam]).validate_python([original])
455+
456+
new_list = ItemHelpers.input_to_new_input_list(validated)
456457
assert len(new_list) == 1
457458
assert new_list[0]["id"] == original["id"] # type: ignore
458-
size = 0
459-
for i, item in enumerate(original["content"]):
460-
size += 1 # pydantic_core._pydantic_core.ValidatorIterator does not support len()
461-
assert item["type"] == original["content"][i]["type"] # type: ignore
462-
assert item["text"] == original["content"][i]["text"] # type: ignore
463-
assert size == 1
464459
assert new_list[0]["role"] == original["role"] # type: ignore
465460
assert new_list[0]["status"] == original["status"] # type: ignore
466461
assert new_list[0]["type"] == original["type"]
462+
assert isinstance(new_list[0]["content"], list)
463+
464+
first_content = cast(dict[str, object], new_list[0]["content"][0])
465+
assert first_content["type"] == "output_text"
466+
assert first_content["text"] == "Hey, what's up?"
467+
assert isinstance(first_content["annotations"], list)
468+
assert isinstance(first_content["logprobs"], list)
469+
470+
# This used to fail when validated payloads retained ValidatorIterator fields.
471+
json.dumps(new_list)

0 commit comments

Comments
 (0)