请求输出上限
Sunra 会把您传入的上限转发给上游 provider。响应因触及该上限而停止时,会返回
finish_reason: "length"(Chat Completions 与 Responses)或 stop_reason: "max_tokens"(Messages)——与您直接调用 provider 得到的信号一致。
在 Chat Completions 上,部分 provider 只认
max_tokens 而忽略 max_completion_tokens——于是您以为设了上限,实际得到的是无上限的补全。对于 deepseek/ 系列模型,网关会代您把 max_completion_tokens 翻译为 max_tokens。两个字段同时传入时,取较小者生效,因为两者都是上限语义。原生支持 max_completion_tokens 的 provider 不做改写,因此您设置的上限只会在一处被执行一次。模型输出上限
每个模型都有自己的最大输出长度,与您请求的数值无关。请求超过模型允许的值会被拒绝,而不是被静默削平——上游 provider 返回400,并在消息中给出合法区间。例如 deepseek/deepseek-v4-flash 与 deepseek/deepseek-v4-pro 最高接受 393,216 个输出令牌,超出即拒绝:
400 是刻意设计。被静默降低的上限与”正常跑到上限”无法区分:两者都报 finish_reason: "length",usage 里也没有任何字段能告诉您发生的是哪一种。
输出上限因模型而异,并随 provider 发布新版本而变化。请把 400 当作权威答案,不要在客户端里硬编码某个上限值。
流生命周期
流式请求(stream: true)在网关侧受两条限制约束:
只要流在空闲窗口内持续输出令牌,这两条限制都不会触发——长生成不会仅仅因为”长”而被打断。非流式请求不受这两条限制约束,它们有自己固定的 6 分钟整体上限。
中止帧
任一限制触发时——或流中途上游连接故障时——网关不会裸断连。它会把仍持有的内容刷出,按您所调用 endpoint 的 SSE 方言发送一个 error 帧,然后干净地关闭流。该帧之前已投递的所有内容都是有效输出,可以照常使用。 在/v1/chat/completions 与 /v1/responses 上:
[DONE] 哨兵只属于 Chat Completions 方言;Responses 流在 error 帧之后直接结束,没有 [DONE]。
在 /v1/messages 上,该帧遵循 Anthropic 方言——一个带类型的 error 事件,且没有 [DONE]:
type 恒为 gateway_stream_aborted。在 OpenAI 形状的方言上,code 指明触发的是哪条限制:
没有中止帧的流就是正常结束的流。由于该帧是确定性的,若流被截断且没有中止帧,应视为客户端或网络问题,而不是网关中止。
被中止的流如何计费
被中止的流按已投递给您的内容计费,绝不会按 provider 内部可能继续生成的完整内容计费。- 若中止前 provider 已上报最终 usage,则按该上报计费。
- 否则按保守估计结算已投递的输出——大约每 4 个字符折算 1 个令牌,并以您请求的输出上限封顶。该笔记录会标记
usage_source: "conservative_estimate"备查。 - 若没有投递任何内容,或 provider 上报了明确的错误,请求标记为失败并释放预留的额度。失败的请求不计费。