对话补全
POST /v1/chat/completions —— 发一段对话过去,模型接着往下写一句。
先理解这个接口#
对话补全接口做的事只有一件:你把整段对话发过去,模型接着往下写一句,然后返回。它没有记忆——服务端不保存任何上下文,第二轮要接着聊,就得把前面所有轮次连同模型上一次的回答一起再发一遍。
对话内容放在 messages 数组里,按时间顺序排列。每条消息有 role 和 content 两个必填字段,role 表明这句话是谁说的。
一个三轮对话的 messages:注意 assistant 那条是模型之前说的,由你带回来
[ { "role": "system", "content": "你是一名简洁的技术支持助理,只用中文回答。" }, { "role": "user", "content": "我的订单还能退款吗?" }, { "role": "assistant", "content": "可以的,支付后 7 天内都能申请全额退款。" }, { "role": "user", "content": "那退到账要多久?" }]第一次调用#
下面三段代码做同一件事:问一个问题,打印模型的回答。
curl:最小请求
curl https://ai.oceango.hk/v1/chat/completions \ -H "Authorization: Bearer $AIROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-5.2", "messages": [ { "role": "user", "content": "用一句话解释什么是向量数据库" } ] }'响应:回答在 choices[0].message.content 里
{ "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1754126400, "model": "openai/gpt-5.2", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "向量数据库是一种按语义相似度而不是精确匹配来检索数据的存储系统。" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 21, "completion_tokens": 28, "total_tokens": 49 }}finish_reason 说明模型为什么停下:stop 是正常说完,length 是撞上了 max_tokens 上限(回答被截断了,多半要调大重来),tool_calls 是模型要求你去调工具。
Python:用官方 openai 库,改 base_url 即可
import osfrom openai import OpenAI
client = OpenAI( api_key=os.environ["AIROUTER_API_KEY"], base_url="https://ai.oceango.hk/v1",)
response = client.chat.completions.create( model="openai/gpt-5.2", messages=[ {"role": "system", "content": "你是一名简洁的技术写作者。"}, {"role": "user", "content": "用一句话解释什么是向量数据库"}, ],)
print(response.choices[0].message.content)print(response.usage.total_tokens, "tokens")Node.js:同样只改 baseURL
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.AIROUTER_API_KEY, baseURL: 'https://ai.oceango.hk/v1',});
const response = await client.chat.completions.create({ model: 'openai/gpt-5.2', messages: [ { role: 'system', content: '你是一名简洁的技术写作者。' }, { role: 'user', content: '用一句话解释什么是向量数据库' }, ],});
console.log(response.choices[0].message.content);请求字段#
下表逐条说明请求体认识的字段。控制随机性的采样参数(temperature、top_p 等)单独放在下一节。未列出的字段会原样转发给上游。
modelstring必填- 模型名。支持别名、
:变体与逗号降级链,语法见 模型与路由。 messagesarray必填- 整段对话,按时间顺序排列;角色与多轮写法见上面的"先理解这个接口"。
content允许为null(工具调用回合的合法形态),会被规范化成空文本部分。 max_tokensinteger- 不传时预扣按
0.6 × 模型最大输出估算,见 计费口径。给了它能让冻结额更贴近实际。 streambooleantrue时返回text/event-stream,帧序列见下一节。stream_optionsobject- 传
{"include_usage": true}才会在流的末尾多收到一帧用量统计;不传的话整条流里没有任何用量信息。 tools / tool_choicearray / string- 工具调用,透传。
parallel_tool_calls也透传;Anthropic 上游用方向相反的disable_parallel_tool_use表达,网关会自动取反。 response_formatobject- 结构化输出。是否可用取决于目标端点,可配合
provider.require_parameters强制只选支持它的线路。 reasoningobject- 跨方言统一的思考开关:
enabled打开思考,effort取minimal/low/medium/high,max_tokens给思考预算,exclude表示照样思考但不返回思考内容。出站时映射成上游各自的字段(Anthropic 的thinking、OpenAI 的reasoning_effort);模型不支持时以上游行为为准,要强制只走支持它的线路请配合provider.require_parameters。 providerobject- 路由偏好(本平台扩展):白名单、黑名单、排序、价格上限、区域与合规约束。见 模型与路由。
modelsarray- 模型级降级链。它不是
model里逗号写法的替代品,而是与之合并去重(顺序为model的逗号链 →models→provider.models);主模型加兜底最多 4 个,多出来的静默忽略。 transformsarray- 为兼容 OpenRouter 保留的字段:能被收下并记录,但当前没有任何变换真的生效(
middle-out压缩尚未实现)。输入超窗请自己裁剪或换长上下文模型,别指望它兜住。
一个带路由约束的请求体
{ "model": "anthropic/claude-fable-5", "messages": [{ "role": "user", "content": "总结这份会议记录" }], "provider": { "sort": "price", "only": ["cl-sp", "cl-of"], "allow_fallbacks": true, "max_price": { "prompt": "5.00000000", "completion": "20.00000000" } }}采样参数#
模型每写一个词,实际是在一堆候选词上算概率再挑一个。下面这些参数控制"怎么挑",它们都是可选的,不传就用模型自己的默认值。
provider.require_parameters,见模型与路由。流式输出#
每一帧的格式是 data: <JSON>,JSON 结构为 chat.completion.chunk,流以 data: [DONE] 结束。[DONE] 不是合法 JSON,客户端必须先判断这个哨兵值再解析。
error 字段的数据、紧接着 [DONE]。客户端如果不检查每帧是否含 error,会把失败请求当成正常结束。典型的一段流(: ping 是 SSE 注释行,用于防止中间层超时,标准解析器会忽略)
data: {"choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}data: {"choices":[{"index":0,"delta":{"content":"维多利亚港的风"},"finish_reason":null}]}: pingdata: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}data: {"choices":[],"usage":{"prompt_tokens":26,"completion_tokens":48,"total_tokens":74}}data: [DONE]usage 帧照样发——你的用量统计逻辑不必按上游分家。但注意估算标记不在响应体里:非流式看响应头 X-AiRouter-Usage-Estimated,流式只能在 调用日志 里看到(响应头此时已经发出去了)。Python:逐字打印,帧里没有 content 的时候要跳过
import osfrom openai import OpenAI
client = OpenAI(api_key=os.environ["AIROUTER_API_KEY"], base_url="https://ai.oceango.hk/v1")
stream = client.chat.completions.create( model="openai/gpt-5.2", messages=[{"role": "user", "content": "写一首关于香港雨季的短诗"}], stream=True, stream_options={"include_usage": True}, # 不传这个就收不到用量帧)
for chunk in stream: # 用量帧的 choices 是空数组,直接访问 [0] 会抛 IndexError if not chunk.choices: if chunk.usage: print(f"\n\n共 {chunk.usage.total_tokens} tokens") continue delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)Node.js:同样的循环
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.AIROUTER_API_KEY, baseURL: 'https://ai.oceango.hk/v1',});
const stream = await client.chat.completions.create({ model: 'openai/gpt-5.2', messages: [{ role: 'user', content: '写一首关于香港雨季的短诗' }], stream: true, stream_options: { include_usage: true },});
for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content; if (delta) process.stdout.write(delta); if (chunk.usage) console.log(`\n\n共 ${chunk.usage.total_tokens} tokens`);}用量口径#
- 非流式响应总是带
usage;流式默认不带,需要显式传stream_options.include_usage。 prompt_tokens是全部输入 token,包含缓存读与缓存写部分;缓存读在prompt_tokens_details.cached_tokens,缓存写在cache_creation_input_tokens。按标准输入价计费的是三者相减后的余量。completion_tokens只含可见输出,不含思考 token;思考量在completion_tokens_details.reasoning_tokens里单独给出、单独计费。- 用量帧只报 token,不含金额。非流式的费用在响应头,流式的费用在 调用日志。
与 OpenAI 的已知差异#
Anthropic 方言#
除了上面那套格式,网关还提供 POST /v1/messages,它遵循 Anthropic 的 Messages 格式——那是另一套写法不同、能力相当的对话协议:system 不放在 messages 里而是独立的顶层字段,返回的正文是 content 数组而不是单个字符串。如果你现有代码用的是 Anthropic 官方 SDK,指过来就能跑;否则用上面的 /v1/chat/completions 即可,两条端点背后是同一批模型。
POST /v1/messages 与 Anthropic Messages API 兼容:model 一样支持别名、:变体 与逗号降级链,流式天然带用量(message_start / message_delta 里就有),不需要 include_usage。
curl:Anthropic 格式的同一个问题
curl https://ai.oceango.hk/v1/messages \ -H "X-Api-Key: $AIROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-fable-5", "max_tokens": 256, "system": "你是一名简洁的技术写作者。", "messages": [ { "role": "user", "content": "用一句话解释什么是向量数据库" } ] }'provider / models / transforms 这些扩展只在 OpenAI 方言的请求体里被识别。写在 /v1/messages 上会被当成未知字段原样转发给上游,多半直接被上游 400。要用路由偏好就走 /v1/chat/completions,或者把降级链写进 model 的逗号形式。anthropic-version不用传:网关出站时自己填2023-06-01。anthropic-beta目前不转发——出站请求头由网关重新构造,客户端的自定义头不会带过去。- 错误信封是 Anthropic 形状,没有
code字段,分支要靠 HTTP 状态与error.type,见 错误码。