Skip to content

feat(provider): add anthropic chat format for new provider#20

Merged
bzp2010 merged 2 commits intomainfrom
bzp/feat-new-provider-anthropic
Apr 6, 2026
Merged

feat(provider): add anthropic chat format for new provider#20
bzp2010 merged 2 commits intomainfrom
bzp/feat-new-provider-anthropic

Conversation

@bzp2010
Copy link
Copy Markdown
Collaborator

@bzp2010 bzp2010 commented Apr 6, 2026

As the 6th part of the major refactoring of the provider, add the Anthropic provider.

Summary by CodeRabbit

  • New Features

    • Added Anthropic as a gateway provider with native Anthropic message support and OpenAI↔Anthropic bridging for requests, responses, and streaming.
  • Bug Fixes / Improvements

    • Stream state now preserves response metadata (id, model, created) to improve streaming reliability.
  • Documentation

    • Updated internal docs to describe stream state and the Anthropic provider design.

Copilot AI review requested due to automatic review settings April 6, 2026 18:26
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0e3444a-52b4-4504-bf17-2f07a20f1e6a

📥 Commits

Reviewing files that changed from the base of the PR and between 462e7c6 and 0ba57ae.

📒 Files selected for processing (2)
  • Cargo.toml
  • src/gateway/providers/anthropic/transform.rs
✅ Files skipped from review due to trivial changes (1)
  • Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/gateway/providers/anthropic/transform.rs

📝 Walkthrough

Walkthrough

Adds an Anthropic gateway provider with OpenAI↔Anthropic request/response and SSE bridging, extends chat stream state to include response metadata, updates provider registry to register AnthropicDef, and documents hub stream state carrying response metadata alongside tool-call state.

Changes

Cohort / File(s) Summary
Documentation
docs/internals/llm-types.md
Clarifies hub stream state includes streamed response metadata (id, model, created) in addition to tool-call state; documents AnthropicDef as a hand-written non-OpenAI-compatible provider using a custom ChatTransform plus native Anthropic support.
Anthropic provider
src/gateway/providers/anthropic/mod.rs, src/gateway/providers/anthropic/transform.rs
New AnthropicDef provider: ProviderMeta, ChatTransform, and native Anthropic messages support. Implements auth headers, request/response transformations, SSE parsing to OpenAI chat chunks, native SSE parsing, and includes unit tests covering headers, serialization, transforms, and streaming behavior.
Provider registry
src/gateway/providers/mod.rs
Adds pub mod anthropic; and pub use anthropic::AnthropicDef; and registers AnthropicDef in default_provider_registry(); test updated to assert presence.
Chat stream state
src/gateway/traits/chat_format.rs
Extended ChatStreamState with optional response_id, response_model, and response_created fields to carry response-level metadata through streaming conversions.
Manifest
Cargo.toml
Sets rust-version = "1.85" in package manifest.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Gateway
    participant AnthropicAPI as Anthropic API

    Client->>Gateway: OpenAI ChatCompletionRequest
    Note over Gateway: Transform request\n- Hoist system/developer into Anthropic system prompt\n- Convert messages & tools to Anthropic format\n- Map stop sequences / tool-choice
    Gateway->>AnthropicAPI: AnthropicMessagesRequest
    AnthropicAPI-->>Gateway: AnthropicMessagesResponse (id, model, created) / SSE events
    Note over Gateway: Parse response/SSE\n- Capture response metadata into ChatStreamState\n- Map content blocks → assistant messages\n- Map tool_use → function/tool calls\n- Aggregate streaming deltas into ChatCompletionChunk(s)
    Gateway->>Client: ChatCompletionResponse / streaming chunks
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
E2e Test Quality Review ⚠️ Warning PR lacks end-to-end tests for AnthropicDef provider integration with gateway service layer, HTTP client, and real streaming scenarios. Add integration tests verifying: provider registry integration, full request flow through HTTP client, streaming state management, and error propagation across stack layers.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding Anthropic provider support with chat format transformation. It directly reflects the PR's primary objective and the most significant code additions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bzp/feat-new-provider-anthropic

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/gateway/providers/anthropic/mod.rs (2)

201-206: Assertion can be simplified.

The assertion response.choices[0].message.content.as_ref().map(|_| true) just checks for Some. A cleaner approach would be to use is_some().

✨ Suggested simplification
-        assert_eq!(
-            response.choices[0].message.content.as_ref().map(|_| true),
-            Some(true)
-        );
+        assert!(response.choices[0].message.content.is_some());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/anthropic/mod.rs` around lines 201 - 206, The test
assertion that checks presence of content uses a convoluted map-to-true pattern;
replace the expression response.choices[0].message.content.as_ref().map(|_|
true) == Some(true) with a direct is_some() check (e.g.,
assert!(response.choices[0].message.content.as_ref().is_some())) to simplify and
clarify the intent for the Content field on response.choices[0].message.

47-50: Consider documenting or making the Anthropic API version configurable.

The anthropic-version header is hardcoded to 2023-06-01. While this is a stable version, Anthropic periodically releases new API versions with additional features. Consider adding a comment explaining this choice or making it configurable if version-specific features become necessary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/anthropic/mod.rs` around lines 47 - 50, The Anthropic
API version header is hardcoded in the headers.insert call
(HeaderName::from_static("anthropic-version"),
HeaderValue::from_static("2023-06-01")); update this to be configurable or at
least documented: add a short comment explaining why this version is pinned and
potential impacts, and refactor the HeaderValue to use a configurable constant
or env var (e.g., ANTHROPIC_API_VERSION) so code paths that construct headers
can reference that constant instead of the literal string; ensure the unique
symbols headers.insert, HeaderName::from_static("anthropic-version"), and
HeaderValue::from_static("2023-06-01") are updated to use the new constant or
accompanied by the explanatory comment.
src/gateway/providers/anthropic/transform.rs (2)

