mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-09 13:21:42 +00:00
fix(anthropic): preserve ordered replay cache markers
This commit is contained in:
parent
80d71e8d2e
commit
e1ff736f26
2 changed files with 62 additions and 5 deletions
|
|
@ -1891,6 +1891,18 @@ def _sanitize_replay_block(b: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|||
return None
|
||||
|
||||
|
||||
def _apply_assistant_cache_control_to_last_cacheable_block(
|
||||
blocks: List[Dict[str, Any]],
|
||||
cache_control: Any,
|
||||
) -> None:
|
||||
if not isinstance(cache_control, dict):
|
||||
return
|
||||
for block in reversed(blocks):
|
||||
if isinstance(block, dict) and block.get("type") in {"text", "tool_use"}:
|
||||
block.setdefault("cache_control", dict(cache_control))
|
||||
break
|
||||
|
||||
|
||||
def _convert_assistant_message(m: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Convert an assistant message to Anthropic content blocks.
|
||||
|
||||
|
|
@ -1945,6 +1957,9 @@ def _convert_assistant_message(m: Dict[str, Any]) -> Dict[str, Any]:
|
|||
clean["input"] = redacted
|
||||
replayed.append(clean)
|
||||
if replayed:
|
||||
_apply_assistant_cache_control_to_last_cacheable_block(
|
||||
replayed, m.get("cache_control")
|
||||
)
|
||||
return {"role": "assistant", "content": replayed}
|
||||
|
||||
blocks = _extract_preserved_thinking_blocks(m)
|
||||
|
|
@ -1970,11 +1985,9 @@ def _convert_assistant_message(m: Dict[str, Any]) -> Dict[str, Any]:
|
|||
"name": fn.get("name", ""),
|
||||
"input": parsed_args,
|
||||
})
|
||||
if isinstance(m.get("cache_control"), dict):
|
||||
for block in reversed(blocks):
|
||||
if isinstance(block, dict) and block.get("type") in {"text", "tool_use"}:
|
||||
block.setdefault("cache_control", dict(m["cache_control"]))
|
||||
break
|
||||
_apply_assistant_cache_control_to_last_cacheable_block(
|
||||
blocks, m.get("cache_control")
|
||||
)
|
||||
# Kimi's /coding endpoint (Anthropic protocol) requires assistant
|
||||
# tool-call messages to carry reasoning_content when thinking is
|
||||
# enabled server-side. Preserve it as a thinking block so Kimi
|
||||
|
|
|
|||
|
|
@ -1046,6 +1046,50 @@ class TestConvertMessages:
|
|||
assert tool_use["id"] == "tc_1"
|
||||
assert tool_use["cache_control"] == {"type": "ephemeral"}
|
||||
|
||||
def test_ordered_replay_tool_use_cache_control_is_preserved(self):
|
||||
messages = apply_anthropic_cache_control([
|
||||
{"role": "system", "content": "System prompt"},
|
||||
{"role": "user", "content": "Run the tool"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"anthropic_content_blocks": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "Need a tool.",
|
||||
"signature": "sig_1",
|
||||
},
|
||||
{
|
||||
"type": "tool_use",
|
||||
"id": "tc_1",
|
||||
"name": "test_tool",
|
||||
"input": {"query": "raw"},
|
||||
},
|
||||
],
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "tc_1",
|
||||
"function": {
|
||||
"name": "test_tool",
|
||||
"arguments": '{"query":"redacted"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{"role": "tool", "tool_call_id": "tc_1", "content": "result"},
|
||||
], native_anthropic=True)
|
||||
|
||||
_, result = convert_messages_to_anthropic(messages)
|
||||
assistant_msg = [m for m in result if m["role"] == "assistant"][0]
|
||||
thinking, tool_use = assistant_msg["content"]
|
||||
|
||||
assert thinking["type"] == "thinking"
|
||||
assert "cache_control" not in thinking
|
||||
assert tool_use["type"] == "tool_use"
|
||||
assert tool_use["id"] == "tc_1"
|
||||
assert tool_use["input"] == {"query": "redacted"}
|
||||
assert tool_use["cache_control"] == {"type": "ephemeral"}
|
||||
|
||||
def test_tool_cache_control_is_preserved_on_tool_result_block(self):
|
||||
messages = apply_anthropic_cache_control([
|
||||
{"role": "system", "content": "System prompt"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue