实现统一模型 ID 格式 (provider_id/model_name),支持跨协议模型标识和 Smart Passthrough。 核心变更: - 新增 pkg/modelid 包:解析、格式化、校验统一模型 ID - 数据库迁移:models 表使用 UUID 主键 + UNIQUE(provider_id, model_name) 约束 - Repository 层:FindByProviderAndModelName、ListEnabled 方法 - Service 层:联合唯一校验、provider ID 字符集校验 - Conversion 层:ExtractModelName、RewriteRequestModelName/RewriteResponseModelName 方法 - Handler 层:统一模型 ID 路由、Smart Passthrough、Models API 本地聚合 - 新增 error-responses、unified-model-id 规范 测试覆盖: - 单元测试:modelid、conversion、handler、service、repository - 集成测试:统一模型 ID 路由、Smart Passthrough 保真性、跨协议转换 - 迁移测试:UUID 主键、UNIQUE 约束、级联删除 OpenSpec: - 归档 unified-model-id 变更到 archive/2026-04-21-unified-model-id - 同步 11 个 delta specs 到 main specs - 新增 error-responses、unified-model-id 规范文件
5.8 KiB
5.8 KiB
Request Validation
Purpose
定义请求验证规范,覆盖 AI 协议请求(OpenAI、Anthropic)和管理 API 请求的验证规则,确保输入数据的合法性和安全性。
Requirements
Requirement: 使用 validator 库
系统 SHALL 使用 go-playground/validator 进行请求验证。
Scenario: 验证器初始化
- WHEN 应用启动
- THEN SHALL 初始化 validator 实例
- THEN SHALL 注册自定义验证规则
Scenario: 验证规则定义
- WHEN 定义请求结构体
- THEN SHALL 使用 struct tag 定义验证规则
- THEN SHALL 支持必需字段、范围、格式等验证
Requirement: 验证 OpenAI 请求
系统 SHALL 验证 OpenAI ChatCompletionRequest,验证逻辑位于 ProtocolAdapter 的 decodeRequest 内。
Scenario: 必需字段验证
- WHEN OpenAI Adapter 的 decodeRequest 解析请求
- THEN SHALL 验证 model 字段不为空
- THEN SHALL 验证 messages 字段不为空且至少有一条消息
- THEN 验证失败 SHALL 返回 INVALID_INPUT 类型的 ConversionError
Scenario: 参数范围验证
- WHEN OpenAI Adapter 的 decodeRequest 解析参数
- THEN SHALL 验证 temperature 范围在 [0, 2]
- THEN SHALL 验证 max_tokens 大于 0
- THEN SHALL 验证 top_p 范围在 (0, 1]
Scenario: 消息内容验证
- WHEN 验证 messages 字段
- THEN SHALL 验证每条消息的 role 有效(system、developer、user、assistant、tool)
- THEN SHALL 验证 content 不为空
Requirement: 验证 Anthropic 请求
系统 SHALL 验证 Anthropic MessagesRequest,验证逻辑位于 ProtocolAdapter 的 decodeRequest 内。
Scenario: 必需字段验证
- WHEN Anthropic Adapter 的 decodeRequest 解析请求
- THEN SHALL 验证 model 字段不为空
- THEN SHALL 验证 messages 字段不为空且至少有一条消息
- THEN SHALL 验证 max_tokens 大于 0(或使用默认值)
Scenario: 参数范围验证
- WHEN Anthropic Adapter 的 decodeRequest 解析参数
- THEN SHALL 验证 temperature 范围在 [0, 1]
- THEN SHALL 验证 top_p 范围在 (0, 1]
Scenario: 消息内容验证
- WHEN 验证 messages 字段
- THEN SHALL 验证每条消息的 role 有效(user、assistant)
- THEN SHALL 验证 content 数组不为空
Requirement: 验证管理 API 请求
系统 SHALL 验证管理 API 请求。
Scenario: Provider 创建验证
- WHEN 创建 Provider
- THEN SHALL 验证 id、name、api_key、base_url 字段不为空
- THEN SHALL 验证 base_url 格式有效(URL 格式)
- THEN SHALL 验证 id 格式(字母、数字、下划线、连字符)
Scenario: Model 创建验证
- WHEN 创建 Model
- THEN SHALL 验证 id、provider_id、model_name 字段不为空
- THEN SHALL 验证 provider_id 存在
Scenario: 更新请求验证
- WHEN 更新资源
- THEN SHALL 验证至少提供一个可更新字段
- THEN SHALL 验证字段值有效性
Requirement: 供应商 ID 校验
创建供应商时,SHALL 对 id 字段进行字符集校验。
Scenario: 合法字符集
- WHEN 创建供应商,id 仅包含
[a-zA-Z0-9_]字符 - THEN SHALL 校验通过
Scenario: 非法字符
- WHEN 创建供应商,id 包含
-、.、/、空格、中文等非[a-zA-Z0-9_]字符 - THEN SHALL 返回 400 错误
Scenario: 长度限制
- WHEN 创建供应商,id 长度超过 64
- THEN SHALL 返回 400 错误
Requirement: 模型创建校验
创建模型时,SHALL 对 provider_id + model_name 进行联合唯一性校验。
Scenario: 正常创建
- WHEN 创建模型,provider_id 存在且 provider_id + model_name 组合唯一
- THEN SHALL 校验通过
Scenario: 联合唯一冲突
- WHEN 创建模型,provider_id + model_name 组合已存在
- THEN SHALL 返回 409 错误
Scenario: model_name 为空
- WHEN 创建模型,未提供 model_name
- THEN SHALL 返回 400 错误
Requirement: 返回友好的验证错误
系统 SHALL 返回友好的验证错误响应。
Scenario: 转换错误格式
- WHEN decodeRequest 验证失败返回 ConversionError
- THEN ProxyHandler SHALL 使用 clientAdapter.encodeError 编码错误响应
- THEN 错误 SHALL 使用客户端协议的格式
Scenario: 多字段错误
- WHEN 多个字段验证失败
- THEN ConversionError.details SHALL 包含所有验证错误
- THEN 错误响应 SHALL 包含完整的验证错误信息
Requirement: 在 handler 中应用验证
系统 SHALL 在 handler 中应用验证。
Scenario: 验证中间件
- WHEN 使用验证中间件
- THEN SHALL 在请求解析后立即验证
- THEN SHALL 在验证失败时提前返回错误
- THEN SHALL 避免执行后续处理逻辑
Scenario: 验证时机
- WHEN 处理请求
- THEN SHALL 在 handler 函数开始时验证
- THEN SHALL 在验证通过后才执行业务逻辑
Requirement: 使用标准库解析 JSON
系统 SHALL 使用 encoding/json 标准库解析 JSON 请求。
Scenario: 提取 model 字段
- WHEN 从请求体提取 model 字段
- THEN SHALL 使用 json.Unmarshal 解析到结构体
- THEN SHALL NOT 手动扫描字节查找字段
- THEN 解析失败 SHALL 返回空字符串(不报错)
Scenario: 检测 stream 字段
- WHEN 检测请求是否为流式请求
- THEN SHALL 使用 json.Unmarshal 解析到结构体
- THEN SHALL NOT 手动扫描字节查找字段
- THEN 解析失败 SHALL 返回 false(非流式)
Scenario: JSON 解析健壮性
- WHEN 解析 JSON 请求体
- THEN SHALL 正确处理转义字符
- THEN SHALL 正确处理嵌套结构
- THEN SHALL 正确处理 Unicode 字符
- THEN 解析失败 SHALL 有明确的错误处理