Chat Completions 格式
根据对话历史生成模型回答,支持流式和非流式响应、图片输入和工具调用。兼容 OpenAI Chat Completions API:OpenAI 官方 SDK 和大多数兼容 OpenAI 的应用把 Base URL 改成 https://api.lyrouter.com/v1 即可使用。同一个模型可能由 OpenAI 兼容或 Anthropic 协议的通道服务,网关会自动转换请求和响应,本页同时说明转换时哪些参数会变化。
/v1/chat/completions鉴权
在请求头中携带 API Key:
Authorization: Bearer <API Key>
Bearer 区分大小写。也可以改用 x-api-key: <API Key>,两者同时存在时只读 Authorization。API Key 在工作空间的 API 密钥 页面创建,只用于 LYRouter 鉴权,不会转发给上游服务商。完整说明见接入地址与鉴权。
请求头
| 请求头 | 必填 | 说明 |
|---|---|---|
Content-Type |
否 | 建议携带 application/json。不带时网关按 JSON 解析请求体;写成其他值(如 application/x-www-form-urlencoded)时返回 400 |
请求体
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | 否 | 模型 ID,例如模型广场中复制的系统模型标识;也可以写虚拟模型 @vmodel/<名称> 或 lyrouter/auto。不写、写成 "" 或 null 时使用工作空间的默认模型,没有默认模型时返回 400。见模型命名与查询 |
messages |
array | 是 | 对话消息,结构见下文 |
stream |
boolean | 否 | true 时以 SSE 流式返回,默认 false |
stream_options |
object | 否 | 流式时网关总是设为 {"include_usage": true},最后一个 chunk 带 usage,你传的值会被覆盖 |
max_tokens |
integer | 否 | 本次最多生成的 Token 数。Anthropic 协议通道要求必填,请求中没有 max_tokens 和 max_completion_tokens 时网关补 4096 |
max_completion_tokens |
integer | 否 | OpenAI 新版的输出上限字段,透传给 OpenAI 兼容通道;发往 Anthropic 协议通道时在没有 max_tokens 的情况下作为 max_tokens 使用 |
temperature |
number | 否 | 采样温度,取值范围以模型为准(Anthropic 协议通道为 0~1),网关不做换算 |
top_p |
number | 否 | 核采样 |
stop |
string | array | 否 | 停止序列;发往 Anthropic 协议通道时转换为 stop_sequences |
tools |
array | 否 | 可调用的函数列表,见工具调用 |
tool_choice |
string | object | 否 | "auto"、"none"、"required" 或 {"type":"function","function":{"name":"..."}},各协议的对应关系见工具调用 |
parallel_tool_calls |
boolean | 否 | 是否允许一次返回多个工具调用;发往 Anthropic 协议通道时会被去掉 |
response_format |
object | 否 | {"type":"json_object"} 或 {"type":"json_schema",...};只对 OpenAI 兼容通道生效,发往 Anthropic 协议通道时会被去掉 |
reasoning_effort |
string | 否 | 推理强度,网关按服务商处理,见下文「推理参数」 |
enable_thinking / thinking_budget |
boolean / integer | 否 | 思考开关与思考预算,网关按服务商处理,见下文「推理参数」 |
seed |
integer | 否 | 随机种子;透传给 OpenAI 兼容通道,是否生效取决于模型;发往 Anthropic 协议通道时会被去掉 |
user |
string | 否 | 终端用户标识,透传给上游,是否生效取决于模型 |
frequency_penalty / presence_penalty / logprobs / n |
— | 否 | 透传给 OpenAI 兼容通道,是否生效取决于模型;发往 Anthropic 协议通道时会被去掉 |
表中没有列出的其他参数,网关原样转发给 OpenAI 兼容通道,是否生效取决于模型。
messages 的结构
每条消息包含 role 和 content:
role |
用途 | 说明 |
|---|---|---|
system |
系统提示词 | 放在最前面。发往 Anthropic 协议通道时,所有 system 和 developer 消息合并为 Anthropic 的 system,content 请写成字符串 |
developer |
新版 OpenAI 的系统提示词 | 透传给 OpenAI 兼容通道,是否支持取决于模型;Anthropic 协议通道按 system 处理 |
user |
用户输入 | content 可以是字符串或内容块数组 |
assistant |
模型之前的回答 | 多轮对话时原样放回;包含工具调用时带 tool_calls |
tool |
工具执行结果 | 必须带 tool_call_id,对应上一条 assistant 消息中的调用 ID |
content 有两种写法。纯文本直接写字符串;需要图片时写成内容块数组:
{
"role": "user",
"content": [
{ "type": "text", "text": "这张图里有什么?" },
{ "type": "image_url", "image_url": { "url": "https://example.com/cat.jpg" } }
]
}
image_url.url可以是公网可访问的https://地址,也可以是data:image/png;base64,<数据>形式的 Base64 数据。- 模型必须支持图像输入,只接受文本的模型返回 400
This model accepts text input only。在模型广场用「输入模态」筛选可用模型,详见多模态能力。 - Anthropic 协议通道只转换文本块和图片块;
input_audio、file等其他内容块只对 OpenAI 兼容通道有效。
推理参数
三个推理参数分属不同服务商的写法,网关按实际服务这次请求的服务商保留或去掉,不会把其中一个换算成另一个(发往 Anthropic 协议通道时转换成 thinking,见表中说明):
| 实际服务的上游 | reasoning_effort |
enable_thinking |
thinking_budget |
|---|---|---|---|
| OpenAI 官方接口 | 转发 | 去掉 | 去掉 |
| 阿里云百炼(DashScope 兼容接口) | 去掉 | 转发 | 转发 |
| DeepSeek 官方接口 | 去掉 | 去掉 | 去掉(是否思考由所选模型决定) |
| Anthropic 协议通道 | 去掉 | true 且没有预算时转为 thinking: {"type": "adaptive"},false 时不开启思考 |
大于 0 时转为 thinking: {"type": "enabled", "budget_tokens": N} |
| 其他 OpenAI 兼容服务商 | 原样转发 | 原样转发 | 原样转发 |
- 实际服务的服务商在请求日志详情的「服务商」中查看。同一模型有多个来源时,请对每个来源验证推理参数是否生效。
- Anthropic 协议通道开启思考时,上游要求
max_tokens大于budget_tokens,网关不调整这些参数,不满足时由上游返回 400。 - 强制工具调用(
tool_choice为"required"或指定函数)且没有写enable_thinking/thinking_budget时,阿里云百炼通道会被自动加上enable_thinking: false,因为这类上游在思考模式下不接受强制工具调用。 - OpenAI Python SDK 没有定义
enable_thinking、thinking_budget,请放在extra_body中传递,例如extra_body={"enable_thinking": False};Node.js SDK 直接写在请求参数里即可。
请求示例
curl https://api.lyrouter.com/v1/chat/completions \
-H "Authorization: Bearer $LYROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"messages": [
{"role": "system", "content": "你是一个简洁的助手。"},
{"role": "user", "content": "用一句话介绍 LYRouter。"}
],
"temperature": 0.7,
"max_tokens": 512
}'
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com/v1",
)
completion = client.chat.completions.create(
model="MODEL_ID",
messages=[
{"role": "system", "content": "你是一个简洁的助手。"},
{"role": "user", "content": "用一句话介绍 LYRouter。"},
],
temperature=0.7,
max_tokens=512,
)
print(completion.choices[0].message.content)
print(completion.usage)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com/v1",
});
const completion = await client.chat.completions.create({
model: "MODEL_ID",
messages: [
{ role: "system", content: "你是一个简洁的助手。" },
{ role: "user", content: "用一句话介绍 LYRouter。" },
],
temperature: 0.7,
max_tokens: 512,
});
console.log(completion.choices[0].message.content);
console.log(completion.usage);
响应
非流式请求返回一个 chat.completion 对象;出错时返回 error 对象。下面的 200 是 OpenAI 兼容通道的典型响应,400 是没有写 model 且工作空间没有默认模型,429 是 API Key 当天的 Token 配额已用完(见429:限流与配额)。
{
"id": "chatcmpl-9f1c2a7e4b",
"object": "chat.completion",
"created": 1791950400,
"model": "MODEL_ID",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "LYRouter 是可信模型服务与 Token 治理平台,用一个 API Key 统一调用多家模型服务商。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1250,
"completion_tokens": 32,
"total_tokens": 1282,
"prompt_tokens_details": { "cached_tokens": 1024 },
"completion_tokens_details": { "reasoning_tokens": 0 }
}
}
{
"error": {
"message": "you must provide a model parameter",
"type": "invalid_request_error",
"code": "missing_model"
}
}
{
"error": {
"message": "rate limit exceeded on apikey total_tokens/day",
"type": "rate_limit_error",
"subject": "apikey",
"metric": "total_tokens",
"window": "day",
"limit": 1000000,
"used": 1000523,
"reset_at": "2026-10-14T16:01:00Z"
}
}
| 字段 | 说明 |
|---|---|
id |
上游生成的响应 ID,不是 LYRouter 的 Request ID。网关不在响应中返回 Request ID,排查时在日志详情顶部复制 |
model |
上游返回的模型名,可能与请求中的模型 ID 写法不同;实际使用的模型以日志的「模型(实际)」为准 |
choices[0].message.content |
回答正文;只返回工具调用时可能为 null 或空字符串 |
choices[0].message.tool_calls |
模型请求调用的工具,见工具调用 |
choices[0].message.reasoning_content |
部分模型返回的思考过程;由 Anthropic 协议通道服务且开启思考时同样出现在这里 |
choices[0].message.reasoning_blocks |
LYRouter 扩展字段,只在 Anthropic 协议通道开启思考时出现,保存带签名的原始思考块。多轮工具调用时把 assistant 消息原样放回,网关才能把思考块交还上游 |
choices[0].finish_reason |
stop(正常结束)、length(达到输出上限)、tool_calls(等待工具结果)、content_filter(被上游过滤)等。Anthropic 协议通道的 end_turn、stop_sequence 映射为 stop,max_tokens 映射为 length,tool_use 映射为 tool_calls |
usage |
本次的 Token 用量,由上游上报,见下表 |
usage 与日志中的四项 Token
usage 的字段取决于实际服务的上游。它与请求日志「Token 消耗」四项的对应关系如下:
| 日志中的名称 | OpenAI 兼容通道 | Anthropic 协议通道 |
|---|---|---|
| 「输入(缓存命中)」 | prompt_tokens_details.cached_tokens(DeepSeek 等上游为 prompt_cache_hit_tokens) |
cache_read_input_tokens |
| 「输入(缓存未命中)」 | prompt_tokens 减去缓存命中 |
prompt_tokens |
| 「输出」 | completion_tokens,包含思考 Token(completion_tokens_details.reasoning_tokens) |
completion_tokens |
| 「显式缓存创建」 | 不出现 | cache_creation_input_tokens |
由 Anthropic 协议通道服务时,响应中的 usage 是这样的,prompt_tokens 不包含缓存命中和缓存写入:
"usage": {
"prompt_tokens": 226,
"completion_tokens": 32,
"total_tokens": 258,
"cache_read_input_tokens": 1024,
"cache_creation_input_tokens": 0
}
自己按 usage 估算费用时,先确认是哪种上游,再按四项分别计价,方法见调用费用与缓存计费。核对时以日志为准。
流式响应
设置 "stream": true 后,响应的 Content-Type 为 text/event-stream,每个事件是一行 data: <JSON> 加一个空行:
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"LYRouter 是"},"finish_reason":null}]}
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"可信模型服务与 Token 治理平台。"},"finish_reason":null}]}
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[],"usage":{"prompt_tokens":1250,"completion_tokens":32,"total_tokens":1282,"prompt_tokens_details":{"cached_tokens":1024}}}
data: [DONE]
- 正文在
choices[0].delta.content中逐段到达,思考过程在delta.reasoning_content(个别上游为delta.reasoning)。 - 网关总会开启
include_usage,所以结束前有一个choices为空数组、带usage的 chunk。读取choices[0]之前先判断数组非空。 data: [DONE]是正常结束的标记。没有收到它就断开,或收到了"code": "stream_interrupted"的错误事件,说明输出不完整,见超时与流式中断。- 由 Anthropic 协议通道服务时,chunk 由网关转换生成,带
usage的 chunk 只有prompt_tokens、completion_tokens、total_tokens三个字段,缓存数量请在日志中查看。
curl -N https://api.lyrouter.com/v1/chat/completions \
-H "Authorization: Bearer $LYROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID","stream":true,"messages":[{"role":"user","content":"写两行关于秋天的诗"}]}'
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com/v1",
)
stream = client.chat.completions.create(
model="MODEL_ID",
messages=[{"role": "user", "content": "写两行关于秋天的诗"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com/v1",
});
const stream = await client.chat.completions.create({
model: "MODEL_ID",
messages: [{ role: "user", content: "写两行关于秋天的诗" }],
stream: true,
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content;
if (text) process.stdout.write(text);
if (chunk.usage) console.log("\n", chunk.usage);
}
三种对话接口的事件格式对比和客户端实现要点见流式响应。
网关行为
与直接调用上游相比,经 LYRouter 调用有以下差别:
- 选择通道与回退:网关按工作空间的路由策略为模型选择通道,失败时换下一个通道,最后可能改用工作空间的默认模型,见失败重试与备用模型和通道选择与路由策略。流式输出开始后不再换通道。
- 协议转换:通道是 Anthropic 协议时,网关把请求转换成 Anthropic Messages 格式,响应再转换回 Chat Completions 格式。
response_format、seed、n、logprobs、top_logprobs、frequency_penalty、presence_penalty、logit_bias、parallel_tool_calls、reasoning_effort会被去掉,metadata只保留user_id;这些去掉不会写进响应头。对比见模型能力与兼容性。 - 推理参数:按上文「推理参数」的表格保留、去掉或转换。
- 提示词缓存标记:
cache_control只在 Anthropic 协议通道上生效,保留文本内容块和工具定义上的标记,"ttl": "1h"时网关把anthropic-beta请求头设为 1 小时缓存所需的值(覆盖客户端自带的值);发往 OpenAI 兼容通道时全部删除,由上游的隐式缓存工作。 - 历史消息中的扩展字段:发往 OpenAI 兼容通道前,网关删除消息上的
reasoning_content、reasoning_blocks和cache_control。 - 固定 JSON 键序:网关把发往上游的请求体中所有 JSON 对象的键按固定顺序重新排列,包括工具调用参数字符串里的 JSON,使相同内容每次产生相同的字节,提高上游前缀缓存的命中率,见降低成本与优化缓存。
- 数字精度:网关会解析并重新编码请求体,数字最多保留 14 位有效数字。超过的长整数(如 19 位 ID)会变成科学计数法,请以字符串传递。
- 用量:流式请求总会返回
usage;usage的字段随上游变化,见上文对照表。
常见错误
错误响应为 {"error":{"message":"...","type":"...","code":"..."}},code 只有部分错误才有。
| 状态码 | error.message |
error.code |
原因与处理 |
|---|---|---|---|
| 400 | you must provide a model parameter |
missing_model |
没有写 model,工作空间也没有默认模型。写上模型 ID。请求体不是合法的 JSON 对象时,部分版本也返回这个错误,见下一行 |
| 400 | request body must be a JSON object |
invalid_json |
请求体不是合法的 JSON 对象,例如 JSON 被截断或顶层是数组。部分版本返回的是 400 you must provide a model parameter(missing_model)。工作空间设置了默认模型时,返回的是 {"message":"could not get parse JSON request body: <解析错误>"},外层没有 error |
| 400 | This model accepts text input only |
unsupported_model_input |
给只接受文本的模型发了图片等非文本内容,换用支持图像输入的模型 |
| 400 | This model does not support the chat_completions API |
unsupported_model_api |
模型不支持对话接口,例如图像生成模型 |
| 403 | Model is not available in the model plaza |
model_not_available |
工作空间没有开通这个模型,请管理员在模型广场开通,或换用已开通的模型 |
| 503 | all routed candidates exhausted |
no_route_candidate |
找不到可服务这次请求的通道,常见于 @vmodel/<名称> 写错或模型没有可用来源,见模型不可用 |
- 401 / 403 鉴权问题见401 / 403:鉴权与权限,429 见429:限流与配额。
- 500 / 504 是连接上游失败或等待上游超时,响应体是 HTML 页面而不是 JSON,见超时与流式中断。
- 网关自己生成的部分 400 是一行纯文本而不是 JSON,例如
Content-Type不是application/json时的unsupported content-type: …。这类响应的响应头目前仍是Content-Type: application/json,解析错误体前请先判断正文是否为 JSON。 - 上游服务商返回的错误会原样转发,状态码和报错原文由上游决定。完整列表见错误码与请求标识。