172-194: Tool call accumulator created via or_default() may lack metadata if ContentBlockStart is missed.

If a ContentBlockDelta with InputJsonDelta arrives before its corresponding ContentBlockStart (e.g., due to network issues or malformed streams), the accumulator will be created without id, kind, or name. The build_stream_chunk will still succeed, but the emitted ChunkToolCall will have None for these fields.

This is likely acceptable since a malformed stream from Anthropic would be an upstream issue, but consider adding a debug log or metric for this edge case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/anthropic/transform.rs` around lines 172 - 194,
ContentDelta::InputJsonDelta handling currently creates an accumulator with
state.tool_call_accumulators.entry((0, index)).or_default() which can produce a
tool-call accumulator missing metadata (id/kind/name) if the corresponding
ContentBlockStart was missed; update this branch to detect when the entry is
newly created or when accumulator.id/kind/name are None and emit a debug log or
increment a metric before pushing partial_json and calling build_stream_chunk
(referencing ContentDelta::InputJsonDelta, state.tool_call_accumulators,
build_stream_chunk, ChunkToolCall, ChunkFunctionCall); ensure the log/metric
includes the index and any context to diagnose malformed streams.

119-160: Hardcoded choice index 0 may limit future extensibility.

The tool call accumulator key uses a hardcoded choice index of 0 (line 127: (0, index)). While Anthropic's current API doesn't support multiple choices (n > 1), this assumption is baked into multiple places. If Anthropic adds multi-choice streaming in the future, this would need refactoring.

This is acceptable for now given Anthropic's current API constraints, but worth noting in a comment.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/gateway/providers/anthropic/transform.rs`:
- Around line 395-408: The crate uses if-let chain syntax (see
normalize_anthropic_content and its if let [...] && cache_control.is_none()
usage) which requires Rust 1.76; update Cargo.toml's [package] section to
declare the MSRV by adding rust-version = "1.76" so the minimum supported Rust
version is explicitly documented.

---

Nitpick comments:
In `@src/gateway/providers/anthropic/mod.rs`:
- Around line 201-206: The test assertion that checks presence of content uses a
convoluted map-to-true pattern; replace the expression
response.choices[0].message.content.as_ref().map(|_| true) == Some(true) with a
direct is_some() check (e.g.,
assert!(response.choices[0].message.content.as_ref().is_some())) to simplify and
clarify the intent for the Content field on response.choices[0].message.
- Around line 47-50: The Anthropic API version header is hardcoded in the
headers.insert call (HeaderName::from_static("anthropic-version"),
HeaderValue::from_static("2023-06-01")); update this to be configurable or at
least documented: add a short comment explaining why this version is pinned and
potential impacts, and refactor the HeaderValue to use a configurable constant
or env var (e.g., ANTHROPIC_API_VERSION) so code paths that construct headers
can reference that constant instead of the literal string; ensure the unique
symbols headers.insert, HeaderName::from_static("anthropic-version"), and
HeaderValue::from_static("2023-06-01") are updated to use the new constant or
accompanied by the explanatory comment.

In `@src/gateway/providers/anthropic/transform.rs`:
- Around line 172-194: ContentDelta::InputJsonDelta handling currently creates
an accumulator with state.tool_call_accumulators.entry((0, index)).or_default()
which can produce a tool-call accumulator missing metadata (id/kind/name) if the
corresponding ContentBlockStart was missed; update this branch to detect when
the entry is newly created or when accumulator.id/kind/name are None and emit a
debug log or increment a metric before pushing partial_json and calling
build_stream_chunk (referencing ContentDelta::InputJsonDelta,
state.tool_call_accumulators, build_stream_chunk, ChunkToolCall,
ChunkFunctionCall); ensure the log/metric includes the index and any context to
diagnose malformed streams.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19913990-0e33-4442-90ca-a2db38b9fb6d

📥 Commits

Reviewing files that changed from the base of the PR and between a95ba1a and 462e7c6.

📒 Files selected for processing (5)
  • docs/internals/llm-types.md
  • src/gateway/providers/anthropic/mod.rs
  • src/gateway/providers/anthropic/transform.rs
  • src/gateway/providers/mod.rs
  • src/gateway/traits/chat_format.rs

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a first-class Anthropic provider implementation to the gateway’s new provider stack, including request/response bridging between the hub (OpenAI Chat Completions) types and Anthropic Messages, plus streaming adaptation.

Changes:

  • Extend ChatStreamState to carry streamed response metadata (id, model, created) needed by provider SSE adapters.
  • Register a new AnthropicDef provider and expose it via the default provider registry.
  • Implement OpenAI↔Anthropic transform logic (including SSE stream event conversion) and document the new provider layering.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/gateway/traits/chat_format.rs Adds response metadata fields to shared streaming state.
src/gateway/providers/mod.rs Registers and exports the new Anthropic provider definition.
src/gateway/providers/anthropic/mod.rs Implements ProviderMeta/ChatTransform and native Anthropic Messages support wiring.
src/gateway/providers/anthropic/transform.rs Implements request/response and SSE streaming conversions between OpenAI hub types and Anthropic Messages.
docs/internals/llm-types.md Documents stream-state metadata and Anthropic provider positioning in the new stack.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants