|
26 | 26 | _conversation_history_start = _DEFAULT_CONVERSATION_HISTORY_START |
27 | 27 | _conversation_history_end = _DEFAULT_CONVERSATION_HISTORY_END |
28 | 28 |
|
| 29 | +# Item types that are summarized in the conversation history. |
| 30 | +# They should not be forwarded verbatim to the next agent to avoid duplication. |
| 31 | +_SUMMARY_ONLY_INPUT_TYPES = { |
| 32 | + "function_call", |
| 33 | + "function_call_output", |
| 34 | +} |
| 35 | + |
29 | 36 |
|
30 | 37 | def set_conversation_history_wrappers( |
31 | 38 | *, |
@@ -67,23 +74,34 @@ def nest_handoff_history( |
67 | 74 |
|
68 | 75 | normalized_history = _normalize_input_history(handoff_input_data.input_history) |
69 | 76 | flattened_history = _flatten_nested_history_messages(normalized_history) |
70 | | - pre_items_as_inputs = [ |
71 | | - _run_item_to_plain_input(item) for item in handoff_input_data.pre_handoff_items |
72 | | - ] |
73 | | - new_items_as_inputs = [_run_item_to_plain_input(item) for item in handoff_input_data.new_items] |
| 77 | + |
| 78 | + # Convert items to plain inputs for the transcript summary. |
| 79 | + pre_items_as_inputs: list[TResponseInputItem] = [] |
| 80 | + filtered_pre_items: list[RunItem] = [] |
| 81 | + for run_item in handoff_input_data.pre_handoff_items: |
| 82 | + plain_input = _run_item_to_plain_input(run_item) |
| 83 | + pre_items_as_inputs.append(plain_input) |
| 84 | + if _should_forward_pre_item(plain_input): |
| 85 | + filtered_pre_items.append(run_item) |
| 86 | + |
| 87 | + new_items_as_inputs: list[TResponseInputItem] = [] |
| 88 | + filtered_input_items: list[RunItem] = [] |
| 89 | + for run_item in handoff_input_data.new_items: |
| 90 | + plain_input = _run_item_to_plain_input(run_item) |
| 91 | + new_items_as_inputs.append(plain_input) |
| 92 | + if _should_forward_new_item(plain_input): |
| 93 | + filtered_input_items.append(run_item) |
| 94 | + |
74 | 95 | transcript = flattened_history + pre_items_as_inputs + new_items_as_inputs |
75 | 96 |
|
76 | 97 | mapper = history_mapper or default_handoff_history_mapper |
77 | 98 | history_items = mapper(transcript) |
78 | | - filtered_pre_items = tuple( |
79 | | - item |
80 | | - for item in handoff_input_data.pre_handoff_items |
81 | | - if _get_run_item_role(item) != "assistant" |
82 | | - ) |
83 | 99 |
|
84 | 100 | return handoff_input_data.clone( |
85 | 101 | input_history=tuple(deepcopy(item) for item in history_items), |
86 | | - pre_handoff_items=filtered_pre_items, |
| 102 | + pre_handoff_items=tuple(filtered_pre_items), |
| 103 | + # new_items stays unchanged for session history. |
| 104 | + input_items=tuple(filtered_input_items), |
87 | 105 | ) |
88 | 106 |
|
89 | 107 |
|
@@ -231,6 +249,20 @@ def _split_role_and_name(role_text: str) -> tuple[str, str | None]: |
231 | 249 | return (role_text or "developer", None) |
232 | 250 |
|
233 | 251 |
|
234 | | -def _get_run_item_role(run_item: RunItem) -> str | None: |
235 | | - role_candidate = run_item.to_input_item().get("role") |
236 | | - return role_candidate if isinstance(role_candidate, str) else None |
| 252 | +def _should_forward_pre_item(input_item: TResponseInputItem) -> bool: |
| 253 | + """Return False when the previous transcript item is represented in the summary.""" |
| 254 | + role_candidate = input_item.get("role") |
| 255 | + if isinstance(role_candidate, str) and role_candidate == "assistant": |
| 256 | + return False |
| 257 | + type_candidate = input_item.get("type") |
| 258 | + return not (isinstance(type_candidate, str) and type_candidate in _SUMMARY_ONLY_INPUT_TYPES) |
| 259 | + |
| 260 | + |
| 261 | +def _should_forward_new_item(input_item: TResponseInputItem) -> bool: |
| 262 | + """Return False for tool or side-effect items that the summary already covers.""" |
| 263 | + # Items with a role should always be forwarded. |
| 264 | + role_candidate = input_item.get("role") |
| 265 | + if isinstance(role_candidate, str) and role_candidate: |
| 266 | + return True |
| 267 | + type_candidate = input_item.get("type") |
| 268 | + return not (isinstance(type_candidate, str) and type_candidate in _SUMMARY_ONLY_INPUT_TYPES) |
0 commit comments