Repository navigation
Description
Summary
The Python Anthropic provider appears to have two related serialization gaps at the final Anthropic request boundary:
- Provider-specific structured system blocks for Anthropic prompt caching are forwarded as a raw request kwarg instead of being translated into Anthropic
systemblocks. - SDK messages that contain an assistant message with interleaved
function_call,function_result,function_callcontent are serialized as one Anthropicassistantmessage containingtool_use,tool_result,tool_use. Anthropic expects tool results on the user/tool-result side of the transcript, immediately following the corresponding assistant tool use.
I traced through the public SDK path, not just private helper methods:
Agent.run(...) -> Agent._prepare_run_context(...) -> Agent._call_chat_client(...) -> BaseChatClient.get_response(...) -> Anthropic _inner_get_response(...) -> anthropic_client.beta.messages.create(...)
The generic SDK layers preserve provider-specific options and messages; the final Anthropic provider serializer is where these need to be handled.
Environment checked
- Repo:
microsoft/agent-framework - Branch checked: current
mainas of 2026-06-10, also consistent with Python releasepython-1.8.1 - Package path:
python/packages/anthropic/agent_framework_anthropic/_chat_client.py - Anthropic SDK: current provider dependency uses
anthropic>=0.80.0,<0.80.1
Issue 1: anthropic_system_blocks leaks to Anthropic API kwargs
Anthropic prompt caching requires structured system blocks with cache_control, for example:
[
{"type": "text", "text": "stable instructions", "cache_control": {"type": "ephemeral", "ttl": "1h"}},
{"type": "text", "text": "per request context"},
]The provider currently copies unknown options into run_options:
run_options = {
k: v for k, v in options.items() if v is not None and k not in {"instructions", "response_format"}
}So an option such as anthropic_system_blocks remains in the final kwargs sent to beta.messages.create(...), and system remains unset.
Expected
The provider should consume this provider-specific option, exclude it from raw request kwargs, and map it to Anthropic system blocks. If both generic instructions and structured system blocks are supplied, generic instructions should either be appended as a text block or a clear precedence rule should be documented.
Actual full-path capture
Using a dummy Anthropic client that captures the final kwargs from beta.messages.create(...) after Agent.run(...):
FULL_AGENT_PROMPT_CACHE_TRACE
has_anthropic_system_blocks= True
system= None
message_roles= ['user']
This means the raw Anthropic API call receives an unexpected anthropic_system_blocks kwarg and no structured system blocks.
Issue 2: assistant-embedded function results serialize to assistant-role tool_result
When replay/history contains one SDK assistant message with interleaved function calls and results, the Anthropic provider serializes that directly as one assistant message.
Repro shape
Message(
role="assistant",
contents=[
Content.from_function_call(call_id="call_1", name="first", arguments="{}"),
Content.from_function_result(call_id="call_1", result="ok"),
Content.from_function_call(call_id="call_2", name="second", arguments="{}"),
],
)Actual full-path capture
Using the public client.get_response(...) path with a dummy Anthropic client:
FULL_CLIENT_TOOL_RESULT_TRACE
roles= ['assistant']
types= [['tool_use', 'tool_result', 'tool_use']]
Expected
The Anthropic provider should split assistant-embedded function results into Anthropic-valid role groups before wire serialization, for example:
roles= ['assistant', 'user', 'assistant']
types= [['tool_use'], ['tool_result'], ['tool_use']]
This is related to prior Anthropic tool-call role issues, but it is not the same shape as a tool_use incorrectly landing in a user message. Here, the remaining invalid shape is tool_result being embedded in an assistant message during replay/history serialization.
Why this matters
The SDK layers between Agent.run(...) and the provider do not currently fix either shape:
Agent._prepare_run_context(...)preserves remaining provider-specific options through**opts.BaseChatClient.get_response(...)only performs compaction/annotation before_inner_get_response(...).- The Anthropic provider then serializes the messages/options directly into
beta.messages.create(...)kwargs.
So consumers cannot get Anthropic prompt caching through the standard MAF options path, and replay/history can produce invalid Anthropic tool-result role ordering unless every application normalizes this outside the provider.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
rg-ve commentedon Jun 10, 2026
Clarification after re-checking the upstream option surface:
anthropic_system_blocksis not currently a documented/typed upstream option. That part of this issue is better framed as a missing Anthropic provider feature needed for prompt caching: there is no first-class way to pass structured Anthropicsystemblocks withcache_controlthroughAgent.run(...)/ChatOptions;instructionsis string-only and is serialized to a plainsystemstring. The leak shown in the repro is what happens when an application adds a custom provider-specific option to fill that gap.\n\nSo: the prompt-cache item is an upstream feature gap plus arguably unsafe forwarding of unknown options, not an upstream regression of a supportedanthropic_system_blocksfield. The assistant-embeddedtool_resultitem is still a direct provider serialization bug.github-actions commentedon Jun 11, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
Repro: RawAnthropicClient._prepare_options() in python/packages/anthropic/agent_framework_anthropic/_chat_client.py (line 582) copies unknown option keys directly into run_options; 'anthropic_system_blocks' leaks as a raw kwarg and 'system' is never set from it. Additionally, _prepare_message_for_anthropic (line 705) serializes all content blocks of an assistant Message into one Anthropic message dict without splitting tool_result blocks into user-role messages. Minimal repro: call _prepare_options with options={'anthropic_system_blocks': [...]} or pass an assistant Message containing interleaved function_call/function_result/function_call contents and observe the resulting run_options dict.
python/packages/anthropic/tests/test_issue_6450_repro.pypython-1.8.11.0b2604ashoksmore commentedon Jun 11, 2026
Hi Evan Mattson (@moonbox3), I'd like to work on this.
Plan: To start with Issue 2 as a small PR with tests, then Issue 1 once we agree on the typed API and how it interacts with
instructions.Does that split work? Happy to take an assignment if so.