This commit is contained in:
Timothy Jaeryang Baek
2026-05-19 22:25:39 +04:00
parent eb3076c1b0
commit cfa6908d57
+59 -1
View File
@@ -130,6 +130,64 @@ def get_content_from_message(message: dict) -> str | None:
return None
def reconcile_tool_pairs(messages: list[dict]) -> list[dict]:
"""Drop unpaired tool_use / tool_result from a reconstructed conversation.
Stored output can be incomplete — a tool result may be missing (e.g. the
knowledge base was updated mid-chat, or the call was interrupted), or a
tool call may be missing while its result survived. Strict providers
(Anthropic, AWS Bedrock Converse) reject either direction of mismatch.
Well-formed output is unaffected: every id pairs, so nothing is stripped.
"""
completed_tool_call_ids = {
message['tool_call_id']
for message in messages
if message.get('role') == 'tool' and message.get('tool_call_id')
}
requested_tool_call_ids = {
tool_call['id']
for message in messages
for tool_call in message.get('tool_calls') or ()
if message.get('role') == 'assistant' and tool_call.get('id')
}
reconciled_messages = []
for message in messages:
role = message.get('role')
# Orphan tool result — no assistant ever claimed this call_id.
if role == 'tool' and message.get('tool_call_id') not in requested_tool_call_ids:
continue
# Non-assistant or no tool_calls — pass through unchanged.
if role != 'assistant' or not message.get('tool_calls'):
reconciled_messages.append(message)
continue
# Keep only tool_calls whose id received a tool-role response.
valid_tool_calls = [
tool_call
for tool_call in message['tool_calls']
if tool_call.get('id') in completed_tool_call_ids
]
if valid_tool_calls:
reconciled_messages.append({**message, 'tool_calls': valid_tool_calls})
continue
# All tool_calls were orphans — keep the message only if it
# carries meaningful text or reasoning content.
content = message.get('content', '')
has_meaningful_content = content.strip() if isinstance(content, str) else content
if has_meaningful_content or message.get('reasoning_content'):
reconciled_messages.append(
{key: value for key, value in message.items() if key != 'tool_calls'}
)
return reconciled_messages
def convert_output_to_messages(
output: list,
raw: bool = False,
@@ -296,7 +354,7 @@ def convert_output_to_messages(
# Flush remaining content/tool_calls
flush_pending()
return messages
return reconcile_tool_pairs(messages)
def get_last_user_message(messages: list[dict]) -> str | None: