Skip to content

fix(api): improve Anthropic adapter transparency and compatibility#66

Merged
pescn merged 4 commits into
mainfrom
fix/anthropic-adapter-improvements
Jan 31, 2026
Merged

fix(api): improve Anthropic adapter transparency and compatibility#66
pescn merged 4 commits into
mainfrom
fix/anthropic-adapter-improvements

Conversation

@pescn
Copy link
Copy Markdown
Contributor

@pescn pescn commented Jan 31, 2026

Summary

  • Forward client User-Agent header to upstream providers instead of stripping it, making the gateway behave as a transparent proxy
  • Add additionalProperties: true to all Anthropic TypeBox validation schemas (messages.ts) so requests with cache_control, extended tool fields, etc. are no longer rejected at validation
  • Make signature optional on thinking blocks in the validation schema
  • Append /v1/ to Anthropic upstream base URL construction so the final URL becomes {BASE_URL}/v1/messages

Test plan

  • Send an Anthropic API request with cache_control on system prompt blocks and verify it is accepted
  • Send an Anthropic API request with cache_control on tool definitions and verify it is accepted
  • Verify upstream requests carry the original client User-Agent header
  • Verify Anthropic upstream URL resolves to {BASE_URL}/v1/messages

🤖 Generated with Claude Code

Summary by CodeRabbit

发布说明

  • 新特性

    • “思考”类消息及流增量支持可选签名字段,流式响应会发出 signature_delta 增量。
    • 工具对象包含缓存控制信息(cache_control/cacheControl)。
  • 兼容性

    • 放宽上游消息与块的校验以接受额外字段,增强兼容性与代理透明性。
    • 规范化上游基础 URL 构建,减少地址拼接错误。
  • 修复

    • 恢复并转发客户端 User-Agent 头。

✏️ Tip: You can customize this high-level summary in your review settings.

- Forward client User-Agent to upstream providers for gateway transparency
- Add additionalProperties: true to Anthropic TypeBox validation schemas
  so tools, system prompts, and content blocks with cache_control are accepted
- Make thinking block signature optional in validation schema
- Append /v1/ to Anthropic upstream base URL to match actual API endpoint

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

规范化 Anthropic 上游 URL 为 /v1/messages,新增并传播消息块可选字段 signature(含流式的 signature_delta 事件),工具对象携带缓存控制信息,放宽消息 API 的模式以允许额外属性,并恢复转发 user-agent 头。

Changes

