1
0
Files
lanyuanxiaoyao 56ecc73d1b docs: 整合 openspec 规范,合并配置和前端相关独立 spec
将 cli-config、config-priority、env-config 合并入 config-management;
将 tdesign-integration、recharts-integration、frontend-config-ui、
frontend-testing、stats-dashboard 合并入新的 frontend/spec.md;
清理其余 spec 中的冗余标记,补充缺失场景。
2026-04-20 19:55:56 +08:00

273 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Protocol Adapter - Anthropic
## Purpose
实现 Anthropic 协议的完整 ProtocolAdapter支持请求/响应编解码、流式转换和错误处理,遵循 Anthropic Messages API 规范。
### Requirement: 实现 Anthropic ProtocolAdapter
系统 SHALL 全新实现 Anthropic 协议的完整 ProtocolAdapter对照 `docs/conversion_anthropic.md`。不沿用旧 `internal/protocol/anthropic/` 代码。
- `protocolName()` SHALL 返回 `"anthropic"`
- `supportsPassthrough()` SHALL 返回 true
- `buildHeaders(provider)` SHALL 构建 `x-api-key``anthropic-version``anthropic-beta``Content-Type`
- `buildUrl(nativePath, interfaceType)` SHALL 按接口类型映射 URL 路径
- `supportsInterface()` SHALL 对 CHAT、MODELS、MODEL_INFO 返回 true对 EMBEDDINGS、RERANK 返回 false
#### Scenario: 认证 Header 构建
- **WHEN** 调用 buildHeaders(provider)
- **THEN** SHALL 设置 `x-api-key: <provider.api_key>`
- **THEN** SHALL 设置 `anthropic-version`(默认 `"2023-06-01"`,从 adapter_config 可覆盖)
- **WHEN** adapter_config 包含 anthropic_beta
- **THEN** SHALL 以逗号拼接为 `anthropic-beta` Header
#### Scenario: URL 映射
- **WHEN** interfaceType == CHAT
- **THEN** SHALL 映射为 `/v1/messages`
- **WHEN** interfaceType == MODELS
- **THEN** SHALL 映射为 `/v1/models`
- **WHEN** interfaceType == EMBEDDINGS 或 RERANK
- **THEN** SHALL NOT 调用 buildUrlsupportsInterface 返回 false引擎走透传
### Requirement: Anthropic 请求解码Anthropic → Canonical
系统 SHALL 实现完整的 Anthropic MessagesRequest 到 CanonicalRequest 的解码。
#### Scenario: System 消息提取
- **WHEN** Anthropic 请求包含顶层 `system` 字段
- **THEN** String 类型 SHALL 直接提取为 canonical.system
- **THEN** SystemBlock 数组 SHALL 提取为 canonical.systemArray
#### Scenario: User 消息中 tool_result 拆分
- **WHEN** Anthropic user 消息的 content 中包含 tool_result 块
- **THEN** SHALL 将 tool_result 块拆分为独立的 CanonicalMessage{role: "tool"}
- **THEN** 非 tool_result 块 SHALL 保留为独立的 CanonicalMessage{role: "user"}
- **THEN** 仅 tool_result 块时 SHALL 只产出 tool 角色消息
#### Scenario: 参数映射
- **WHEN** 解码 Anthropic 请求参数
- **THEN** max_tokens SHALL 直接映射
- **THEN** temperature/top_p/top_k SHALL 直接映射
- **THEN** stop_sequences SHALL 直接映射
#### Scenario: ThinkingConfig 解码
- **WHEN** 解码 Anthropic thinking 字段
- **THEN** type="enabled" SHALL 映射为 Canonical thinking.type="enabled"
- **THEN** type="disabled" SHALL 映射为 Canonical thinking.type="disabled"
- **THEN** type="adaptive" SHALL 映射为 Canonical thinking.type="adaptive"
- **THEN** budget_tokens 和 output_config.effort SHALL 直接映射
#### Scenario: 公共字段提取
- **WHEN** 解码 Anthropic 公共字段
- **THEN** metadata.user_id SHALL 提取为 user_id
- **THEN** output_config.formatjson_schema 类型SHALL 提取为 output_format
- **THEN** disable_parallel_tool_use SHALL 反转映射为 parallel_tool_usetrue → false
#### Scenario: 协议特有字段处理
- **WHEN** 解码遇到 redacted_thinking
- **THEN** SHALL 丢弃,不在中间层保留
- **WHEN** 解码遇到 cache_control
- **THEN** SHALL 忽略,不晋升为公共字段
### Requirement: Anthropic 请求编码Canonical → Anthropic
系统 SHALL 实现完整的 CanonicalRequest 到 Anthropic MessagesRequest 的编码。
#### Scenario: System 消息注入
- **WHEN** canonical.system 不为空
- **THEN** SHALL 编码为 Anthropic 顶层 `system` 字段
#### Scenario: Tool 角色合并到 User 消息
- **WHEN** CanonicalMessage{role: "tool"} 出现在消息序列中
- **THEN** SHALL 将其 tool_result 块合并到相邻的 Anthropic user 消息的 content 数组中
- **WHEN** 相邻前一条不是 user 消息
- **THEN** SHALL 创建新的 user 消息来承载 tool_result 块
#### Scenario: 首消息 user 保证
- **WHEN** 编码后的 Anthropic messages 数组首条消息不是 user 角色
- **THEN** SHALL 自动注入一条空 user 消息到头部
#### Scenario: 角色交替约束
- **WHEN** 编码后存在连续同角色消息
- **THEN** SHALL 合并为单条消息content 数组拼接)
#### Scenario: 参数编码
- **WHEN** 编码 CanonicalRequest 参数
- **THEN** parameters.max_tokens SHALL 直接映射Anthropic 必填)
- **THEN** parameters.top_k SHALL 直接映射
- **THEN** canonical.thinking.type="enabled" SHALL 映射为 thinking{type: "enabled", budget_tokens}
- **THEN** canonical.thinking.type="adaptive" SHALL 映射为 thinking{type: "adaptive"}
#### Scenario: 公共字段编码
- **WHEN** canonical.user_id 不为空
- **THEN** SHALL 编码为 metadata.user_id
- **WHEN** canonical.parallel_tool_use == false
- **THEN** SHALL 编码为 disable_parallel_tool_use: true
- **WHEN** canonical.output_format 存在
- **THEN** SHALL 编码为 output_config.format
#### Scenario: 降级处理
- **WHEN** canonical.output_format.type == "json_object"
- **THEN** SHALL 降级为 output_config.format{type: "json_schema", schema: {type: "object"}}
- **WHEN** canonical.output_format.type == "text"
- **THEN** SHALL 丢弃,不设置 output_config
### Requirement: Anthropic 响应解码Anthropic → Canonical
系统 SHALL 实现 Anthropic MessagesResponse 到 CanonicalResponse 的解码。
#### Scenario: 内容块解码
- **WHEN** Anthropic response 包含 text 块
- **THEN** SHALL 解码为 TextBlock
- **WHEN** 包含 tool_use 块
- **THEN** SHALL 解码为 ToolUseBlock
- **WHEN** 包含 thinking 块
- **THEN** SHALL 解码为 ThinkingBlock
- **WHEN** 包含 redacted_thinking 块
- **THEN** SHALL 丢弃
#### Scenario: 停止原因映射
- **WHEN** 解码 stop_reason
- **THEN** "end_turn" SHALL 映射为 "end_turn"
- **THEN** "max_tokens" SHALL 映射为 "max_tokens"
- **THEN** "tool_use" SHALL 映射为 "tool_use"
- **THEN** "stop_sequence" SHALL 映射为 "stop_sequence"
- **THEN** "refusal" SHALL 映射为 "refusal"
- **THEN** "pause_turn" SHALL 映射为 "pause_turn"
#### Scenario: Usage 映射
- **WHEN** 解码 Anthropic usage
- **THEN** input_tokens SHALL 直接映射
- **THEN** output_tokens SHALL 直接映射
- **THEN** cache_read_input_tokens SHALL 映射为 cache_read_tokens
- **THEN** cache_creation_input_tokens SHALL 映射为 cache_creation_tokens
### Requirement: Anthropic 响应编码Canonical → Anthropic
系统 SHALL 实现 CanonicalResponse 到 Anthropic MessagesResponse 的编码。
#### Scenario: 降级处理
- **WHEN** canonical.stop_reason 为 "content_filter"
- **THEN** SHALL 降级映射为 "end_turn"
- **WHEN** canonical.reasoning_tokens 不为空
- **THEN** SHALL 丢弃Anthropic 无此字段)
### Requirement: Anthropic 流式解码器
系统 SHALL 实现 AnthropicStreamDecoder将 Anthropic 命名 SSE 事件转换为 CanonicalStreamEvent。
Decoder 几乎 1:1 映射,维护最小状态机:
- messageStarted: 是否已发送 MessageStartEvent
- redactedBlocks: 需要丢弃的 block index 集合
- utf8Remainder: UTF-8 跨 chunk 安全缓冲
#### Scenario: 命名事件 1:1 映射
- **WHEN** 收到 `event: message_start`
- **THEN** SHALL 发出 MessageStartEvent
- **WHEN** 收到 `event: content_block_start`
- **THEN** SHALL 发出 ContentBlockStartEvent
- **WHEN** 收到 `event: content_block_delta`
- **THEN** SHALL 发出 ContentBlockDeltaEvent
- **WHEN** 收到 `event: content_block_stop`
- **THEN** SHALL 发出 ContentBlockStopEvent
- **WHEN** 收到 `event: message_delta`
- **THEN** SHALL 发出 MessageDeltaEvent
- **WHEN** 收到 `event: message_stop`
- **THEN** SHALL 发出 MessageStopEvent
- **WHEN** 收到 `event: ping`
- **THEN** SHALL 发出 PingEvent
- **WHEN** 收到 `event: error`
- **THEN** SHALL 发出 ErrorEvent
#### Scenario: redacted_thinking 块丢弃
- **WHEN** content_block_start 事件中 content_block.type 为 "redacted_thinking"
- **THEN** SHALL 将 index 加入 redactedBlocks
- **THEN** 后续该 index 的 delta 和 stop 事件 SHALL 丢弃
#### Scenario: 协议特有 delta 丢弃
- **WHEN** delta 类型为 citations_delta 或 signature_delta
- **THEN** SHALL 丢弃,不影响 block 生命周期
#### Scenario: 服务端工具块丢弃
- **WHEN** content_block_start 事件中类型为 server_tool_use / web_search_tool_result 等
- **THEN** SHALL 丢弃整个 block
### Requirement: Anthropic 流式编码器
系统 SHALL 实现 AnthropicStreamEncoder将 CanonicalStreamEvent 编码为 Anthropic 命名 SSE 事件。
#### Scenario: 直接映射,无缓冲
- **WHEN** 收到任意 CanonicalStreamEvent
- **THEN** SHALL 直接编码为对应的 Anthropic SSE 事件
- **THEN** SHALL NOT 缓冲等待(与 OpenAI 编码器不同)
#### Scenario: SSE 格式
- **WHEN** 编码输出
- **THEN** SHALL 使用 `event: <type>\ndata: <json>\n\n` 格式
#### Scenario: Delta 类型编码
- **WHEN** delta.type == "text_delta"
- **THEN** SHALL 编码为 Anthropic text_delta
- **WHEN** delta.type == "input_json_delta"
- **THEN** SHALL 编码为 Anthropic input_json_delta
- **WHEN** delta.type == "thinking_delta"
- **THEN** SHALL 编码为 Anthropic thinking_delta
### Requirement: Anthropic 错误编码
系统 SHALL 实现 Anthropic 协议的错误编码。
#### Scenario: 错误响应格式
- **WHEN** 调用 encodeError(conversionError)
- **THEN** SHALL 返回 `{type: "error", error: {type: <error_code>, message: <message>}}`
### Requirement: Anthropic 扩展层接口编解码
系统 SHALL 实现 Anthropic 协议的扩展层接口编解码(仅 Models
#### Scenario: /models 列表接口
- **WHEN** 解码 Anthropic models 响应
- **THEN** data[].display_name SHALL 映射为 models[].name
- **THEN** data[].created_atRFC 3339SHALL 转换为 models[].createdUnix 时间戳)
- **WHEN** 编码 CanonicalModelList 为 Anthropic 格式
- **THEN** SHALL 输出包含 has_more、first_id、last_id 的结构
- **THEN** models[].createdUnix 时间戳SHALL 转换为 RFC 3339 字符串
#### Scenario: /models 详情接口
- **WHEN** 解码/编码 model 详情
- **THEN** SHALL 处理 display_name ↔ name 和 RFC 3339 ↔ Unix 时间戳的转换
#### Scenario: EMBEDDINGS 和 RERANK 不支持
- **WHEN** interfaceType 为 EMBEDDINGS 或 RERANK
- **THEN** supportsInterface SHALL 返回 false
- **THEN** 引擎 SHALL 走透传或返回空响应