1
0
Files
nex/openspec/specs/error-responses/spec.md

316 lines
9.9 KiB
Markdown
Raw 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.
# 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** 如解析失败或不包含 codeSHALL 创建不包含 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"
}
```