29 KiB
Anthropic 协议适配清单
依据 conversion_design.md 附录 D 模板编撰,覆盖 Anthropic API 的全部对接细节。
目录
1. 协议基本信息
| 项目 | 说明 |
|---|---|
| 协议名称 | "anthropic" |
| 协议版本 | 2023-06-01(通过 anthropic-version Header 传递) |
| Base URL | https://api.anthropic.com |
| 认证方式 | x-api-key: <api_key> |
2. 接口识别
2.1 URL 路径模式
| URL 路径 | InterfaceType |
|---|---|
/v1/messages |
CHAT |
/v1/models |
MODELS |
/v1/models/{model} |
MODEL_INFO |
/v1/batches |
透传 |
/v1/messages/count_tokens |
透传 |
/v1/* |
透传 |
2.2 detectInterfaceType
Anthropic.detectInterfaceType(nativePath):
if nativePath == "/v1/messages": return CHAT
if nativePath == "/v1/models": return MODELS
if nativePath matches "^/v1/models/[^/]+$": return MODEL_INFO
return PASSTHROUGH
说明:detectInterfaceType 由 Anthropic Adapter 实现,根据 Anthropic 协议的 URL 路径约定识别接口类型。
2.3 接口能力矩阵
Anthropic.supportsInterface(type):
CHAT: return true
MODELS: return true
MODEL_INFO: return true
EMBEDDINGS: return false // Anthropic 无此接口
RERANK: return false // Anthropic 无此接口
default: return false
2.4 URL 映射表
Anthropic.buildUrl(nativePath, interfaceType):
switch interfaceType:
case CHAT: return "/v1/messages"
case MODELS: return "/v1/models"
case MODEL_INFO: return "/v1/models/{modelId}"
default: return nativePath
不支持 EMBEDDINGS 和 RERANK(supportsInterface 返回 false),引擎会自动走透传逻辑。
3. 请求头构建
3.1 buildHeaders
Anthropic.buildHeaders(provider):
result = {}
result["x-api-key"] = provider.api_key
result["anthropic-version"] = provider.adapter_config["anthropic_version"] ?? "2023-06-01"
if provider.adapter_config["anthropic_beta"]:
result["anthropic-beta"] = provider.adapter_config["anthropic_beta"].join(",")
result["Content-Type"] = "application/json"
return result
3.2 adapter_config 契约
| Key | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
anthropic_version |
String | 否 | "2023-06-01" |
API 版本,映射为 anthropic-version Header |
anthropic_beta |
Array<String> | 否 | [] |
Beta 功能标识列表,逗号拼接为 anthropic-beta Header |
4. 核心层 — Chat 请求编解码
4.1 Decoder(Anthropic → Canonical)
系统消息
decodeSystem(system):
if system is None: return None
if system is String: return system
return system.map(s => SystemBlock {text: s.text})
Anthropic 使用顶层 system 字段(String 或 SystemBlock 数组),直接提取为 canonical.system。
消息角色映射
| Anthropic role | Canonical role | 说明 |
|---|---|---|
user |
user |
直接映射;可能包含 tool_result |
assistant |
assistant |
直接映射 |
关键差异:Anthropic 没有 system 和 tool 角色。system 通过顶层字段传递;tool_result 嵌入 user 消息的 content 数组中。
内容块解码
decodeContentBlocks(content):
if content is String: return [{type: "text", text: content}]
return content.map(block => {
switch block.type:
"text" → TextBlock{text: block.text}
"tool_use" → ToolUseBlock{id: block.id, name: block.name, input: block.input}
"tool_result" → ToolResultBlock{tool_use_id: block.tool_use_id, ...}
"thinking" → ThinkingBlock{thinking: block.thinking}
"redacted_thinking" → 丢弃 // 仅 Anthropic 使用,不在中间层保留
})
tool_result 角色转换:
decodeMessage(msg):
switch msg.role:
case "user":
blocks = decodeContentBlocks(msg.content)
toolResults = blocks.filter(b => b.type == "tool_result")
others = blocks.filter(b => b.type != "tool_result")
if toolResults.length > 0:
return [
...(others.length > 0 ? [{role: "user", content: others}] : []),
{role: "tool", content: toolResults}]
return [{role: "user", content: blocks}]
case "assistant":
return [{role: "assistant", content: decodeContentBlocks(msg.content)}]
Anthropic user 消息中的 tool_result 块被拆分为独立的 Canonical tool 角色消息。
工具定义
| Anthropic | Canonical | 说明 |
|---|---|---|
tools[].name |
tools[].name |
直接映射 |
tools[].description |
tools[].description |
直接映射 |
tools[].input_schema |
tools[].input_schema |
字段名相同 |
tools[].type |
— | Anthropic 无 function 包装层 |
工具选择
| Anthropic tool_choice | Canonical ToolChoice |
|---|---|
{type: "auto"} |
{type: "auto"} |
{type: "none"} |
{type: "none"} |
{type: "any"} |
{type: "any"} |
{type: "tool", name} |
{type: "tool", name} |
参数映射
| Anthropic | Canonical | 说明 |
|---|---|---|
max_tokens |
parameters.max_tokens |
直接映射;Anthropic 必填 |
temperature |
parameters.temperature |
直接映射 |
top_p |
parameters.top_p |
直接映射 |
top_k |
parameters.top_k |
直接映射 |
stop_sequences (Array) |
parameters.stop_sequences (Array) |
直接映射 |
stream |
stream |
直接映射 |
新增公共字段
decodeExtras(raw):
user_id = raw.metadata?.user_id
output_format = decodeOutputFormat(raw.output_config)
parallel_tool_use = raw.disable_parallel_tool_use == true ? false : null
thinking = raw.thinking ? ThinkingConfig {
type: raw.thinking.type, // "enabled" | "disabled" | "adaptive"
budget_tokens: raw.thinking.budget_tokens,
effort: raw.output_config?.effort } : null
ThinkingConfig 三种类型解码:
| Anthropic thinking.type | Canonical thinking.type | 说明 |
|---|---|---|
"enabled" |
"enabled" |
有 budget_tokens,直接映射 |
"disabled" |
"disabled" |
直接映射 |
"adaptive" |
"adaptive" |
Anthropic 自动决定是否启用思考,映射为 "adaptive"(新增 Canonical 值) |
注意:
thinking.display("summarized"/"omitted")为 Anthropic 特有字段,控制响应中思考内容的显示方式,不晋升为公共字段。
output_config 解码:
decodeOutputFormat(output_config):
if output_config?.format?.type == "json_schema":
return { type: "json_schema", json_schema: { name: "output", schema: output_config.format.schema, strict: true } }
return null
| Anthropic | Canonical | 提取规则 |
|---|---|---|
metadata.user_id |
user_id |
从嵌套对象提取 |
output_config.format |
output_format |
仅支持 json_schema 类型;映射为 Canonical OutputFormat |
output_config.effort |
thinking.effort |
"low" / "medium" / "high" / "xhigh" / "max" 直接映射 |
disable_parallel_tool_use |
parallel_tool_use |
语义反转:true → false |
thinking.type |
thinking.type |
直接映射 |
thinking.budget_tokens |
thinking.budget_tokens |
直接映射 |
协议特有字段
| 字段 | 处理方式 |
|---|---|
cache_control |
忽略(仅 Anthropic 使用,不晋升为公共字段) |
redacted_thinking |
解码时丢弃,不在中间层保留 |
metadata (除 user_id) |
忽略 |
thinking.display |
忽略(控制响应显示方式,不影响请求语义) |
container |
忽略(容器标识,协议特有) |
inference_geo |
忽略(地理区域控制,协议特有) |
service_tier |
忽略(服务层级选择,协议特有) |
协议约束
max_tokens为必填字段- messages 必须以
user角色开始 user和assistant角色必须严格交替(除连续 tool_result 场景)- tool_result 必须紧跟在包含对应 tool_use 的 assistant 消息之后
4.2 Encoder(Canonical → Anthropic)
模型名称
使用 provider.model_name 覆盖 canonical.model。
系统消息注入
encodeSystem(system):
if system is String: return system
return system.map(s => ({text: s.text}))
将 canonical.system 编码为 Anthropic 顶层 system 字段。
消息编码
关键差异:Canonical 的 tool 角色消息需要合并到 Anthropic 的 user 消息中:
encodeMessages(canonical):
result = []
for msg in canonical.messages:
switch msg.role:
case "user":
result.append({role: "user", content: encodeContentBlocks(msg.content)})
case "assistant":
result.append({role: "assistant", content: encodeContentBlocks(msg.content)})
case "tool":
// tool 角色转为 Anthropic 的 user 消息内 tool_result 块
toolResults = msg.content.filter(b => b.type == "tool_result")
if result.length > 0 && result.last.role == "user":
result.last.content = result.last.content + toolResults
else:
result.append({role: "user", content: toolResults})
角色约束处理
Anthropic 要求 user/assistant 严格交替。编码时需要:
- 将 Canonical
tool角色合并到相邻user消息中 - 确保首条消息为
user角色(若无,自动注入空 user 消息) - 合并连续同角色消息
工具编码
encodeTools(canonical):
if canonical.tools:
result.tools = canonical.tools.map(t => ({
name: t.name, description: t.description, input_schema: t.input_schema}))
encodeToolChoice(choice):
switch choice.type:
"auto" → {type: "auto"}
"none" → {type: "none"}
"any" → {type: "any"}
"tool" → {type: "tool", name: choice.name}
公共字段编码
encodeRequest(canonical, provider):
result = {
model: provider.model_name,
messages: encodeMessages(canonical),
max_tokens: canonical.parameters.max_tokens,
temperature: canonical.parameters.temperature,
top_p: canonical.parameters.top_p,
top_k: canonical.parameters.top_k,
stream: canonical.stream
}
if canonical.system:
result.system = encodeSystem(canonical.system)
if canonical.parameters.stop_sequences:
result.stop_sequences = canonical.parameters.stop_sequences
if canonical.user_id:
result.metadata = {user_id: canonical.user_id}
if canonical.output_format or canonical.thinking?.effort:
result.output_config = {}
if canonical.output_format:
result.output_config.format = encodeOutputFormat(canonical.output_format)
if canonical.thinking?.effort:
result.output_config.effort = canonical.thinking.effort
if canonical.parallel_tool_use == false:
result.disable_parallel_tool_use = true
if canonical.tools:
result.tools = canonical.tools.map(t => ({
name: t.name, description: t.description, input_schema: t.input_schema}))
if canonical.tool_choice:
result.tool_choice = encodeToolChoice(canonical.tool_choice)
if canonical.thinking:
result.thinking = encodeThinkingConfig(canonical.thinking)
return result
encodeThinkingConfig(canonical):
switch canonical.type:
"enabled":
cfg = {type: "enabled", budget_tokens: canonical.budget_tokens}
return cfg
"disabled":
return {type: "disabled"}
"adaptive":
return {type: "adaptive"}
return {type: "disabled"}
encodeOutputFormat(output_format):
switch output_format.type:
"json_schema":
return {type: "json_schema", schema: output_format.json_schema.schema}
"json_object":
return {type: "json_schema", schema: {type: "object"}}
降级处理
对照架构文档 §8.4 三级降级策略,确认每个不支持字段的处理:
| Canonical 字段 | Anthropic 不支持时 | 降级策略 |
|---|---|---|
thinking.effort |
Anthropic 通过 output_config.effort 传递 |
自动映射为 output_config.effort |
stop_reason: "content_filter" |
Anthropic 无此值 | 自动映射为 "end_turn" |
output_format: "text" |
Anthropic 无 text 输出格式 | 丢弃,不设置 output_config |
output_format: "json_object" |
Anthropic 用 json_schema 替代 | 替代方案:生成空 schema 的 json_schema |
5. 核心层 — Chat 响应编解码
逐字段对照 §4.7 CanonicalResponse 确认映射关系。
5.1 响应结构
Anthropic 响应顶层结构:
{
id: String,
type: "message",
role: "assistant",
model: String,
content: [ContentBlock...],
stop_reason: String,
stop_sequence: String | null,
stop_details: Object | null,
container: Object | null,
usage: { input_tokens, output_tokens, cache_read_input_tokens?, cache_creation_input_tokens?,
cache_creation?, inference_geo?, server_tool_use?, service_tier? }
}
新增字段(对比 §4.7 CanonicalResponse):
| Anthropic 字段 | 说明 |
|---|---|
stop_details |
结构化拒绝信息:{type: "refusal", category, explanation},仅 stop_reason == "refusal" 时存在 |
container |
容器信息:{id, expires_at},仅使用 code execution 工具时存在 |
5.2 Decoder(Anthropic → Canonical)
decodeResponse(anthropicResp):
blocks = []
for block in anthropicResp.content:
switch block.type:
"text" → blocks.append({type: "text", text: block.text})
"tool_use" → blocks.append({type: "tool_use", id: block.id, name: block.name, input: block.input})
"thinking" → blocks.append({type: "thinking", thinking: block.thinking})
"redacted_thinking" → 丢弃 // 仅 Anthropic 使用,不在中间层保留
return CanonicalResponse {id, model, content: blocks, stop_reason: mapStopReason(anthropicResp.stop_reason),
usage: CanonicalUsage {input_tokens, output_tokens,
cache_read_tokens: anthropicResp.usage.cache_read_input_tokens,
cache_creation_tokens: anthropicResp.usage.cache_creation_input_tokens}}
内容块解码:
text→ TextBlock(直接映射;忽略citations字段)tool_use→ ToolUseBlock(直接映射;忽略caller字段)thinking→ ThinkingBlock(直接映射;忽略signature字段)redacted_thinking→ 丢弃(协议特有,不晋升为公共字段)server_tool_use/web_search_tool_result/code_execution_tool_result等 → 丢弃(服务端工具块,协议特有)
停止原因映射:
| Anthropic stop_reason | Canonical stop_reason | 说明 |
|---|---|---|
"end_turn" |
"end_turn" |
直接映射 |
"max_tokens" |
"max_tokens" |
直接映射 |
"tool_use" |
"tool_use" |
直接映射 |
"stop_sequence" |
"stop_sequence" |
直接映射 |
"pause_turn" |
"pause_turn" |
长轮次暂停,映射为 Canonical 新增值 |
"refusal" |
"refusal" |
安全拒绝,直接映射 |
Token 用量映射:
| Anthropic usage | Canonical Usage | 说明 |
|---|---|---|
input_tokens |
input_tokens |
直接映射 |
output_tokens |
output_tokens |
直接映射 |
cache_read_input_tokens |
cache_read_tokens |
字段名映射 |
cache_creation_input_tokens |
cache_creation_tokens |
字段名映射 |
cache_creation |
— | 协议特有(按 TTL 细分),不晋升 |
inference_geo |
— | 协议特有,不晋升 |
server_tool_use |
— | 协议特有,不晋升 |
service_tier |
— | 协议特有,不晋升 |
| — | reasoning_tokens |
Anthropic 不返回此字段,始终为 null |
协议特有内容:
| 字段 | 处理方式 |
|---|---|
redacted_thinking |
解码时丢弃 |
stop_sequence |
解码时忽略(Canonical 用 stop_reason 覆盖) |
stop_details |
解码时忽略(协议特有,不晋升) |
container |
解码时忽略(协议特有,不晋升) |
text.citations |
解码时忽略(协议特有,不晋升) |
tool_use.caller |
解码时忽略(协议特有,不晋升) |
thinking.signature |
解码时忽略(协议特有,不晋升;同协议透传时自然保留) |
5.3 Encoder(Canonical → Anthropic)
encodeResponse(canonical):
blocks = canonical.content.map(block => {
switch block.type:
"text" → {type: "text", text: block.text}
"tool_use" → {type: "tool_use", id: block.id, name: block.name, input: block.input}
"thinking" → {type: "thinking", thinking: block.thinking}})
return {id: canonical.id, type: "message", role: "assistant", model: canonical.model,
content: blocks,
stop_reason: mapCanonicalStopReason(canonical.stop_reason),
stop_sequence: None,
usage: {input_tokens: canonical.usage.input_tokens, output_tokens: canonical.usage.output_tokens,
cache_read_input_tokens: canonical.usage.cache_read_tokens,
cache_creation_input_tokens: canonical.usage.cache_creation_tokens}}
内容块编码:
- TextBlock →
{type: "text", text}(直接映射) - ToolUseBlock →
{type: "tool_use", id, name, input}(直接映射) - ThinkingBlock →
{type: "thinking", thinking}(直接映射)
停止原因映射:
| Canonical stop_reason | Anthropic stop_reason |
|---|---|
"end_turn" |
"end_turn" |
"max_tokens" |
"max_tokens" |
"tool_use" |
"tool_use" |
"stop_sequence" |
"stop_sequence" |
"pause_turn" |
"pause_turn" |
"refusal" |
"refusal" |
"content_filter" |
"end_turn"(降级) |
降级处理:
| Canonical 字段 | Anthropic 不支持时 | 降级策略 |
|---|---|---|
stop_reason: "content_filter" |
Anthropic 无此值 | 自动映射为 "end_turn" |
reasoning_tokens |
Anthropic 无此字段 | 丢弃 |
协议特有内容:
| 字段 | 处理方式 |
|---|---|
redacted_thinking |
编码时不产出 |
stop_sequence |
编码时始终为 null |
stop_details |
编码时不产出 |
container |
编码时不产出 |
text.citations |
编码时不产出 |
thinking.signature |
编码时不产出(同协议透传时自然保留) |
6. 核心层 — 流式编解码
6.1 SSE 格式
Anthropic 使用命名 SSE 事件,与 CanonicalStreamEvent 几乎 1:1 对应:
event: message_start
data: {"type":"message_start","message":{"id":"msg_xxx","model":"claude-4",...}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":10}}
event: message_stop
data: {"type":"message_stop"}
event: ping
data: {"type":"ping"}
6.2 StreamDecoder(Anthropic SSE → Canonical 事件)
Anthropic SSE 事件与 CanonicalStreamEvent 几乎 1:1 映射,状态机最简单:
| Anthropic SSE 事件 | Canonical 事件 | 说明 |
|---|---|---|
message_start |
MessageStartEvent | 直接映射 |
content_block_start |
ContentBlockStartEvent | 直接映射 content_block |
content_block_delta |
ContentBlockDeltaEvent | 见下方 delta 类型映射表 |
content_block_stop |
ContentBlockStopEvent | 直接映射 |
message_delta |
MessageDeltaEvent | 直接映射 delta 和 usage |
message_stop |
MessageStopEvent | 直接映射 |
ping |
PingEvent | 直接映射 |
error |
ErrorEvent | 直接映射 |
delta 类型映射(content_block_delta 事件内):
| Anthropic delta 类型 | Canonical delta 类型 | 说明 |
|---|---|---|
text_delta |
{type: "text_delta", text} |
直接映射 |
input_json_delta |
{type: "input_json_delta", partial_json} |
直接映射 |
thinking_delta |
{type: "thinking_delta", thinking} |
直接映射 |
citations_delta |
丢弃 | 协议特有,不晋升为公共字段 |
signature_delta |
丢弃 | 协议特有(用于多轮思考签名连续性),不晋升 |
content_block_start 类型映射:
| Anthropic content_block 类型 | Canonical content_block | 说明 |
|---|---|---|
{type: "text", text: ""} |
{type: "text", text: ""} |
直接映射 |
{type: "tool_use", id, name, input: {}} |
{type: "tool_use", id, name, input: {}} |
直接映射 |
{type: "thinking", thinking: ""} |
{type: "thinking", thinking: ""} |
直接映射 |
{type: "redacted_thinking", data: ""} |
丢弃整个 block | 跳过后续 delta 直到 content_block_stop |
server_tool_use / web_search_tool_result 等 |
丢弃 | 服务端工具块,协议特有 |
6.3 StreamDecoder 状态机
StreamDecoderState {
messageStarted: Boolean
openBlocks: Set<Integer>
currentBlockType: Map<Integer, String>
currentBlockId: Map<Integer, String>
redactedBlocks: Set<Integer> // 追踪需要丢弃的 redacted_thinking block
utf8Remainder: Option<ByteArray> // UTF-8 跨 chunk 安全
accumulatedUsage: Option<CanonicalUsage>
}
Anthropic Decoder 无需 OpenAI 的 toolCallIdMap / toolCallNameMap / toolCallArguments,因为 Anthropic 的事件已经有明确的结构。
关键处理:
redacted_thinking:在content_block_start事件中检测类型,将 index 加入redactedBlocks,后续 delta 和 stop 事件均丢弃citations_delta/signature_delta:在 delta 映射时直接丢弃,不影响 block 生命周期server_tool_use等服务端工具块:与redacted_thinking处理方式一致,加入redactedBlocks丢弃- UTF-8 安全:跨 chunk 截断的 UTF-8 字节需要用
utf8Remainder缓冲 - usage 累积:
message_delta中的 usage 与message_start中的 usage 合并
6.4 StreamEncoder(Canonical → Anthropic SSE)
| Canonical 事件 | Anthropic SSE 事件 | 说明 |
|---|---|---|
| MessageStartEvent | event: message_start |
直接映射 |
| ContentBlockStartEvent | event: content_block_start |
直接映射 content_block |
| ContentBlockDeltaEvent | event: content_block_delta |
见下方 delta 编码表 |
| ContentBlockStopEvent | event: content_block_stop |
直接映射 |
| MessageDeltaEvent | event: message_delta |
直接映射 |
| MessageStopEvent | event: message_stop |
直接映射 |
| PingEvent | event: ping |
直接映射 |
| ErrorEvent | event: error |
直接映射 |
delta 编码表:
| Canonical delta 类型 | Anthropic delta 类型 | 说明 |
|---|---|---|
{type: "text_delta", text} |
text_delta |
直接映射 |
{type: "input_json_delta", partial_json} |
input_json_delta |
直接映射 |
{type: "thinking_delta", thinking} |
thinking_delta |
直接映射 |
缓冲策略:无需缓冲,每个 Canonical 事件直接编码为对应的 Anthropic SSE 事件。
SSE 编码格式:
event: <event_type>\n
data: <json_payload>\n
\n
7. 扩展层接口
7.1 /models & /models/{model}
列表接口 GET /v1/models:
| 项目 | 说明 |
|---|---|
| 接口是否存在 | 是 |
| 请求格式 | GET 请求,支持 limit、after_id、before_id 查询参数 |
响应 Decoder(Anthropic → Canonical):
decodeModelsResponse(anthropicResp):
return CanonicalModelList {
models: anthropicResp.data.map(m => CanonicalModel {
id: m.id, name: m.display_name ?? m.id, created: parseTimestamp(m.created_at),
owned_by: "anthropic"})}
parseTimestamp(timestamp):
// Anthropic 返回 RFC 3339 字符串(如 "2025-05-14T00:00:00Z"),需转为 Unix 时间戳
return rfc3339ToUnix(timestamp) ?? 0
响应 Encoder(Canonical → Anthropic):
encodeModelsResponse(canonical):
return {data: canonical.models.map(m => ({
id: m.id,
display_name: m.name ?? m.id,
created_at: m.created ? unixToRfc3339(m.created) : epochRfc3339(),
type: "model"})),
has_more: false,
first_id: canonical.models[0]?.id, last_id: canonical.models.last?.id}
详情接口 GET /v1/models/{model}:
| 项目 | 说明 |
|---|---|
| 接口是否存在 | 是 |
| 请求格式 | GET 请求,路径参数 model_id |
响应 Decoder(Anthropic → Canonical):
decodeModelInfoResponse(anthropicResp):
return CanonicalModelInfo {
id: anthropicResp.id, name: anthropicResp.display_name ?? anthropicResp.id,
created: parseTimestamp(anthropicResp.created_at), owned_by: "anthropic" }
响应 Encoder(Canonical → Anthropic):
encodeModelInfoResponse(canonical):
return {id: canonical.id,
display_name: canonical.name ?? canonical.id,
created_at: canonical.created ? unixToRfc3339(canonical.created) : epochRfc3339(),
type: "model"}
字段映射(列表和详情共用):
| Anthropic | Canonical | 说明 |
|---|---|---|
data[].id |
models[].id |
直接映射 |
data[].display_name |
models[].name |
Anthropic 特有的显示名称 |
data[].created_at |
models[].created |
类型转换:Anthropic 为 RFC 3339 字符串,Canonical 为 Unix 时间戳 |
data[].type: "model" |
— | 固定值 |
has_more |
— | 编码时固定为 false |
first_id / last_id |
— | 从列表提取 |
data[].capabilities |
— | 协议特有,不晋升 |
data[].max_input_tokens |
— | 协议特有,不晋升 |
data[].max_tokens |
— | 协议特有,不晋升 |
跨协议对接示例(入站 /anthropic/v1/models,目标 OpenAI):
入站: GET /anthropic/v1/models, x-api-key: sk-ant-xxx
→ client=anthropic, provider=openai
→ URL: /v1/models, Headers: Authorization: Bearer sk-xxx
OpenAI 上游响应: {object: "list", data: [{id: "gpt-4o", object: "model", created: 1700000000, owned_by: "openai"}]}
→ OpenAI.decodeModelsResponse → CanonicalModelList
→ Anthropic.encodeModelsResponse
返回客户端: {data: [{id: "gpt-4o", display_name: "gpt-4o", created_at: "2023-11-04T18:26:40Z", type: "model"}],
has_more: false, first_id: "gpt-4o", last_id: "gpt-4o"}
8. 错误编码
8.1 错误响应格式
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "Error message"
}
}
8.2 encodeError
Anthropic.encodeError(error):
return {type: "error", error: {type: error.code, message: error.message}}
8.3 常用 HTTP 状态码
| HTTP Status | 说明 |
|---|---|
| 400 | 请求格式错误 |
| 401 | 认证失败(无效 API key) |
| 403 | 无权限访问 |
| 404 | 接口不存在 |
| 429 | 速率限制 |
| 500 | 服务器内部错误 |
| 529 | 服务过载 |
9. 自检清单
| 章节 | 检查项 |
|---|---|
| §2 | [x] detectInterfaceType(nativePath) 已实现,所有已知路径已覆盖 |
| §2 | [x] 所有 InterfaceType 的 supportsInterface 返回值已确定 |
| §2 | [x] 所有 InterfaceType 的 buildUrl 映射已确定 |
| §3 | [x] buildHeaders(provider) 已实现,adapter_config 契约已文档化 |
| §4 | [x] Chat 请求的 Decoder 和 Encoder 已实现(逐字段对照 §4.1/§4.2) |
| §4 | [x] 角色映射和消息顺序约束已处理(tool→user 合并、首消息 user 保证、交替约束) |
| §4 | [x] 工具调用(tool_use / tool_result)的编解码已处理 |
| §4 | [x] 协议特有字段已识别并确定处理方式(cache_control 忽略、redacted_thinking 丢弃) |
| §5 | [x] Chat 响应的 Decoder 和 Encoder 已实现(逐字段对照 §4.7) |
| §5 | [x] stop_reason 映射表已确认 |
| §5 | [x] usage 字段映射已确认(input_tokens / cache_read_input_tokens 等) |
| §6 | [x] 流式 StreamDecoder 和 StreamEncoder 已实现(对照 §4.8) |
| §7 | [x] 扩展层接口的编解码已实现(/models、/models/{model}) |
| §8 | [x] encodeError 已实现 |