fixes
This commit is contained in:
60
ai_client.py
60
ai_client.py
@@ -417,13 +417,44 @@ def _strip_cache_controls(history: list[dict]):
|
||||
if isinstance(block, dict):
|
||||
block.pop("cache_control", None)
|
||||
|
||||
def _repair_anthropic_history(history: list[dict]):
|
||||
"""
|
||||
If history ends with an assistant message that contains tool_use blocks
|
||||
without a following user tool_result message, append a synthetic tool_result
|
||||
message so the history is valid before the next request.
|
||||
"""
|
||||
if not history:
|
||||
return
|
||||
last = history[-1]
|
||||
if last.get("role") != "assistant":
|
||||
return
|
||||
content = last.get("content", [])
|
||||
# Find tool_use blocks (content may be a list of dicts or SDK objects)
|
||||
tool_use_ids = []
|
||||
for block in content:
|
||||
if isinstance(block, dict):
|
||||
if block.get("type") == "tool_use":
|
||||
tool_use_ids.append(block["id"])
|
||||
else:
|
||||
# SDK object
|
||||
if getattr(block, "type", None) == "tool_use":
|
||||
tool_use_ids.append(block.id)
|
||||
if not tool_use_ids:
|
||||
return
|
||||
# Append a synthetic tool_result for each dangling tool_use
|
||||
history.append({
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "tool_result",
|
||||
"tool_use_id": tid,
|
||||
"content": "Tool call was not completed (session interrupted).",
|
||||
}
|
||||
for tid in tool_use_ids
|
||||
],
|
||||
})
|
||||
|
||||
def _send_anthropic(md_content: str, user_message: str, base_dir: str) -> str:
|
||||
"""
|
||||
Send via Anthropic using chunked inline text.
|
||||
Context is split into <=_ANTHROPIC_CHUNK_SIZE char blocks with
|
||||
cache_control:ephemeral on the last block, then the user message is appended.
|
||||
"""
|
||||
try:
|
||||
_ensure_anthropic_client()
|
||||
|
||||
@@ -434,6 +465,7 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str) -> str:
|
||||
]
|
||||
|
||||
_strip_cache_controls(_anthropic_history)
|
||||
_repair_anthropic_history(_anthropic_history)
|
||||
_anthropic_history.append({"role": "user", "content": user_content})
|
||||
|
||||
n_chunks = len(context_blocks)
|
||||
@@ -513,9 +545,25 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str) -> str:
|
||||
"tool_use_id": block.id,
|
||||
"content": output,
|
||||
})
|
||||
elif block.type == "tool_use":
|
||||
# Unknown tool - return an error result so history stays valid
|
||||
tool_results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": block.id,
|
||||
"content": f"ERROR: unknown tool '{block.name}'",
|
||||
})
|
||||
|
||||
# Always append tool_results when stop_reason was tool_use,
|
||||
# even if the list is somehow empty (shouldn't happen, but be safe)
|
||||
if not tool_results:
|
||||
break
|
||||
# Synthesise a result for every tool_use block to keep history valid
|
||||
for block in response.content:
|
||||
if block.type == "tool_use":
|
||||
tool_results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": block.id,
|
||||
"content": "ERROR: tool could not be executed.",
|
||||
})
|
||||
|
||||
_anthropic_history.append({
|
||||
"role": "user",
|
||||
|
||||
Reference in New Issue
Block a user