feat(advisor) 2/4: preserve advisor blocks in ChatResponse round-trip - #177
Merged
ericleepi314 merged 1 commit intoMay 19, 2026
Merged
Conversation
Stacked on #PR1. The Anthropic provider's `_build_chat_response`
projects the SDK stream into `(content, tool_uses, ...)` for the
agent loop, but drops everything else — including server-side advisor
blocks. Once the next stack layer wires activation, those blocks need
to round-trip through history so the next turn's API call sees a
matched `server_tool_use(name=advisor)` + `advisor_tool_result` pair.
- `src/providers/base.py`: adds
`ChatResponse.raw_content_blocks: Optional[list[dict]] = None`,
the channel for passthrough blocks the projector chose not to
flatten.
- `src/providers/anthropic_provider.py`: extends `_build_chat_response`
to collect blocks where `type=='advisor_tool_result'` or
`(type=='server_tool_use' and name=='advisor')`, serialize via
`block.model_dump(exclude_none=True)`, and attach to
`raw_content_blocks`. The SDK's lenient `construct_type` preserves
the original fields on unknown discriminators (verified
empirically against `anthropic==0.88.0`), so the round-trip is
faithful.
Other server tools (web_search, code_execution, etc.) are
deliberately NOT scooped up — they have their own SDK projection.
## Test plan
- [x] `tests/test_advisor_chat_response_roundtrip.py` (6 tests):
builds a real SDK ParsedMessage via `accumulate_event` for
each of the three discriminated `advisor_tool_result` content
shapes (`advisor_result`, `advisor_redacted_result`,
`advisor_tool_result_error`), the orphan case, the non-advisor
server-tool exclusion, and a mixed-block case (text + tool_use
+ advisor pair).
- [x] Pins the SDK round-trip contract — a future SDK upgrade (e.g.
Pydantic v3, stricter discriminator handler) that silently
drops extra fields on unknown discriminators would fail the
`encrypted_content` and `error_code` assertions locally.
- [x] No regressions in adjacent provider tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7 tasks
ericleepi314
added a commit
that referenced
this pull request
Jul 7, 2026
…vation feat(advisor) 2/4: preserve advisor blocks in ChatResponse round-trip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
2/4 in the
/advisorstack. Stacked on #176.Summary
The Anthropic provider's
_build_chat_responseprojects the SDK stream into(content, tool_uses, ...)for the agent loop, but drops everything else — including server-side advisor blocks. Once PR 3 wires activation, those blocks need to round-trip through history so the next turn's API call sees a matchedserver_tool_use(name=advisor)+advisor_tool_resultpair.src/providers/base.py— addsChatResponse.raw_content_blocks: Optional[list[dict]] = None, the channel for passthrough blocks the projector chose not to flatten.src/providers/anthropic_provider.py— extends_build_chat_responseto collect blocks wheretype == 'advisor_tool_result'or(type == 'server_tool_use' and name == 'advisor'), serialize viablock.model_dump(exclude_none=True), and attach toraw_content_blocks. The SDK's lenientconstruct_typepreserves the original fields on unknown discriminators (verified empirically againstanthropic==0.88.0).Other server tools (web_search, code_execution, etc.) are deliberately NOT scooped up — they have their own SDK projection.
Stack
Test plan
tests/test_advisor_chat_response_roundtrip.py— 6 tests: builds a real SDK ParsedMessage viaaccumulate_eventfor each discriminatedadvisor_tool_resultcontent shape (advisor_result,advisor_redacted_result,advisor_tool_result_error), the orphan case, the non-advisor server-tool exclusion, and a mixed-block case.encrypted_contentanderror_codeassertions locally.🤖 Generated with Claude Code