Wire Claude as primary LLM provider (CLAUDE-A/B/C) - #1
Conversation
Adds `ModelTarget::Claude` and a third dispatch arm so requests to /v1/chat/completions can flow through `AnthropicClient` instead of always falling back to Grok. When `ANTHROPIC_API_KEY` is set, the router picks Opus 4.7 for planner tasks (architecture, review) and Sonnet 4.6 for executor tasks (scaffold, extract, tag, answer). The Anthropic client is built once at startup with a `PromptCache` attached so its session state persists across requests, and the proxy now surfaces `cache_creation_input_tokens` / `cache_read_input_tokens` on the `x_ra_metadata` response payload (including round-tripped through the cached-response code path). Also fixes a pre-existing compile blocker where `Config` had `task_executor` and `task_watcher` fields declared but never initialized in `load()` or `Default`. `TaskWatcherConfig` gains `Debug, Clone` so it can satisfy `Config`'s own derive; both fields are `#[serde(skip, default)]` since they are not configured from disk.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughThis PR integrates Anthropic Claude as a primary LLM provider alongside existing Ollama and Grok routing. Claude dispatch is added to the proxy with two-tier task routing (planner for architecture/review, executor for scaffolding/todo), prompt-cache token tracking, and full streaming support. Configuration, routing logic, and documentation are updated throughout. ChangesClaude Integration with Two-Tier Routing
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Proxy as /v1/chat/completions
participant Router as ModelRouter
participant Dispatch as dispatch()
participant Claude as AnthropicClient
Client->>Proxy: POST with model="claude-opus-4-7", task_kind="architecture"
Proxy->>Router: route(TaskKind::Architecture)
Router->>Router: Prefer Claude (anthropic_enabled=true)
Router-->>Proxy: ModelTarget::Claude { model: "claude-opus-4-7", tier: Planner }
Proxy->>Dispatch: dispatch(ModelTarget::Claude, messages)
Dispatch->>Claude: send_message() with prompt_cache
Claude-->>Dispatch: MessageResponse { usage, content: [Text blocks] }
Dispatch->>Dispatch: extract_text() and compute cache tokens
Dispatch-->>Proxy: DispatchOutcome { reply, cache_creation_input_tokens, cache_read_input_tokens }
Proxy->>Proxy: build_oai_response() with RaMetadata::cache_* fields
Proxy-->>Client: OpenAI response { choices, x_ra_metadata { cache_creation_input_tokens, ... } }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Resolves the P0 "Wire Claude as Primary Provider" block in
TODO.md(CLAUDE-A, CLAUDE-B, CLAUDE-C). Until now
AnthropicClientincrates/api/src/providers/anthropic.rswas fully built but neverreached from the proxy hot path — every request still dispatched to
Grok. This PR wires Claude in as the primary backend, with two-tier
routing and prompt-cache token accounting.
What changed
Routing & dispatch
ModelTarget::Claude { model, tier }added alongsideLocal/Remoteinsrc/model_router.rs.ClaudeTier { Planner, Executor }enum plus aTaskKind::tier()helper so each
TaskKindmaps cleanly to one of the two slugs.ModelRouterConfiggainedplanner_model,executor_model, andanthropic_enabled;route()andon_local_failure()honourAnthropic-first ordering when enabled.
src/api/proxy.rs::dispatch()now has a third arm that callsAnthropicClient::send_message()via a smalldispatch_claudehelper, and the streaming handler synthesises a single-delta SSE
burst from the same blocking call (native streaming via
stream_messageleft as a follow-up).route_from_model_fieldpicks the right Claude tier fromclaude-opus*/claude-sonnet*slugs (and theanthropic/prefix used by OpenClaw).
handle_list_modelsadvertisesclaude-opus-4-7andclaude-sonnet-4-6(plusopenai/-prefixed aliases).Prompt caching
RepoAppStatecarries anOption<Arc<AnthropicClient>>builtonce at startup in
src/server.rswith.with_prompt_cache(PromptCache::new("rustcode-proxy"))so thecache session persists across requests.
RaMetadata(and the cachedCachedProxyResponse) now surfacecache_creation_input_tokensandcache_read_input_tokenspopulated from
MessageResponse::usage. Both are#[serde(skip_serializing_if = "Option::is_none")]so non-Clauderesponses keep their existing payload shape.
Config / docs
ModelConfigreadsANTHROPIC_API_KEY,RC_PLANNER_MODEL,RC_EXECUTOR_MODEL. A new.env.examplelists every variable.crates/api/src/providers/mod.rsalias"opus"updated fromclaude-opus-4-6toclaude-opus-4-7.README.mdconfig table + routing diagram updated.TODO.mdmarks CLAUDE-A/B/C done with implementation notes.Ride-along fix
src/config.rspreviously declaredtask_executorandtask_watcherfields but never initialized them inload()orDefault, andTaskWatcherConfigwas missingDebug, Clone—the crate could not compile without this. Both fields are now
#[serde(skip, default)]andTaskWatcherConfigderives themissing traits.
Test plan
cargo check --workspaceclean on a machine withcdn.pyke.ioreachable (the sandbox here blocks theort-sysprebuilt download, so I could only verify theapiandruntimecrates locally)cargo test -p apipassescargo test -p rustcode --lib model_routercovers the threenew tier-routing tests (
claude_planner_for_review_and_architecture,claude_executor_for_scaffold,task_kind_tier_mapping)rc-appwith onlyANTHROPIC_API_KEYset, send a/v1/chat/completionsrequest with
model: "auto"and a planner-shaped prompt,confirm
x_ra_metadata.task_kindisArchitecturalReasonand the echoed model is Opus
cache_read_input_tokens > 0on the second responseANTHROPIC_API_KEY, setXAI_API_KEY,confirm classic Grok behaviour still works
Outstanding for follow-up
curl https://api.anthropic.com/v1/models)per DEPLOY-C before final release.
AnthropicClient::stream_message(currentlya single-burst delta, matching the existing Grok path).
StreamChunkso streamingresponses also surface
cache_*_input_tokens.Generated by Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation