From 2433293564f0b2c9b5e59dbde7e4765d57628d34 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2026 19:56:02 -0700 Subject: [PATCH] anthropic: Match other providers format for provider_metadata That is, do it as `{"anthropic": {...}}` instead of `{"provider": "anthropic", ...}` dict. This matches what gateway does and what our openai code does. BACKWARDS COMPATABILITY: this changes the public `provider_metadata` shape; signatures on messages persisted under the old flat key won't be recognized on replay. --- src/ai/providers/anthropic/protocol.py | 19 ++++++++++++++----- tests/providers/anthropic/test_adapter.py | 10 +++------- tests/providers/anthropic/test_stream.py | 10 +++------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/ai/providers/anthropic/protocol.py b/src/ai/providers/anthropic/protocol.py index 05351b21..bf4e359c 100644 --- a/src/ai/providers/anthropic/protocol.py +++ b/src/ai/providers/anthropic/protocol.py @@ -40,7 +40,14 @@ def _provider_metadata(**values: Any) -> dict[str, Any]: - return {"provider": PROVIDER_NAME, **values} + """Namespace metadata as ``{"anthropic": {...}}``.""" + return {PROVIDER_NAME: {**values}} + + +def _anthropic_metadata(pm: dict[str, Any] | None) -> dict[str, Any]: + """Read back the metadata written by :func:`_provider_metadata`.""" + meta = (pm or {}).get(PROVIDER_NAME) + return meta if isinstance(meta, dict) else {} # --------------------------------------------------------------------------- @@ -207,9 +214,9 @@ async def _messages_to_anthropic( text=text, provider_metadata=provider_metadata, ): - signature = (provider_metadata or {}).get( - "signature" - ) + signature = _anthropic_metadata( + provider_metadata + ).get("signature") if signature: content.append( { @@ -252,7 +259,9 @@ async def _messages_to_anthropic( # Result block type comes from the original wire # event ("web_search_tool_result", etc.); stored in # provider metadata when emitted. - part_metadata = part.provider_metadata or {} + part_metadata = _anthropic_metadata( + part.provider_metadata + ) wire_type = ( part_metadata.get("resultType") or f"{part.tool_name}_tool_result" diff --git a/tests/providers/anthropic/test_adapter.py b/tests/providers/anthropic/test_adapter.py index 01d23da6..8e1e407e 100644 --- a/tests/providers/anthropic/test_adapter.py +++ b/tests/providers/anthropic/test_adapter.py @@ -127,10 +127,7 @@ async def test_reasoning_signature_round_trips_from_provider_metadata( ai.assistant_message( ai.thinking( "hidden", - provider_metadata={ - "provider": "anthropic", - "signature": "sig", - }, + provider_metadata={"anthropic": {"signature": "sig"}}, ) ), ai.user_message("Hi"), @@ -161,15 +158,14 @@ async def test_builtin_tool_parts_round_trip( tool_call_id="srvtoolu_1", tool_name="web_search", tool_args='{"query":"weather"}', - provider_metadata={"provider": "anthropic"}, + provider_metadata={"anthropic": {}}, ) result = messages.BuiltinToolReturnPart( tool_call_id="srvtoolu_1", tool_name="web_search", result=[{"title": "Forecast", "url": "https://example.com"}], provider_metadata={ - "provider": "anthropic", - "resultType": "web_search_tool_result", + "anthropic": {"resultType": "web_search_tool_result"}, }, ) convo = [ diff --git a/tests/providers/anthropic/test_stream.py b/tests/providers/anthropic/test_stream.py index 58ae600b..ad0400dc 100644 --- a/tests/providers/anthropic/test_stream.py +++ b/tests/providers/anthropic/test_stream.py @@ -65,7 +65,7 @@ async def test_server_tool_use_emits_builtin_events( assert calls[0].tool_call_id == "srvtoolu_1" assert calls[0].tool_name == "web_search" assert calls[0].tool_args == '{"query":"weather"}' - assert calls[0].provider_metadata == {"provider": "anthropic"} + assert calls[0].provider_metadata == {"anthropic": {}} async def test_tool_result_block_emits_builtin_result( @@ -102,8 +102,7 @@ async def test_tool_result_block_emits_builtin_result( assert ret.tool_name == "web_search" assert ret.result == payload assert ret.provider_metadata == { - "provider": "anthropic", - "resultType": "web_search_tool_result", + "anthropic": {"resultType": "web_search_tool_result"}, } @@ -120,10 +119,7 @@ async def test_signature_delta_emits_provider_metadata( reasoning = s.message.parts[0] assert isinstance(reasoning, messages.ReasoningPart) - assert reasoning.provider_metadata == { - "provider": "anthropic", - "signature": "sig", - } + assert reasoning.provider_metadata == {"anthropic": {"signature": "sig"}} async def test_event_kinds_in_order(monkeypatch: pytest.MonkeyPatch) -> None: