Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions src/agents/handoffs/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
_SUMMARY_ONLY_INPUT_TYPES = {
"function_call",
"function_call_output",
"code_interpreter_call",
# Reasoning items can become orphaned after other summarized items are filtered.
"reasoning",
}
Expand Down
46 changes: 46 additions & 0 deletions tests/test_handoff_history_duplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
ResponseOutputMessage,
ResponseOutputText,
)
from openai.types.responses.response_code_interpreter_tool_call import (
ResponseCodeInterpreterToolCall,
)
from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary

from agents import Agent, RunConfig, Runner, function_tool, handoff
Expand Down Expand Up @@ -114,6 +117,19 @@ def _create_reasoning_item(agent: Agent) -> ReasoningItem:
return ReasoningItem(agent=agent, raw_item=raw_item, type="reasoning_item")


def _create_code_interpreter_item(agent: Agent) -> ToolCallItem:
"""Create a mock code interpreter tool call."""
raw_item = ResponseCodeInterpreterToolCall(
id="ci_123",
code="print('hello')",
container_id="cntr_123",
outputs=[],
status="completed",
type="code_interpreter_call",
)
return ToolCallItem(agent=agent, raw_item=raw_item, type="tool_call_item")


def _create_tool_approval_item(agent: Agent) -> ToolApprovalItem:
"""Create a mock ToolApprovalItem."""
raw_item = {
Expand Down Expand Up @@ -277,6 +293,36 @@ def test_reasoning_items_are_filtered_from_input_items(self):
summary = str(first_item.get("content", ""))
assert "reasoning" in summary

def test_code_interpreter_items_are_filtered_from_input_items(self):
"""Verify code interpreter calls are summarized instead of forwarded raw."""
agent = _create_mock_agent()

handoff_data = HandoffInputData(
input_history=({"role": "user", "content": "Hello"},),
pre_handoff_items=(),
new_items=(
_create_reasoning_item(agent),
_create_code_interpreter_item(agent),
_create_handoff_call_item(agent),
_create_handoff_output_item(agent),
),
)

nested = nest_handoff_history(handoff_data)

assert nested.input_items is not None
has_code_interpreter = any(
isinstance(item, ToolCallItem)
and cast(dict[str, Any], item.to_input_item()).get("type") == "code_interpreter_call"
for item in nested.input_items
)
assert not has_code_interpreter, "Code interpreter call should be filtered from input_items"

first_item = nested.input_history[0]
assert isinstance(first_item, dict)
summary = str(first_item.get("content", ""))
assert "code_interpreter_call" in summary

def test_summary_contains_filtered_items_as_text(self):
"""Verify the summary message contains the filtered tool items as text.

Expand Down