feat: 实现分层架构,包含 domain、service、repository 和 pkg 层
- 新增 domain 层:model、provider、route、stats 实体 - 新增 service 层:models、providers、routing、stats 业务逻辑 - 新增 repository 层:models、providers、stats 数据访问 - 新增 pkg 工具包:errors、logger、validator - 新增中间件:CORS、logging、recovery、request ID - 新增数据库迁移:初始 schema 和索引 - 新增单元测试和集成测试 - 新增规范文档:config-management、database-migration、error-handling、layered-architecture、middleware-system、request-validation、structured-logging、test-coverage - 移除 config 子包和 model_router(已迁移至分层架构)
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
# Anthropic 协议代理
|
||||
# Anthropic Protocol Proxy
|
||||
|
||||
## Purpose
|
||||
|
||||
TBD - 提供 Anthropic Messages API 的代理功能,通过协议转换实现与 OpenAI 兼容供应商的互操作
|
||||
|
||||
## Requirements
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 支持 Anthropic Messages API 端点
|
||||
|
||||
@@ -26,6 +22,8 @@ TBD - 提供 Anthropic Messages API 的代理功能,通过协议转换实现
|
||||
- **THEN** 网关 SHALL 将 OpenAI 流事件转换为 Anthropic 流事件
|
||||
- **THEN** 网关 SHALL 使用 SSE 格式将转换后的事件流式返回给应用
|
||||
|
||||
**变更说明:** handler 通过 service 层调用,而非直接调用 config 和 provider 包。API 接口保持不变。
|
||||
|
||||
### Requirement: 将 Anthropic 请求转换为 OpenAI 格式
|
||||
|
||||
网关 SHALL 将 Anthropic Messages API 请求转换为 OpenAI Chat Completions API 格式。
|
||||
@@ -41,138 +39,38 @@ TBD - 提供 Anthropic Messages API 的代理功能,通过协议转换实现
|
||||
- **THEN** 网关 SHALL 在转换后的 OpenAI 请求中保留这些消息
|
||||
- **THEN** 网关 SHALL 保留每条消息的 role 和 content
|
||||
|
||||
#### Scenario: Tools 转换
|
||||
**变更说明:** 协议转换逻辑保持不变,仅调用方式改为通过 service 层。
|
||||
|
||||
- **WHEN** Anthropic 请求包含带有 `input_schema` 的 `tools`
|
||||
- **THEN** 网关 SHALL 将每个工具转换为 OpenAI 格式,使用 `function.parameters` 替代 `input_schema`
|
||||
- **THEN** 网关 SHALL 保留工具名称和描述
|
||||
## ADDED Requirements
|
||||
|
||||
#### Scenario: Tool choice 转换
|
||||
### Requirement: 使用 service 层处理请求
|
||||
|
||||
- **WHEN** Anthropic 请求包含 `type: "auto"` 的 `tool_choice`
|
||||
- **THEN** 网关 SHALL 将其转换为 OpenAI 格式的 `"auto"`
|
||||
- **WHEN** Anthropic 请求包含 `type: "any"` 的 `tool_choice`
|
||||
- **THEN** 网关 SHALL 将其转换为 OpenAI 格式的 `"auto"`
|
||||
- **WHEN** Anthropic 请求包含 `type: "tool"` 和 `name` 的 `tool_choice`
|
||||
- **THEN** 网关 SHALL 将其转换为 OpenAI 格式的 `{"type": "function", "function": {"name": <name>}}`
|
||||
Handler SHALL 通过 service 层处理业务逻辑。
|
||||
|
||||
#### Scenario: Tool result 转换
|
||||
#### Scenario: 调用 routing service
|
||||
|
||||
- **WHEN** Anthropic 请求包含用户消息,其 `content` 数组包含 `type: "tool_result"` 块
|
||||
- **THEN** 网关 SHALL 将每个工具结果转换为 `role: "tool"` 的消息
|
||||
- **THEN** 网关 SHALL 从 `tool_use_id` 设置 `tool_call_id`
|
||||
- **THEN** 网关 SHALL 保留 content
|
||||
- **WHEN** handler 收到请求并转换为 OpenAI 格式
|
||||
- **THEN** SHALL 调用 RoutingService.Route() 获取路由结果
|
||||
- **THEN** SHALL 使用路由结果中的供应商信息
|
||||
|
||||
#### Scenario: Max tokens 处理
|
||||
#### Scenario: 调用 stats service
|
||||
|
||||
- **WHEN** Anthropic 请求包含 `max_tokens`
|
||||
- **THEN** 网关 SHALL 在 OpenAI 请求中包含它作为 `max_tokens`
|
||||
- **WHEN** Anthropic 请求不包含 `max_tokens`
|
||||
- **THEN** 网关 SHALL 设置默认值(4096)以满足 Anthropic 的要求
|
||||
- **WHEN** 请求成功完成
|
||||
- **THEN** SHALL 调用 StatsService.Record() 记录统计
|
||||
- **THEN** SHALL 异步记录统计(不阻塞响应)
|
||||
|
||||
### Requirement: 将 OpenAI 响应转换为 Anthropic 格式
|
||||
### Requirement: 使用结构化错误处理
|
||||
|
||||
网关 SHALL 将 OpenAI Chat Completions API 响应转换为 Anthropic Messages API 格式。
|
||||
Handler SHALL 使用结构化错误处理。
|
||||
|
||||
#### Scenario: Content 转换
|
||||
#### Scenario: 协议转换错误
|
||||
|
||||
- **WHEN** OpenAI 响应包含 `choices[0].message.content`
|
||||
- **THEN** 网关 SHALL 将其转换为 Anthropic 格式的 `content: [{"type": "text", "text": <content>}]`
|
||||
- **WHEN** 协议转换失败
|
||||
- **THEN** SHALL 返回结构化错误响应
|
||||
- **THEN** SHALL 包含详细的错误信息
|
||||
|
||||
#### Scenario: Tool calls 转换
|
||||
#### Scenario: 路由错误处理
|
||||
|
||||
- **WHEN** OpenAI 响应包含 `choices[0].message.tool_calls`
|
||||
- **THEN** 网关 SHALL 将每个工具调用转换为 `type: "tool_use"` 的内容块
|
||||
- **THEN** 网关 SHALL 从 `tool_calls[].id` 设置 `id`
|
||||
- **THEN** 网关 SHALL 从 `tool_calls[].function.name` 设置 `name`
|
||||
- **THEN** 网关 SHALL 解析 `arguments` JSON 字符串并将其设置为 `input` 对象
|
||||
|
||||
#### Scenario: Finish reason 转换
|
||||
|
||||
- **WHEN** OpenAI 响应的 `finish_reason` 为 `"stop"`
|
||||
- **THEN** 网关 SHALL 在 Anthropic 响应中设置 `stop_reason: "end_turn"`
|
||||
- **WHEN** OpenAI 响应的 `finish_reason` 为 `"tool_calls"`
|
||||
- **THEN** 网关 SHALL 在 Anthropic 响应中设置 `stop_reason: "tool_use"`
|
||||
|
||||
#### Scenario: Usage 转换
|
||||
|
||||
- **WHEN** OpenAI 响应包含带有 `prompt_tokens` 和 `completion_tokens` 的 `usage`
|
||||
- **THEN** 网关 SHALL 转换为 Anthropic 格式,使用 `input_tokens` 和 `output_tokens`
|
||||
|
||||
### Requirement: 转换流式事件
|
||||
|
||||
网关 SHALL 实时将 OpenAI 流事件转换为 Anthropic 流事件。
|
||||
|
||||
#### Scenario: Message start 事件
|
||||
|
||||
- **WHEN** 网关开始流式传输 Anthropic 响应
|
||||
- **THEN** 网关 SHALL 发送带有消息元数据的 `message_start` 事件
|
||||
|
||||
#### Scenario: Content block start 事件
|
||||
|
||||
- **WHEN** OpenAI 流开始返回内容
|
||||
- **THEN** 网关 SHALL 发送带有 `type: "text"` 的 `content_block_start` 事件
|
||||
|
||||
#### Scenario: Content delta 事件
|
||||
|
||||
- **WHEN** OpenAI 流发送带有内容的 delta
|
||||
- **THEN** 网关 SHALL 发送带有 `type: "text_delta"` 的 `content_block_delta` 事件,包含文本
|
||||
|
||||
#### Scenario: Tool use 流式传输
|
||||
|
||||
- **WHEN** OpenAI 流发送工具调用 delta
|
||||
- **THEN** 网关 SHALL 缓冲 `arguments` 块
|
||||
- **THEN** 网关 SHALL 在工具调用开始时发送带有 `type: "tool_use"` 的 `content_block_start`
|
||||
- **THEN** 网关 SHALL 发送带有部分 JSON 的 `input_delta` 事件
|
||||
|
||||
#### Scenario: Content block stop 事件
|
||||
|
||||
- **WHEN** 内容块完成
|
||||
- **THEN** 网关 SHALL 发送 `content_block_stop` 事件
|
||||
|
||||
#### Scenario: Message stop 事件
|
||||
|
||||
- **WHEN** OpenAI 流完成
|
||||
- **THEN** 网关 SHALL 发送 `message_stop` 事件
|
||||
|
||||
### Requirement: 支持 Anthropic 特有功能
|
||||
|
||||
网关 SHALL 支持映射到 OpenAI 能力的 Anthropic 特有功能。
|
||||
|
||||
#### Scenario: System prompt 作为独立字段
|
||||
|
||||
- **WHEN** Anthropic 请求包含 `system` 字段
|
||||
- **THEN** 网关 SHALL 将其作为 OpenAI 格式的 system 消息处理
|
||||
|
||||
#### Scenario: 必需的 max_tokens
|
||||
|
||||
- **WHEN** 收到 Anthropic 请求
|
||||
- **THEN** 网关 SHALL 确保 `max_tokens` 存在(如果未提供则使用默认值)
|
||||
|
||||
### Requirement: 处理纯文本内容
|
||||
|
||||
网关 SHALL 在 Anthropic 请求和响应中支持纯文本内容。
|
||||
|
||||
#### Scenario: 消息中的文本内容
|
||||
|
||||
- **WHEN** Anthropic 请求在消息中包含文本内容
|
||||
- **THEN** 网关 SHALL 正确处理和转发文本内容
|
||||
|
||||
#### Scenario: 拒绝多模态内容
|
||||
|
||||
- **WHEN** Anthropic 请求包含多模态内容(图片、文档)
|
||||
- **THEN** 网关 SHALL 返回错误,指示 MVP 不支持多模态内容
|
||||
|
||||
### Requirement: 保留请求元数据
|
||||
|
||||
网关 SHALL 在转换过程中保留请求元数据。
|
||||
|
||||
#### Scenario: 模型名称保留
|
||||
|
||||
- **WHEN** Anthropic 请求指定模型名称
|
||||
- **THEN** 网关 SHALL 在转换后的 OpenAI 请求中保留模型名称
|
||||
|
||||
#### Scenario: 自定义参数
|
||||
|
||||
- **WHEN** Anthropic 请求包含自定义参数(temperature, top_p 等)
|
||||
- **THEN** 网关 SHALL 在转换后的请求中保留这些参数
|
||||
- **WHEN** RoutingService 返回错误
|
||||
- **THEN** SHALL 转换为对应的 AppError
|
||||
- **THEN** SHALL 返回统一的错误响应
|
||||
|
||||
Reference in New Issue
Block a user