- 新增 ConversionEngine 核心引擎,支持 OpenAI 和 Anthropic 协议转换 - 添加 stream decoder/encoder 实现 - 更新 provider client 支持新引擎 - 补充单元测试和集成测试 - 更新 specs 文档
1727 lines
38 KiB
Markdown
1727 lines
38 KiB
Markdown
# OpenAI Chat Completion API 端到端测试用例与 Mock 设计
|
||
|
||
本文档针对 `POST /chat/completions` 接口,设计端到端测试用例及对应的 Mock 返回值结构。
|
||
|
||
---
|
||
|
||
## 一、测试场景总览
|
||
|
||
| 分类 | 测试用例 | 优先级 |
|
||
|------|----------|--------|
|
||
| 基础对话 | 1. 单轮纯文本对话 | P0 |
|
||
| 基础对话 | 2. 多轮对话(system + user + assistant + user) | P0 |
|
||
| 消息类型 | 3. developer 消息(o1+ 模型) | P1 |
|
||
| 消息类型 | 4. 带 tool_calls 的 assistant 消息 + tool 消息 | P0 |
|
||
| 消息内容 | 5. 用户消息含图片 URL(vision) | P1 |
|
||
| 消息内容 | 6. 用户消息含多模态内容(text + image) | P1 |
|
||
| 消息内容 | 7. assistant 消息含 refusal | P1 |
|
||
| 工具调用 | 8. 单工具调用(function tool) | P0 |
|
||
| 工具调用 | 9. 并行多工具调用(parallel_tool_calls) | P1 |
|
||
| 工具调用 | 10. tool_choice 为 "none" | P2 |
|
||
| 工具调用 | 11. tool_choice 指定具体工具名 | P2 |
|
||
| 工具调用 | 12. 废弃的 function_call 格式(向后兼容) | P2 |
|
||
| 参数控制 | 13. temperature + top_p | P1 |
|
||
| 参数控制 | 14. max_tokens 截断 | P1 |
|
||
| 参数控制 | 15. stop 序列截断 | P2 |
|
||
| 参数控制 | 16. frequency_penalty + presence_penalty | P2 |
|
||
| 响应格式 | 17. response_format 为 json_object | P1 |
|
||
| 响应格式 | 18. response_format 为 json_schema(structured output) | P1 |
|
||
| 流式响应 | 19. 流式文本响应(SSE) | P0 |
|
||
| 流式响应 | 20. 流式 + stream_options include_usage | P1 |
|
||
| 流式响应 | 21. 流式工具调用 | P1 |
|
||
| 推理模型 | 22. reasoning_effort 参数(o1/o3 模型) | P2 |
|
||
| 错误处理 | 23. 无效 model 返回 404 | P1 |
|
||
| 错误处理 | 24. 缺少 messages 返回 400 | P1 |
|
||
| 错误处理 | 25. 内容安全策略拒绝(content_filter) | P2 |
|
||
| 其他 | 26. n 参数多选择(multiple choices) | P2 |
|
||
| 其他 | 27. seed 参数可复现 | P2 |
|
||
| 其他 | 28. logprobs + top_logprobs | P2 |
|
||
|
||
---
|
||
|
||
## 二、测试用例详情
|
||
|
||
### 用例 1:单轮纯文本对话
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "你好,请介绍一下你自己"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-001",
|
||
"object": "chat.completion",
|
||
"created": 1700000000,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "你好!我是由 OpenAI 开发的 AI 助手 GPT-4。我可以帮你回答问题、写作、编程、数学推理等任务。请问有什么我可以帮你的?"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 15,
|
||
"completion_tokens": 42,
|
||
"total_tokens": 57
|
||
},
|
||
"system_fingerprint": "fp_test123"
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `choices[0].message.role` == `"assistant"`
|
||
- `choices[0].message.content` 非空
|
||
- `choices[0].finish_reason` == `"stop"`
|
||
- `usage` 字段完整
|
||
|
||
---
|
||
|
||
### 用例 2:多轮对话
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "system",
|
||
"content": "你是一个专业的编程助手,请用简洁的语言回答问题。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "什么是 Go 语言的 interface?"
|
||
},
|
||
{
|
||
"role": "assistant",
|
||
"content": "Go 语言的 interface 是一种类型,它定义了一组方法签名。任何实现了这些方法的类型都自动实现了该 interface,无需显式声明。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "能举个例子吗?"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-002",
|
||
"object": "chat.completion",
|
||
"created": 1700000001,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "当然。例如定义一个 `Reader` interface:\n\n```go\ntype Reader interface {\n Read(p []byte) (n int, err error)\n}\n```\n\n`os.File` 和 `bytes.Buffer` 都实现了 `Read` 方法,所以它们都自动实现了 `Reader` interface。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 120,
|
||
"completion_tokens": 65,
|
||
"total_tokens": 185
|
||
},
|
||
"system_fingerprint": "fp_test123"
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 多轮上下文正确传递
|
||
- 响应与上下文连贯
|
||
|
||
---
|
||
|
||
### 用例 3:developer 消息(o1+ 模型)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "o1",
|
||
"messages": [
|
||
{
|
||
"role": "developer",
|
||
"content": "你是一名数学专家。请逐步推理后再给出答案。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "15 + 23 * 2 等于多少?"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-003",
|
||
"object": "chat.completion",
|
||
"created": 1700000002,
|
||
"model": "o1",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "根据运算优先级,先算乘法:23 * 2 = 46,再加 15:46 + 15 = 61。答案是 61。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 35,
|
||
"completion_tokens": 48,
|
||
"total_tokens": 83,
|
||
"completion_tokens_details": {
|
||
"reasoning_tokens": 20
|
||
}
|
||
},
|
||
"system_fingerprint": "fp_test123"
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- developer 角色被正确处理
|
||
- reasoning_tokens 在详情中体现
|
||
|
||
---
|
||
|
||
### 用例 4:工具调用 + 工具结果
|
||
|
||
**请求(第一轮 - 模型调用工具):**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "北京今天天气怎么样?"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取指定城市的天气信息",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {
|
||
"type": "string",
|
||
"description": "城市名称"
|
||
}
|
||
},
|
||
"required": ["city"]
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"tool_choice": "auto"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK,第一轮):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-004a",
|
||
"object": "chat.completion",
|
||
"created": 1700000003,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": null,
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_abc123",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"北京\"}"
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"finish_reason": "tool_calls",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 80,
|
||
"completion_tokens": 18,
|
||
"total_tokens": 98
|
||
}
|
||
}
|
||
```
|
||
|
||
**请求(第二轮 - 提交工具结果):**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "北京今天天气怎么样?"
|
||
},
|
||
{
|
||
"role": "assistant",
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_abc123",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"北京\"}"
|
||
}
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"role": "tool",
|
||
"tool_call_id": "call_abc123",
|
||
"content": "北京今天晴,气温 25°C,东南风 2 级。"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取指定城市的天气信息",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {
|
||
"type": "string",
|
||
"description": "城市名称"
|
||
}
|
||
},
|
||
"required": ["city"]
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK,第二轮):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-004b",
|
||
"object": "chat.completion",
|
||
"created": 1700000004,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "北京今天天气很好,晴天,气温 25°C,东南风 2 级,适合外出活动。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 120,
|
||
"completion_tokens": 30,
|
||
"total_tokens": 150
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 第一轮 `finish_reason` == `"tool_calls"`
|
||
- `tool_calls` 包含正确的 function name 和 arguments
|
||
- 第二轮能基于工具结果生成自然语言回复
|
||
|
||
---
|
||
|
||
### 用例 5:用户消息含图片 URL(vision)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": [
|
||
{
|
||
"type": "text",
|
||
"text": "这张图片里有什么?"
|
||
},
|
||
{
|
||
"type": "image_url",
|
||
"image_url": {
|
||
"url": "https://example.com/images/cat.jpg",
|
||
"detail": "high"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-005",
|
||
"object": "chat.completion",
|
||
"created": 1700000005,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "图片中有一只橘色的猫,它正躺在沙发上晒太阳。猫的毛色是橘白相间的,眼睛是绿色的,看起来非常放松。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 280,
|
||
"completion_tokens": 45,
|
||
"total_tokens": 325
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 图片 URL 被正确传递
|
||
- 响应描述图片内容
|
||
|
||
---
|
||
|
||
### 用例 6:用户消息含多模态内容(text + image)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": [
|
||
{
|
||
"type": "text",
|
||
"text": "请比较这两张图片有什么不同"
|
||
},
|
||
{
|
||
"type": "image_url",
|
||
"image_url": {
|
||
"url": "https://example.com/images/before.jpg"
|
||
}
|
||
},
|
||
{
|
||
"type": "image_url",
|
||
"image_url": {
|
||
"url": "https://example.com/images/after.jpg"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-006",
|
||
"object": "chat.completion",
|
||
"created": 1700000006,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "两张图片的主要区别:\n1. 第一张是白天的场景,第二张是夜晚\n2. 灯光从自然光变成了暖黄色的室内灯光\n3. 桌上的物品摆放位置有所不同"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 520,
|
||
"completion_tokens": 55,
|
||
"total_tokens": 575
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 7:assistant 消息含 refusal
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "如何制作危险物品?"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-007",
|
||
"object": "chat.completion",
|
||
"created": 1700000007,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"refusal": "抱歉,我无法提供涉及危险活动的信息。我的设计目的是提供有益和安全的帮助。如果你有其他问题,我很乐意协助。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 12,
|
||
"completion_tokens": 35,
|
||
"total_tokens": 47
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `refusal` 字段存在且非空
|
||
- `content` 为 null 或空
|
||
|
||
---
|
||
|
||
### 用例 8:单工具调用(function tool)
|
||
|
||
同用例 4 第一轮。
|
||
|
||
---
|
||
|
||
### 用例 9:并行多工具调用
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "帮我查一下北京、上海、广州三个城市的天气"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取指定城市的天气信息",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {
|
||
"type": "string",
|
||
"description": "城市名称"
|
||
}
|
||
},
|
||
"required": ["city"]
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"tool_choice": "auto"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-009",
|
||
"object": "chat.completion",
|
||
"created": 1700000009,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": null,
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_001",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"北京\"}"
|
||
}
|
||
},
|
||
{
|
||
"id": "call_002",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"上海\"}"
|
||
}
|
||
},
|
||
{
|
||
"id": "call_003",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"广州\"}"
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"finish_reason": "tool_calls",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 95,
|
||
"completion_tokens": 55,
|
||
"total_tokens": 150
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `tool_calls` 数组长度 == 3
|
||
- 每个 call 有不同 ID 和参数
|
||
|
||
---
|
||
|
||
### 用例 10:tool_choice 为 "none"
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "随便聊聊"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取天气",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {"type": "string"}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"tool_choice": "none"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-010",
|
||
"object": "chat.completion",
|
||
"created": 1700000010,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "好的!今天天气不错,很适合聊天。你想聊些什么话题呢?可以是技术、生活、娱乐等任何你感兴趣的内容。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 50,
|
||
"completion_tokens": 35,
|
||
"total_tokens": 85
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 尽管定义了 tools,响应中不包含 `tool_calls`
|
||
- 纯文本回复
|
||
|
||
---
|
||
|
||
### 用例 11:tool_choice 指定具体工具名
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "查天气"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取天气",
|
||
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}}
|
||
}
|
||
},
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_news",
|
||
"description": "获取新闻",
|
||
"parameters": {"type": "object", "properties": {"topic": {"type": "string"}}}
|
||
}
|
||
}
|
||
],
|
||
"tool_choice": {
|
||
"type": "function",
|
||
"function": {"name": "get_weather"}
|
||
}
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-011",
|
||
"object": "chat.completion",
|
||
"created": 1700000011,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": null,
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_weather1",
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"arguments": "{}"
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"finish_reason": "tool_calls",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 80,
|
||
"completion_tokens": 15,
|
||
"total_tokens": 95
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 强制使用了 `get_weather` 而非 `get_news`
|
||
|
||
---
|
||
|
||
### 用例 12:废弃的 function_call 格式(向后兼容)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "查天气"
|
||
}
|
||
],
|
||
"functions": [
|
||
{
|
||
"name": "get_weather",
|
||
"description": "获取天气",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {"type": "string"}
|
||
},
|
||
"required": ["city"]
|
||
}
|
||
}
|
||
],
|
||
"function_call": "auto"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-012",
|
||
"object": "chat.completion",
|
||
"created": 1700000012,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": null,
|
||
"function_call": {
|
||
"name": "get_weather",
|
||
"arguments": "{\"city\": \"北京\"}"
|
||
}
|
||
},
|
||
"finish_reason": "function_call",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 60,
|
||
"completion_tokens": 20,
|
||
"total_tokens": 80
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 废弃的 `functions`/`function_call` 格式仍能被正确处理
|
||
- `finish_reason` == `"function_call"`(而非 `"tool_calls"`)
|
||
|
||
---
|
||
|
||
### 用例 13:temperature + top_p 参数
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "写一首关于春天的短诗"
|
||
}
|
||
],
|
||
"temperature": 0.9,
|
||
"top_p": 0.95
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-013",
|
||
"object": "chat.completion",
|
||
"created": 1700000013,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "春风拂面花自开,\n柳絮飞舞满园香。\n燕归巢中呢喃语,\n万物复苏迎朝阳。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 18,
|
||
"completion_tokens": 32,
|
||
"total_tokens": 50
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 14:max_tokens 截断
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "请详细介绍一下人工智能的发展历史"
|
||
}
|
||
],
|
||
"max_tokens": 30
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-014",
|
||
"object": "chat.completion",
|
||
"created": 1700000014,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "人工智能起源于1950年代,图灵提出了机器能否思考的问题。1956年达特茅斯会议正式确立了AI领域。此后经历了多次起伏..."
|
||
},
|
||
"finish_reason": "length",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 20,
|
||
"completion_tokens": 30,
|
||
"total_tokens": 50
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `finish_reason` == `"length"`
|
||
- `completion_tokens` == 30(等于 max_tokens)
|
||
- 内容被截断
|
||
|
||
---
|
||
|
||
### 用例 15:stop 序列截断
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "从1数到10,每行一个数字"
|
||
}
|
||
],
|
||
"stop": ["5"]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-015",
|
||
"object": "chat.completion",
|
||
"created": 1700000015,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "1\n2\n3\n4\n"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 18,
|
||
"completion_tokens": 10,
|
||
"total_tokens": 28
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `finish_reason` == `"stop"`
|
||
- 内容在遇到 stop 序列时截断
|
||
|
||
---
|
||
|
||
### 用例 16:frequency_penalty + presence_penalty
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "用不同的词汇描述'好'"
|
||
}
|
||
],
|
||
"frequency_penalty": 0.5,
|
||
"presence_penalty": 0.3
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-016",
|
||
"object": "chat.completion",
|
||
"created": 1700000016,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "优秀、出色、精彩、卓越、非凡、完美、绝佳、美妙、精彩、杰出"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 18,
|
||
"completion_tokens": 15,
|
||
"total_tokens": 33
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 17:response_format 为 json_object
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "提取以下信息的姓名和年龄:张三,今年25岁,是一名工程师"
|
||
}
|
||
],
|
||
"response_format": {
|
||
"type": "json_object"
|
||
}
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-017",
|
||
"object": "chat.completion",
|
||
"created": 1700000017,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "{\"name\": \"张三\", \"age\": 25, \"occupation\": \"工程师\"}"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 35,
|
||
"completion_tokens": 25,
|
||
"total_tokens": 60
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `content` 是合法的 JSON 字符串
|
||
|
||
---
|
||
|
||
### 用例 18:response_format 为 json_schema(structured output)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "创建一个用户信息记录,姓名李四,年龄30岁"
|
||
}
|
||
],
|
||
"response_format": {
|
||
"type": "json_schema",
|
||
"json_schema": {
|
||
"name": "user_info",
|
||
"schema": {
|
||
"type": "object",
|
||
"properties": {
|
||
"name": {"type": "string"},
|
||
"age": {"type": "integer"},
|
||
"email": {"type": "string"}
|
||
},
|
||
"required": ["name", "age"]
|
||
},
|
||
"strict": true
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-018",
|
||
"object": "chat.completion",
|
||
"created": 1700000018,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "{\"name\": \"李四\", \"age\": 30}"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 85,
|
||
"completion_tokens": 18,
|
||
"total_tokens": 103
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 输出严格符合 JSON schema 定义
|
||
- 不包含 schema 未定义的字段
|
||
|
||
---
|
||
|
||
### 用例 19:流式文本响应(SSE)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "你好"
|
||
}
|
||
],
|
||
"stream": true
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK, Content-Type: text/event-stream):**
|
||
|
||
```
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"好"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"!我"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"是"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"AI"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"助手"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":","},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"很高兴"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"为你"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"服务"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"。"},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-001","object":"chat.completion.chunk","created":1700000019,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
**验证点:**
|
||
- 每个 chunk 的 `id` 一致
|
||
- 第一个 chunk 的 `delta` 包含 `role`
|
||
- 中间 chunk 的 `delta` 只包含 `content`
|
||
- 最后一个 chunk 包含 `finish_reason`
|
||
- 以 `[DONE]` 结束
|
||
|
||
---
|
||
|
||
### 用例 20:流式 + stream_options include_usage
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "你好"
|
||
}
|
||
],
|
||
"stream": true,
|
||
"stream_options": {
|
||
"include_usage": true
|
||
}
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK, Content-Type: text/event-stream):**
|
||
|
||
最后一个 chunk 之前增加一个 usage chunk:
|
||
|
||
```
|
||
data: {"id":"chatcmpl-stream-020","object":"chat.completion.chunk","created":1700000020,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
|
||
|
||
... (中间内容 chunk) ...
|
||
|
||
data: {"id":"chatcmpl-stream-020","object":"chat.completion.chunk","created":1700000020,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
|
||
|
||
data: {"id":"chatcmpl-stream-020","object":"chat.completion.chunk","created":1700000020,"model":"gpt-4o","choices":[],"usage":{"prompt_tokens":10,"completion_tokens":25,"total_tokens":35}}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
**验证点:**
|
||
- 倒数第二个 chunk 包含 `usage` 字段
|
||
- 该 chunk 的 `choices` 为空数组
|
||
|
||
---
|
||
|
||
### 用例 21:流式工具调用
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "北京天气怎么样?"
|
||
}
|
||
],
|
||
"tools": [
|
||
{
|
||
"type": "function",
|
||
"function": {
|
||
"name": "get_weather",
|
||
"description": "获取天气",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"city": {"type": "string"}
|
||
},
|
||
"required": ["city"]
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"stream": true
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK, Content-Type: text/event-stream):**
|
||
|
||
```
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":null},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_stream1","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"北京"}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}\"}"}}]},"finish_reason":null}]}
|
||
|
||
data: {"id":"chatcmpl-stream-021","object":"chat.completion.chunk","created":1700000021,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
**验证点:**
|
||
- 第一个 chunk 包含 `role`
|
||
- tool_calls 通过增量 chunk 逐步拼接
|
||
- 最后一个 chunk 的 `finish_reason` == `"tool_calls"`
|
||
|
||
---
|
||
|
||
### 用例 22:reasoning_effort 参数(o1/o3 模型)
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "o3",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "一个房间里有3个灯泡,房间外有3个开关,每个开关控制一个灯泡。你只能进房间一次,如何确定哪个开关控制哪个灯泡?"
|
||
}
|
||
],
|
||
"reasoning_effort": "high"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-022",
|
||
"object": "chat.completion",
|
||
"created": 1700000022,
|
||
"model": "o3",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "解决方案:\n\n1. 打开第一个开关,等待几分钟\n2. 关闭第一个开关,打开第二个开关\n3. 立即进入房间\n\n此时:\n- 亮着的灯由第二个开关控制\n- 摸起来发热的灯由第一个开关控制(因为开过几分钟)\n- 既不亮也不热的灯由第三个开关控制"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 85,
|
||
"completion_tokens": 120,
|
||
"total_tokens": 205,
|
||
"completion_tokens_details": {
|
||
"reasoning_tokens": 80
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- reasoning_tokens 显著增加
|
||
- 响应展示推理结果
|
||
|
||
---
|
||
|
||
### 用例 23:无效 model 返回 404
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "nonexistent-model-xyz",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "你好"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(404 Not Found):**
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "The model `nonexistent-model-xyz` does not exist or you do not have access to it.",
|
||
"type": "invalid_request_error",
|
||
"param": null,
|
||
"code": "model_not_found"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 24:缺少 messages 返回 400
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o"
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(400 Bad Request):**
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "'messages' is a required property and must be a non-empty array.",
|
||
"type": "invalid_request_error",
|
||
"param": null,
|
||
"code": "missing_required_field"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 25:内容安全策略拒绝
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "生成违法内容..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(400 Bad Request):**
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "The content you submitted may violate our usage policies.",
|
||
"type": "invalid_request_error",
|
||
"param": null,
|
||
"code": "content_filter"
|
||
}
|
||
}
|
||
```
|
||
|
||
或者作为 completion 返回(finish_reason = "content_filter"):
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-025",
|
||
"object": "chat.completion",
|
||
"created": 1700000025,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": null,
|
||
"refusal": "我无法生成此类内容。"
|
||
},
|
||
"finish_reason": "content_filter",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 15,
|
||
"completion_tokens": 8,
|
||
"total_tokens": 23
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 用例 26:n 参数多选择
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "用一句话描述春天"
|
||
}
|
||
],
|
||
"n": 3
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-026",
|
||
"object": "chat.completion",
|
||
"created": 1700000026,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "春天是万物复苏、百花盛开的季节。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
},
|
||
{
|
||
"index": 1,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "春风拂面,绿意盎然,处处洋溢着生机与希望。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
},
|
||
{
|
||
"index": 2,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "冬雪消融,嫩芽破土,春天带着温暖悄然而至。"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 18,
|
||
"completion_tokens": 60,
|
||
"total_tokens": 78
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `choices` 数组长度为 3
|
||
- 每个 choice 有不同的 `index`
|
||
|
||
---
|
||
|
||
### 用例 27:seed 参数可复现
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "随机生成一个5位数字"
|
||
}
|
||
],
|
||
"seed": 42,
|
||
"temperature": 0
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-027",
|
||
"object": "chat.completion",
|
||
"created": 1700000027,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "12345"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": null
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 18,
|
||
"completion_tokens": 5,
|
||
"total_tokens": 23
|
||
},
|
||
"system_fingerprint": "fp_test_seed42"
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- 相同 seed + temperature=0 应产生相同输出
|
||
- `system_fingerprint` 可用于验证 backend 一致性
|
||
|
||
---
|
||
|
||
### 用例 28:logprobs + top_logprobs
|
||
|
||
**请求:**
|
||
```json
|
||
{
|
||
"model": "gpt-4o",
|
||
"messages": [
|
||
{
|
||
"role": "user",
|
||
"content": "1+1="
|
||
}
|
||
],
|
||
"logprobs": true,
|
||
"top_logprobs": 3
|
||
}
|
||
```
|
||
|
||
**期望 Mock 响应(200 OK):**
|
||
```json
|
||
{
|
||
"id": "chatcmpl-test-028",
|
||
"object": "chat.completion",
|
||
"created": 1700000028,
|
||
"model": "gpt-4o",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "2"
|
||
},
|
||
"finish_reason": "stop",
|
||
"logprobs": {
|
||
"content": [
|
||
{
|
||
"token": "2",
|
||
"logprob": -0.001,
|
||
"bytes": [50],
|
||
"top_logprobs": [
|
||
{"token": "2", "logprob": -0.001, "bytes": [50]},
|
||
{"token": "二", "logprob": -3.5, "bytes": [201, 147]},
|
||
{"token": " two", "logprob": -5.2, "bytes": [32, 116, 119, 111]}
|
||
]
|
||
}
|
||
],
|
||
"refusal": null
|
||
}
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 10,
|
||
"completion_tokens": 1,
|
||
"total_tokens": 11
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证点:**
|
||
- `logprobs.content` 数组包含每个生成 token 的概率信息
|
||
- `top_logprobs` 包含前 K 个候选 token
|
||
|
||
---
|
||
|
||
## 三、Mock 响应通用结构规范
|
||
|
||
### 非流式响应通用结构
|
||
|
||
```json
|
||
{
|
||
"id": "chatcmpl-<unique_id>",
|
||
"object": "chat.completion",
|
||
"created": <unix_timestamp>,
|
||
"model": "<model_name>",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "<text>" | null,
|
||
"refusal": "<text>" | null,
|
||
"tool_calls": [
|
||
{
|
||
"id": "call_<id>",
|
||
"type": "function" | "custom",
|
||
"function": {"name": "<name>", "arguments": "<json_string>"} | null,
|
||
"custom": {"name": "<name>", "input": "<json_string>"} | null
|
||
}
|
||
]
|
||
},
|
||
"finish_reason": "stop" | "length" | "tool_calls" | "content_filter" | "function_call",
|
||
"logprobs": null | { "content": [...], "refusal": null }
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": <int>,
|
||
"completion_tokens": <int>,
|
||
"total_tokens": <int>,
|
||
"prompt_tokens_details": {
|
||
"cached_tokens": <int> | null,
|
||
"audio_tokens": <int> | null
|
||
},
|
||
"completion_tokens_details": {
|
||
"reasoning_tokens": <int> | null,
|
||
"audio_tokens": <int> | null,
|
||
"accepted_prediction_tokens": <int> | null,
|
||
"rejected_prediction_tokens": <int> | null
|
||
}
|
||
},
|
||
"system_fingerprint": "<fingerprint>" | null,
|
||
"service_tier": "auto" | "default" | "flex" | null
|
||
}
|
||
```
|
||
|
||
### 流式 Chunk 通用结构
|
||
|
||
```json
|
||
{
|
||
"id": "chatcmpl-<unique_id>",
|
||
"object": "chat.completion.chunk",
|
||
"created": <unix_timestamp>,
|
||
"model": "<model_name>",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"delta": {
|
||
"role": "assistant" | null,
|
||
"content": "<text_chunk>" | null,
|
||
"refusal": "<text_chunk>" | null,
|
||
"tool_calls": [
|
||
{
|
||
"index": <int>,
|
||
"id": "call_<id>" | null,
|
||
"type": "function" | "custom" | null,
|
||
"function": {"name": "<name>" | null, "arguments": "<partial_json>"} | null,
|
||
"custom": {"name": "<name>" | null, "input": "<partial_json>"} | null
|
||
}
|
||
]
|
||
},
|
||
"finish_reason": "stop" | "length" | "tool_calls" | "content_filter" | null,
|
||
"logprobs": null | { ... }
|
||
}
|
||
],
|
||
"usage": { ... } | null,
|
||
"system_fingerprint": "<fingerprint>" | null
|
||
}
|
||
```
|
||
|
||
### 错误响应通用结构
|
||
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "<human_readable_message>",
|
||
"type": "invalid_request_error" | "authentication_error" | "api_error" | "rate_limit_error",
|
||
"param": null | "<parameter_name>",
|
||
"code": "<error_code>"
|
||
}
|
||
}
|
||
```
|
||
|