feat(openai): 完善 Chat 与 Responses 参数转发

原生 Chat/Responses 改为透明转发,保留标准工具结构并保护调用方显式参数。补齐 Responses 到 Chat 的兼容转换、协议路由边界、完整响应和流式事件,并同步更新 Swagger、回归测试与真实验收脚本。

验证:
- cd apps/api && env -u AI_GATEWAY_TEST_DATABASE_URL go test ./... -count=1
- pnpm openapi
- pnpm lint
- pnpm test
- pnpm build
- gofmt -l 无输出
- git diff --check 通过

风险:
- Chat 回退无法等价表达的 Responses 原生能力现在会返回 unsupported_response_parameter
- 真实供应商 E2E 因本地没有已启用的平台模型候选而未完成
This commit is contained in:
2026-08-04 19:26:48 +08:00
parent b2c9b4f6d9
commit fe8dcb40ca
22 changed files with 2098 additions and 309 deletions
+74 -19
View File
@@ -6450,7 +6450,7 @@
"BearerAuth": []
}
],
"description": "/api/v1/chat/completions 同步执行:stream=true 返回 text/event-stream SSEstream=false 或未传返回兼容 JSON;该接口忽略 X-Async。",
"description": "/api/v1/chat/completions 同步执行:除 Gateway 路由字段外透明转发当前及未来 OpenAI 字段与嵌套结构,完整保留 function/custom tools、tool_choice、tool_calls 和旧版 function_callstream=true 返回 text/event-stream SSEstream=false 或未传返回兼容 JSON;该接口忽略 X-Async。",
"consumes": [
"application/json"
],
@@ -8566,7 +8566,7 @@
"BearerAuth": []
}
],
"description": "公开 OpenAI-compatible Responses 入口。模型声明 openai_responses 时原生转发,否则使用 Chat Completions 转换;store 缺省为 true。previous_response_id 严格绑定首次成功的平台模型和上游协议,链路不可用时不跨平台续接未提供 previous_response_id 时调用方管理完整状态Gateway 以本轮 input/messages 为准且不追加本地历史。",
"description": "公开 OpenAI-compatible Responses 入口。模型声明 openai_responses 时除 Gateway 路由字段外透明转发当前及未来 OpenAI 字段;否则仅对具有等价 Chat Completions 语义的请求执行转换。background、conversation、prompt template、context management、max_tool_calls、自动 truncation、reasoning summary、内置工具、MCP 和 namespace 等原生专属能力不会静默丢弃:路由会排除 Chat 候选,固定 Chat 链路则返回带参数路径的 unsupported_response_parameter。store 缺省为 true。previous_response_id 严格绑定首次成功的平台模型和上游协议,链路不可用时不跨平台续接未提供 previous_response_id 时调用方历史权威Gateway 不追加本地历史。",
"consumes": [
"application/json"
],
@@ -8580,7 +8580,7 @@
"summary": "创建 OpenAI Responses",
"parameters": [
{
"description": "Responses 请求;Chat 回退支持自定义 function tools",
"description": "Responses 请求;Chat 回退支持 function/custom tools 及所有可等价能力",
"name": "input",
"in": "body",
"required": true,
@@ -8597,7 +8597,7 @@
}
},
"400": {
"description": "invalid_previous_response_id / unsupported_response_tool / unsupported_response_parameter",
"description": "invalid_previous_response_id / unsupported_response_parameter",
"schema": {
"$ref": "#/definitions/httpapi.OpenAIErrorEnvelope"
}
@@ -10810,6 +10810,7 @@
"type": "integer",
"example": 0
},
"logprobs": {},
"message": {
"$ref": "#/definitions/httpapi.ChatCompletionChoiceMessage"
}
@@ -10818,14 +10819,18 @@
"httpapi.ChatCompletionChoiceMessage": {
"type": "object",
"properties": {
"content": {
"type": "string",
"example": "Hello"
"annotations": {},
"audio": {},
"content": {},
"function_call": {},
"refusal": {
"type": "string"
},
"role": {
"type": "string",
"example": "assistant"
}
},
"tool_calls": {}
}
},
"httpapi.ChatCompletionCompatibleResponse": {
@@ -10853,6 +10858,12 @@
"type": "string",
"example": "chat.completion"
},
"service_tier": {
"type": "string"
},
"system_fingerprint": {
"type": "string"
},
"usage": {
"$ref": "#/definitions/httpapi.ChatCompletionUsage"
}
@@ -11045,11 +11056,15 @@
"httpapi.ChatMessage": {
"type": "object",
"properties": {
"audio": {},
"content": {},
"function_call": {},
"name": {
"type": "string"
},
"refusal": {
"type": "string"
},
"role": {
"type": "string",
"example": "user"
@@ -12312,18 +12327,35 @@
"httpapi.ResponsesCompatibleResponse": {
"type": "object",
"properties": {
"background": {
"type": "boolean"
},
"completed_at": {
"type": "integer"
},
"conversation": {},
"created_at": {
"type": "integer",
"example": 1710000000
},
"error": {},
"id": {
"type": "string",
"example": "resp_0123456789abcdef0123456789abcdef"
},
"incomplete_details": {},
"instructions": {},
"max_output_tokens": {},
"max_tool_calls": {},
"metadata": {
"type": "object",
"additionalProperties": true
},
"model": {
"type": "string",
"example": "Doubao Seed 2.0 Pro"
},
"moderation": {},
"object": {
"type": "string",
"example": "response"
@@ -12339,18 +12371,47 @@
"type": "string",
"example": "Hello"
},
"previous_response_id": {
"type": "string",
"example": "resp_abcdef0123456789abcdef0123456789"
"parallel_tool_calls": {
"type": "boolean"
},
"previous_response_id": {},
"prompt": {},
"prompt_cache_key": {},
"prompt_cache_options": {},
"prompt_cache_retention": {},
"reasoning": {},
"safety_identifier": {},
"service_tier": {},
"status": {
"type": "string",
"enum": [
"queued",
"in_progress",
"completed",
"incomplete",
"failed",
"cancelled"
],
"example": "completed"
},
"temperature": {},
"text": {},
"tool_choice": {},
"tools": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
},
"top_logprobs": {},
"top_p": {},
"truncation": {},
"usage": {
"type": "object",
"additionalProperties": true
}
},
"user": {}
}
},
"httpapi.ResponsesRequest": {
@@ -12359,13 +12420,7 @@
"background": {
"type": "boolean"
},
"context_management": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
},
"context_management": {},
"conversation": {},
"include": {
"type": "array",