Cohort / File(s) Summary
上游 Anthropic 适配器
backend/src/adapters/upstream/anthropic.ts
合并/调整导入;规范化 baseUrl(去除尾部 //v1)并统一构建 /v1/messages;新增 AnthropicContentBlock.signature?: string;流式响应新增 signature_delta 事件。
请求端 Anthropic 转换
backend/src/adapters/request/anthropic.ts
AnthropicContentBlock.signature 传播为内部 ThinkingContentBlock.signature;工具转换映射 tool.cache_control 到内部 cacheControl
响应/流处理
backend/src/adapters/response/anthropic.ts, backend/src/adapters/types.ts
内部流类型扩展:delta.type 包含 signature_delta,delta 中携带可选 signature;序列化流增添对 signature_delta 的处理。
类型定义
backend/src/adapters/types.ts
ThinkingContentBlock.signature?: stringInternalToolDefinition.cacheControl?: { type: "ephemeral" }InternalStreamChunk.delta.signature?: stringdelta.type 扩展为含 signature_delta
API 消息校验模式
backend/src/api/v1/messages.ts
放宽 Anthropic 相关 block、tool、metadata 模式(additionalProperties: true);thinking.signature 设为可选;调整类型导入以支持更宽松的负载。
API 辅助工具 / 头转发
backend/src/utils/api-helpers.ts
调整导入顺序;从 EXCLUDED_HEADERS 中移除 user-agent,以便将该头转发到上游。

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant API as Messages API
    participant Adapter as Anthropic Adapter
    participant Upstream as Anthropic /v1/messages
    rect rgba(135,206,250,0.5)
    Client->>API: 发起消息请求(含 blocks)
    API->>Adapter: 转换请求(包含 thinking.signature & tools.cache_control)
    Adapter->>Upstream: POST /v1/messages (带 user-agent)
    Upstream-->>Adapter: 流式响应(text_delta / thinking_delta / signature_delta)
    Adapter->>API: 转发内部流块(包含 signature_delta)
    API->>Client: 将合并的流式事件推送到客户端
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🐇 我在代码巷里蹦跳,
签名悄悄藏入思绪草,
流中片段会轻声唱,
工具背包带着缓存梦,
代理头随风自由跑。

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确总结了主要变更:改进Anthropic适配器的透明度和兼容性,涵盖用户代理头转发、additionalProperties支持、签名字段可选化和URL构造等核心变更。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/anthropic-adapter-improvements

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.

❤️ Share

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

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @pescn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Anthropic API adapter by improving its transparency and compatibility. It ensures that client-specific headers are propagated to upstream services and relaxes validation rules for Anthropic request schemas to accommodate additional properties and optional fields. Furthermore, it corrects the base URL used for Anthropic API calls, ensuring proper routing to the /v1/ endpoint.

Highlights

  • API Transparency: The client's User-Agent header is now forwarded to upstream providers, ensuring the gateway acts as a more transparent proxy.
  • Anthropic Schema Compatibility: Anthropic TypeBox validation schemas have been updated to include additionalProperties: true, allowing requests with fields like cache_control and extended tool definitions to be accepted without validation errors.
  • Anthropic Thinking Block Schema: The signature field in Anthropic thinking block validation schemas has been made optional, improving flexibility.
  • Anthropic API URL Correction: The Anthropic upstream base URL construction now correctly appends /v1/, ensuring the final API endpoint resolves to {BASE_URL}/v1/messages.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several improvements for the Anthropic adapter, enhancing compatibility and transparency. It correctly enables forwarding of the User-Agent header, updates the Anthropic API endpoint to include /v1/, and makes the validation schemas more flexible by allowing additional properties and making the signature field on thinking blocks optional.

My main feedback is that while allowing additional properties in the validation schema is a good first step, these properties are currently dropped during the conversion to the internal data model. This means fields like cache_control on tool definitions won't be forwarded to the upstream provider. I've left a detailed comment with suggestions on how to achieve full end-to-end passthrough for these properties.

Comment thread backend/src/api/v1/messages.ts Outdated
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/adapters/upstream/anthropic.ts (1)

461-466: ⚠️ Potential issue | 🟠 Major

修复 Anthropic baseUrl 拼接的 /v1 重复问题。

当前代码硬编码拼接 /v1/messages。若配置的 provider.baseUrl 已包含 /v1(如 https://api.anthropic.com/v1),会产生 baseUrl/v1/v1/messages 的重复路径,导致请求失败。应在拼接时检查是否已存在 /v1 前缀。

修复方案
-    const url = `${baseUrl}/v1/messages`;
+    const url = baseUrl.endsWith("/v1")
+      ? `${baseUrl}/messages`
+      : `${baseUrl}/v1/messages`;

…ter pipeline

- Add cacheControl to InternalToolDefinition so tool-level cache_control
  is forwarded to upstream (was silently dropped after validation)
- Add signature to ThinkingContentBlock so thinking blocks can be
  replayed in multi-turn conversations
- Normalize Anthropic baseUrl to handle both with and without /v1 suffix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/src/adapters/upstream/anthropic.ts (2)

309-313: ⚠️ Potential issue | 🟠 Major

响应解析中缺少 signature 字段的提取。

convertContentBlock 函数在解析上游 Anthropic 响应时,没有将 thinking 块中的 signature 字段传递到内部格式。这将导致从 Anthropic 返回的 signature 丢失,破坏多轮对话中思维块重放功能。

🐛 建议修复
     case "thinking":
       return {
         type: "thinking",
         thinking: block.thinking || "",
+        signature: block.signature,
       } as ThinkingContentBlock;

544-548: ⚠️ Potential issue | 🔴 Critical

流式响应中 thinking 块的 signature 字段未被处理。

在流式响应中,Anthropic API 通过 signature_delta 事件(在 content_block_stop 之前)传递 signature,但代码中存在两个问题:

  1. AnthropicStreamEvent.delta 接口(第 95-104 行)缺少 signature 字段定义,当前仅包含 textthinkingpartial_json 等。

  2. 流式响应解析器中完全没有 signature_delta 事件的处理逻辑,导致 thinking 块的 signature 在流式场景下丢失。

这会破坏多轮对话功能,因为重新提交 thinking 块时需要原始的 signature 字段。需要在 AnthropicStreamEvent.delta 中添加 signature 字段,并在 content_block_delta 事件处理中处理 signature_delta 类型的事件。

🧹 Nitpick comments (1)
backend/src/adapters/upstream/anthropic.ts (1)

467-474: URL 规范化逻辑合理,但建议添加注释说明预期行为。

当前实现能正确处理常见的 baseUrl 格式变体。不过,对于自定义代理路径(如 https://proxy.example.com/custom-path/v1),末尾的 /v1 也会被移除。如果这是预期行为,建议在注释中明确说明。

💡 建议增强注释
     // Build URL — strip trailing slash and /v1 suffix to normalize,
     // then always append /v1/messages. This handles both
     // "https://api.anthropic.com" and "https://api.anthropic.com/v1".
+    // Note: Any path ending with /v1 will have it stripped (e.g., /custom/v1 → /custom).
     let baseUrl = provider.baseUrl.replace(/\/+$/, "");

@pescn
Copy link
Copy Markdown
Contributor Author

pescn commented Jan 31, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several valuable improvements for the Anthropic adapter, enhancing its compatibility and transparency. The changes include forwarding the User-Agent header, making TypeBox validation schemas more flexible by allowing additional properties, making the signature field on thinking blocks optional, and improving the upstream URL construction logic. Support for cache_control on tools is also added.

The implementation is solid and all changes are consistent across the different layers of the application. The code is clean and the logic is robust. I have one suggestion to improve maintainability by reducing code repetition in the TypeBox schema definitions, but overall this is a great set of improvements.

Comment thread backend/src/api/v1/messages.ts Outdated
Comment on lines +55 to +106
const tAnthropicTextBlock = t.Object(
{
type: t.Literal("text"),
text: t.String(),
},
{ additionalProperties: true },
);

const tAnthropicImageBlock = t.Object(
{
type: t.Literal("image"),
source: t.Object(
{
type: t.String(),
media_type: t.Optional(t.String()),
data: t.Optional(t.String()),
url: t.Optional(t.String()),
},
{ additionalProperties: true },
),
},
{ additionalProperties: true },
);

const tAnthropicToolUseBlock = t.Object(
{
type: t.Literal("tool_use"),
id: t.String(),
name: t.String(),
input: t.Record(t.String(), t.Unknown()),
},
{ additionalProperties: true },
);

const tAnthropicToolResultBlock = t.Object(
{
type: t.Literal("tool_result"),
tool_use_id: t.String(),
content: t.Optional(t.Union([t.String(), t.Array(t.Unknown())])),
is_error: t.Optional(t.Boolean()),
},
{ additionalProperties: true },
);

const tAnthropicThinkingBlock = t.Object(
{
type: t.Literal("thinking"),
thinking: t.String(),
signature: t.Optional(t.String()),
},
{ additionalProperties: true },
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

While adding { additionalProperties: true } is the correct fix, it introduces a lot of repetition across the schema definitions. To improve maintainability and reduce boilerplate, you could create a helper function that wraps t.Object and applies this option by default.

For example:

import { t, type TProperties } from "elysia";

const TObjectLoose = <T extends TProperties>(schema: T) => t.Object(schema, { additionalProperties: true });

// Then you can define your schemas more concisely:
const tAnthropicTextBlock = TObjectLoose({
  type: t.Literal("text"),
  text: t.String(),
});

This would make the code in this file (and potentially others) cleaner and less error-prone if you need to apply other default options in the future.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed — introduced a tLooseObject helper in 80181ec to eliminate all 13 repeated { additionalProperties: true } occurrences in this file.

pescn and others added 2 commits January 31, 2026 13:38
…line

- Extract signature from thinking blocks in non-streaming response parsing
- Add signature_delta handling in streaming response parser and serializer
- Add signature field to AnthropicStreamEvent delta and InternalStreamChunk
- Add clarifying comment on baseUrl /v1 normalization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract repeated t.Object(..., { additionalProperties: true }) pattern
into a reusable tLooseObject helper in the Anthropic Messages endpoint.
Reduces 13 occurrences to a single definition.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@pescn
Copy link
Copy Markdown
Contributor Author

pescn commented Jan 31, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several valuable improvements for the Anthropic adapter, enhancing its transparency and compatibility. Key changes include forwarding the User-Agent header, making the validation schemas for Anthropic requests more flexible by allowing additional properties, making the signature field on thinking blocks optional, and refining the upstream URL construction. These changes are well-implemented and align with the goal of making the gateway a more transparent and robust proxy for Anthropic's API. I've identified one area for improvement in the URL construction logic to make it more robust against edge cases, for which I've provided a suggestion.

Comment thread backend/src/adapters/upstream/anthropic.ts
@pescn pescn merged commit cc083fa into main Jan 31, 2026
2 checks passed
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.

1 participant