# Error Responses ## Purpose 定义系统统一的错误响应格式和各类错误场景,确保客户端能够一致地处理错误。 ## Requirements ### Requirement: 统一错误响应格式 系统 SHALL 使用统一的错误响应格式。 #### Scenario: 标准错误格式 - **WHEN** 返回错误响应 - **THEN** SHALL 使用以下 JSON 格式: ```json { "error": "错误描述", "code": "ERROR_CODE" } ``` - **THEN** `error` 字段 SHALL 包含人类可读的错误描述 - **THEN** `code` 字段 SHALL 包含机器可读的错误代码(可选) ### Requirement: 网关层代理错误使用应用统一格式 系统 SHALL 对代理接口中由网关自身产生的错误使用应用统一错误响应格式。 #### Scenario: 标准网关错误格式 - **WHEN** 代理接口返回网关层错误 - **THEN** SHALL 使用以下 JSON 格式: ```json { "error": "错误描述", "code": "ERROR_CODE" } ``` - **THEN** `error` 字段 SHALL 包含人类可读的错误描述 - **THEN** `code` 字段 SHALL 包含机器可读的错误码 #### Scenario: 网关错误码集合 - **WHEN** 代理接口返回网关层错误 - **THEN** code SHALL 使用以下枚举之一:`INVALID_JSON`、`INVALID_REQUEST`、`INVALID_MODEL_ID`、`MODEL_NOT_FOUND`、`PROVIDER_NOT_FOUND`、`UNSUPPORTED_INTERFACE`、`UNSUPPORTED_MULTIMODAL`、`CONVERSION_FAILED`、`UPSTREAM_UNAVAILABLE` ### Requirement: 代理接口上游错误透传 系统 SHALL 对代理接口中已经收到的上游 HTTP 错误响应执行透明透传。 #### Scenario: 非流式上游非 2xx 响应 - **WHEN** 非流式代理请求收到上游 HTTP 响应且状态码不是 2xx - **THEN** SHALL 透传上游 status code - **THEN** SHALL 透传过滤 hop-by-hop header 后的上游 headers - **THEN** SHALL 透传上游 body - **THEN** SHALL NOT 将上游错误包装为应用统一错误 - **THEN** SHALL NOT 将上游错误转换为客户端协议错误格式 #### Scenario: 流式上游非 2xx 响应 - **WHEN** 流式代理请求收到上游 HTTP 响应且状态码不是 2xx - **THEN** SHALL 透传上游 status code - **THEN** SHALL 透传过滤 hop-by-hop header 后的上游 headers - **THEN** SHALL 透传上游 body - **THEN** SHALL NOT 创建 StreamConverter ### Requirement: 上游不可达错误 系统 SHALL 在没有收到上游 HTTP 响应时返回网关层错误。 #### Scenario: 上游连接失败 - **WHEN** ProviderClient 因 DNS、连接失败、TLS、超时或上下文取消等原因无法获得上游 HTTP 响应 - **THEN** SHALL 返回 HTTP 502 或合适的 5xx 状态码 - **THEN** SHALL 返回应用统一错误格式 - **THEN** code SHALL 为 `UPSTREAM_UNAVAILABLE` ### Requirement: Hop-by-hop header 过滤 系统 SHALL 在透传上游错误响应时过滤 hop-by-hop headers。 #### Scenario: 过滤连接级 header - **WHEN** 透传上游错误响应 headers - **THEN** SHALL 过滤 `Connection`、`Transfer-Encoding`、`Keep-Alive`、`Proxy-Authenticate`、`Proxy-Authorization`、`TE`、`Trailer`、`Upgrade` - **THEN** SHALL 保留 `Content-Type` 等普通响应 header ### Requirement: 前端提取并处理错误码 前端 SHALL 提取后端结构化错误响应中的错误码并用于错误处理。 #### Scenario: API 客户端解析结构化错误 - **WHEN** 后端返回错误响应 - **THEN** API 客户端 SHALL 尝试解析 JSON 格式 `{error: string, code?: string}` - **THEN** 如解析成功且包含 code 字段,SHALL 创建包含 code 的 ApiError - **THEN** 如解析失败或不包含 code,SHALL 创建不包含 code 的 ApiError #### Scenario: ApiError 包含错误码 - **WHEN** 创建 ApiError 对象 - **THEN** ApiError 类 SHALL 包含可选的 code 字段 - **THEN** code 字段类型 SHALL 为 `string | undefined` - **THEN** 构造函数 SHALL 接受可选的 code 参数 #### Scenario: Hooks 使用错误码映射友好消息 - **WHEN** useMutation 或其他 Hook 处理错误 - **THEN** SHALL 检查 error.code 是否存在 - **THEN** 如存在,SHALL 使用映射表转换为友好中文消息 - **THEN** 如不存在或未定义映射,SHALL 使用 error.message #### Scenario: 错误码映射表定义 - **WHEN** 定义错误码映射表 - **THEN** 映射表 SHALL 包含以下键值对: - `duplicate_model` → "同一供应商下模型名称已存在" - `invalid_provider_id` → "供应商 ID 仅允许字母、数字、下划线,长度 1-64" - `immutable_field` → "供应商 ID 不允许修改" - `provider_not_found` → "供应商不存在" - **THEN** 映射表 SHALL 使用 TypeScript Record 类型确保类型安全 #### Scenario: 错误码映射降级处理 - **WHEN** 后端返回新的错误码(映射表未定义) - **THEN** 前端 SHALL 降级使用 error.message - **THEN** 前端 SHALL NOT 抛出错误或崩溃 - **THEN** 用户 SHALL 仍能看到原始错误消息 ### Requirement: provider_id 校验错误 系统 SHALL 对 provider_id 校验错误返回明确的错误信息。 #### Scenario: provider_id 包含非法字符 - **WHEN** 创建或更新供应商时,provider_id 包含非 `[a-zA-Z0-9_]` 字符 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "供应商 ID 仅允许字母、数字、下划线", "code": "INVALID_PROVIDER_ID" } ``` #### Scenario: provider_id 长度超限 - **WHEN** 创建或更新供应商时,provider_id 长度超过 64 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "供应商 ID 长度不能超过 64 个字符", "code": "INVALID_PROVIDER_ID" } ``` ### Requirement: 联合唯一约束冲突错误 系统 SHALL 对联合唯一约束冲突返回明确的错误信息。 #### Scenario: 创建模型时 provider_id + model_name 组合已存在 - **WHEN** 创建模型时,provider_id + model_name 组合已存在 - **THEN** SHALL 返回 HTTP 409 Conflict - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "同一供应商下模型名称已存在", "code": "duplicate_model" } ``` #### Scenario: 更新模型时导致 provider_id + model_name 组合冲突 - **WHEN** 更新模型时,修改 provider_id 或 model_name 导致与已有记录冲突 - **THEN** SHALL 返回 HTTP 409 Conflict - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "同一供应商下模型名称已存在", "code": "duplicate_model" } ``` ### Requirement: 资源不存在错误 系统 SHALL 对资源不存在返回明确的错误信息。 #### Scenario: 模型不存在 - **WHEN** 查询或操作不存在的模型 - **THEN** SHALL 返回 HTTP 404 Not Found - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "模型未找到" } ``` #### Scenario: 供应商不存在 - **WHEN** 创建模型时指定的供应商不存在 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "供应商不存在" } ``` ### Requirement: 统一模型 ID 格式错误 系统 SHALL 对统一模型 ID 格式错误返回明确的错误信息。 #### Scenario: 统一模型 ID 格式无效 - **WHEN** 代理请求中的 model 字段不是有效的统一模型 ID 格式 - **THEN** 请求 SHALL 走 forwardPassthrough 透传到上游(兼容未适配的客户端) - **THEN** 不返回错误,保持与上游的兼容性 **设计理由:** - 统一模型 ID 是 BREAKING CHANGE,部分旧客户端可能仍使用原始模型名 - 透传策略允许上游自行判断并返回错误(如 404 model not found) - 网关作为透明代理,不应拦截所有格式非法的请求 #### Scenario: 统一模型 ID 格式有效但对应模型不存在 - **WHEN** 代理请求中的 model 字段是有效的统一模型 ID 格式(含 `/`),但数据库中找不到对应的模型 - **THEN** SHALL 返回 HTTP 404 Not Found - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "模型未找到" } ``` #### Scenario: 统一模型 ID 对应的模型不存在 - **WHEN** 解析统一模型 ID 后,数据库中找不到对应的模型 - **THEN** SHALL 返回 HTTP 404 Not Found - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "模型未找到" } ``` #### Scenario: 统一模型 ID 对应的模型已禁用 - **WHEN** 解析统一模型 ID 后,对应的模型 enabled 为 false - **THEN** SHALL 返回 HTTP 404 Not Found - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "模型未找到" } ``` #### Scenario: 统一模型 ID 对应的供应商已禁用 - **WHEN** 解析统一模型 ID 后,对应的供应商 enabled 为 false - **THEN** SHALL 返回 HTTP 404 Not Found - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "模型未找到" } ``` ### Requirement: JSON 格式错误 系统 SHALL 对请求体 JSON 格式错误返回明确的错误信息。 #### Scenario: 请求体 JSON 格式错误 - **WHEN** 代理请求的请求体不是有效的 JSON 格式,且该接口需要网关解析请求体 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "请求体 JSON 格式错误", "code": "INVALID_JSON" } ``` #### Scenario: Smart Passthrough 时请求体 JSON 格式错误 - **WHEN** 同协议 Smart Passthrough 场景下,请求体 JSON 格式不正确 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "请求体 JSON 格式错误", "code": "INVALID_JSON" } ``` ### Requirement: 不可变字段错误 系统 SHALL 对尝试修改不可变字段返回明确的错误信息。 #### Scenario: 尝试修改供应商 ID - **WHEN** 更新供应商时,请求体中包含 `id` 字段 - **THEN** SHALL 返回 HTTP 400 Bad Request - **THEN** SHALL 返回以下 JSON 格式: ```json { "error": "供应商 ID 不允许修改", "code": "IMMUTABLE_FIELD" } ```