|
12 | 12 | ResponseOutputMessage, |
13 | 13 | ResponseOutputText, |
14 | 14 | ) |
| 15 | +from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary |
15 | 16 |
|
16 | 17 | from agents import Agent |
17 | 18 | from agents.handoffs import HandoffInputData, nest_handoff_history |
18 | 19 | from agents.items import ( |
19 | 20 | HandoffCallItem, |
20 | 21 | HandoffOutputItem, |
21 | 22 | MessageOutputItem, |
| 23 | + ReasoningItem, |
22 | 24 | ToolApprovalItem, |
23 | 25 | ToolCallItem, |
24 | 26 | ToolCallOutputItem, |
@@ -97,6 +99,16 @@ def _create_message_item(agent: Agent) -> MessageOutputItem: |
97 | 99 | return MessageOutputItem(agent=agent, raw_item=raw_item, type="message_output_item") |
98 | 100 |
|
99 | 101 |
|
| 102 | +def _create_reasoning_item(agent: Agent) -> ReasoningItem: |
| 103 | + """Create a mock ReasoningItem.""" |
| 104 | + raw_item = ResponseReasoningItem( |
| 105 | + id="reasoning_123", |
| 106 | + type="reasoning", |
| 107 | + summary=[Summary(text="Thinking about handoff", type="summary_text")], |
| 108 | + ) |
| 109 | + return ReasoningItem(agent=agent, raw_item=raw_item, type="reasoning_item") |
| 110 | + |
| 111 | + |
100 | 112 | def _create_tool_approval_item(agent: Agent) -> ToolApprovalItem: |
101 | 113 | """Create a mock ToolApprovalItem.""" |
102 | 114 | raw_item = { |
@@ -157,6 +169,28 @@ def test_tool_approval_items_are_skipped(self): |
157 | 169 | assert len(nested.pre_handoff_items) == 0 |
158 | 170 | assert nested.input_items == () |
159 | 171 |
|
| 172 | + def test_pre_handoff_reasoning_items_are_filtered(self): |
| 173 | + """Verify ReasoningItem in pre_handoff_items is filtered. |
| 174 | +
|
| 175 | + Reasoning is represented in the summary transcript and should not be |
| 176 | + forwarded as a raw item. |
| 177 | + """ |
| 178 | + agent = _create_mock_agent() |
| 179 | + |
| 180 | + handoff_data = HandoffInputData( |
| 181 | + input_history=({"role": "user", "content": "Hello"},), |
| 182 | + pre_handoff_items=(_create_reasoning_item(agent),), |
| 183 | + new_items=(), |
| 184 | + ) |
| 185 | + |
| 186 | + nested = nest_handoff_history(handoff_data) |
| 187 | + |
| 188 | + assert len(nested.pre_handoff_items) == 0 |
| 189 | + first_item = nested.input_history[0] |
| 190 | + assert isinstance(first_item, dict) |
| 191 | + summary = str(first_item.get("content", "")) |
| 192 | + assert "reasoning" in summary |
| 193 | + |
160 | 194 | def test_new_items_handoff_output_is_filtered_for_input(self): |
161 | 195 | """Verify HandoffOutputItem in new_items is filtered from input_items. |
162 | 196 |
|
@@ -209,6 +243,35 @@ def test_message_items_are_preserved_in_new_items(self): |
209 | 243 | assert len(nested.input_items) == 1, "MessageOutputItem should be preserved in input_items" |
210 | 244 | assert isinstance(nested.input_items[0], MessageOutputItem) |
211 | 245 |
|
| 246 | + def test_reasoning_items_are_filtered_from_input_items(self): |
| 247 | + """Verify ReasoningItem in new_items is filtered from input_items. |
| 248 | +
|
| 249 | + Reasoning is summarized in the conversation transcript and should not be |
| 250 | + forwarded verbatim in nested handoff model input. |
| 251 | + """ |
| 252 | + agent = _create_mock_agent() |
| 253 | + |
| 254 | + handoff_data = HandoffInputData( |
| 255 | + input_history=({"role": "user", "content": "Hello"},), |
| 256 | + pre_handoff_items=(), |
| 257 | + new_items=( |
| 258 | + _create_reasoning_item(agent), |
| 259 | + _create_handoff_call_item(agent), |
| 260 | + _create_handoff_output_item(agent), |
| 261 | + ), |
| 262 | + ) |
| 263 | + |
| 264 | + nested = nest_handoff_history(handoff_data) |
| 265 | + |
| 266 | + assert nested.input_items is not None |
| 267 | + has_reasoning = any(isinstance(item, ReasoningItem) for item in nested.input_items) |
| 268 | + assert not has_reasoning, "ReasoningItem should be filtered from input_items" |
| 269 | + |
| 270 | + first_item = nested.input_history[0] |
| 271 | + assert isinstance(first_item, dict) |
| 272 | + summary = str(first_item.get("content", "")) |
| 273 | + assert "reasoning" in summary |
| 274 | + |
212 | 275 | def test_summary_contains_filtered_items_as_text(self): |
213 | 276 | """Verify the summary message contains the filtered tool items as text. |
214 | 277 |
|
|
0 commit comments