docs+refactor: map the turn path to first token and extract the route's phases - #1388
Merged
Merged
Conversation
Adds docs/specs/turn-path-ttft.md: every stage of a turn (before the handler, inside it, inside the stream, inside the agent build) with what runs where, what is network, what blocks the event loop and which turn_prelude mark times it; the findings that map exposes (fresh boto3 sessions re-parse service models so warm-up never reaches the build, a full-history ListEvents on the head of every turn just to count it, the serial session_mgr/tools build, agent-turn writes on the critical path, the still-undecomposed agent_build.tools); an assessment of PR #1377; an ordered, measure-first plan that leans toward Runtime V2; and a regression plan a cheaper model can execute. Pointers from CLAUDE.md and the preamble spec. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Behaviour-preserving. The invocations handler's contiguous phases become named module-level functions with typed results, in the order the turn-path map lists them: _resolve_turn_attachments, _prepare_session_state, _check_turn_quota, _resolve_turn_model, _resolve_effective_tools, _build_turn_tools, _agent_for_app_dispatch (the two MCP App dispatches shared 60 identical lines), _validate_resume_interrupts, _build_citations, and _refuse_turn / _forbidden_turn for the six conversational early exits. The handler drops from ~2,240 to ~1,640 lines and its docstring is now the phase index keyed to the turn_prelude marks. Every module-level seam the tests patch is kept; the one AST pin on the main get_agent call now reads the binding off turn_tools. Four comments that still described the pre-PR-2 preamble (including the disproved ~53ms-per-GSI-query claim) are corrected. Full backend suite: 10950 passed, 3 skipped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Why
A turn is the most complicated process in the app and nobody had one map of it. Three specs each covered one slice (the preamble, the deferred build, the V2 migration), and the handler in
inference_api/chat/routes.pywas ~2,200 lines in one function with two generators closing over ~40 locals. PR #1377 proposes the next TTFT change on that path, and deciding it needs the whole picture.What changed
Commit 1 —
docs/specs/turn-path-ttft.md. Every stage from the SPA's send to the first token, in four tables (before the handler, inside it, inside the stream, inside the agent build): what runs, what is network, what blocks the event loop, whichturn_preludemark times it, and the measured cost where one exists. Then:boto3.Session()re-parses service models every time (~150ms per client, laptop), and the AgentCore Memory SDK,MemoryClient, strategy discovery and Strands'BedrockModelall build fresh sessions — so a cold build parses the same models four to seven times andwarmup.pyparses into a session none of them use.StreamCoordinator._get_initial_message_countruns oneListEventsover the whole history on the head of every turn just forlen(), in theprepared→thinkinggap no clock covers.session_mgrandtoolsinside the build are independent and serial. Agent turns carry two extra META reads and three writes on the critical path.agent_build.tools(2039ms cold) is still undecomposed.toolsand extend the clock to the first token → P2 one shared boto session warmed at start → P3 overlapsession_mgr‖toolsin the constructor and start the KB search ahead of the build → P4 bookkeeping off the path → P5 the V2 track ending in prewarm-on-intent.Pointers added in
CLAUDE.mdand at the top of the preamble spec.Commit 2 — the route refactor. Behaviour-preserving. The handler's contiguous phases become named module-level functions with typed results, in the order the map lists them:
_resolve_turn_attachments,_prepare_session_state,_check_turn_quota,_resolve_turn_model,_resolve_effective_tools,_build_turn_tools,_agent_for_app_dispatch(the two MCP App dispatches shared 60 identical lines),_validate_resume_interrupts,_build_citations, and_refuse_turn/_forbidden_turnfor the six conversational early exits. The handler drops from ~2,240 to ~1,640 lines and its docstring is now the phase index keyed to theturn_preludemarks. Four comments that still described the pre-PR-2 preamble (including the disproved "~53ms per GSI query" claim) are corrected.What a reviewer should know
toolConfig, restored history or any mark. Every module-level seam the tests patch (is_quota_enforcement_enabled,get_quota_checker,ensure_session_metadata_exists,_load_user_settings,get_agent, …) is kept inroutes.pyfor that reason.test_get_agent_call_sites.pyexpects the mainget_agentcall'smemory_bindingto readturn_tools.memory_binding_key. The property it protects (tools and key element derive from the sameproject_memory) still holds inside_build_turn_tools.stream_with_quota_warning,_guarded_stream) are untouched apart from reading attachment fields off the newTurnAttachments, to keep the merge with perf: A/B the first-turn agent build (shared boto3 session built at warm-up), default off #1377 (which edits_guarded_stream) small.Tests
Full backend suite from the worktree with the main venv: 10950 passed, 3 skipped, 0 failed. Route-area subset (inference_api, route-level invocations, MCP App dispatch, base-agent registration): 488 passed.
🤖 Generated with Claude Code