Repository navigation
Python: Surface Anthropic citations_delta annotations when streaming - #8978
Zakaria Khchiche (Zakariakhchiche) wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 1
Open (2)
What changed in this PR
Adds support for Anthropic streaming citations_delta events so citation annotations are preserved in streaming mode, aligning behavior with non-streaming responses (fixing #8977).
Changes:
- Handle
citations_deltain_parse_contents_from_anthropicby emitting an empty textContentcarrying parsed citation annotations. - Extend
_parse_citations_from_anthropicto also accept the delta’s singlecitationfield. - Add a regression test that streams real Anthropic SDK delta events and asserts final response annotations.
| File | Description |
|---|---|
| python/packages/anthropic/agent_framework_anthropic/_chat_client.py | Parses citations_delta during streaming and maps it into standard text annotations. |
| python/packages/anthropic/tests/test_anthropic_client.py | Adds a regression test validating streamed citations become response annotations. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| content_blocks: Any = getattr(content_block, "citations", None) | ||
| if content_block.type == "citations_delta": | ||
| content_blocks = [content_block.citation] |
| client = create_test_anthropic_client(mock_anthropic_client) | ||
| mock_anthropic_client.beta.messages.create.side_effect = create | ||
| stream = client.get_response([Message(role="user", contents=["Weather in Paris?"])], stream=True) | ||
| _ = [update async for update in stream] |
|
Notes on the automated review:
|
| Content.from_text( | ||
| text="", | ||
| raw_representation=content_block, | ||
| annotations=self._parse_citations_from_anthropic(content_block), |
There was a problem hiding this comment.
Zakaria Khchiche (@Zakariakhchiche), streaming a citation from a plain-text document now aborts here. BetaCitationCharLocation exposes document_title, not title, but _parse_citations_from_anthropic() reads citation.title in its char_location branch. This new dispatch therefore turns a valid document citation delta into an AttributeError, terminating get_response(..., stream=True) as a ChatClientException instead of returning the response. Please map document_title and add a streaming regression using a real BetaCitationCharLocation; the new test covers only web-search citations, and the existing character-citation test supplies an invented title attribute through MagicMock.


Motivation & Context
In streaming mode Anthropic sends citations (web search results, search results, document citations) as
content_block_deltaevents withdelta.type == "citations_delta".AnthropicClienthad no case for them, so streamed responses lost every citation, while non-streaming responses kept them as text annotations.Description & Review Guide
_parse_contents_from_anthropichandlescitations_deltaby emitting an empty textContentthat carries the parsed annotation. This is the same pattern the Responses client uses forresponse.output_text.annotation.added, and text coalescing merges it into the block's text._parse_citations_from_anthropicalso accepts the delta's singlecitationfield, so all citation types keep the existing mapping.anthropicSDK events (BetaCitationsDelta+BetaTextDelta) throughget_response(stream=True)and checks the final response annotations.Related Issue
Fixes #8977
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.