Docs

流式响应

实现实时流式响应

本页目录
  1. 概述
  2. 流式可用范围
  3. 流式结算方式
  4. 选择端点形态
  5. 注意事项
  6. Responses Streaming
  7. Chat Completions Streaming
  8. 流结束条件
  9. Web App 模式
  10. 最佳实践
  11. 新项目优先选择 Responses streaming
  12. 增量刷新输出
  13. 处理断开和重试
  14. 处理流式错误
  15. 下一步

概述

流式响应在模型生成过程中持续返回内容,适合聊天界面和长文本生成。

当客户端需要 Responses 形态时,使用 Responses streaming。当客户端使用 Chat Completions 形态时,使用 Chat Completions streaming。

共享路由上的流式请求仅对已通过流式认证的渠道可用。通过认证的渠道在探测中完成过真实流式请求,并验证了 SSE 终态用量证据(写入 channel_model_usage_evidence 的 sse_terminal 行)。未认证渠道的流式请求会被直接拒绝(model_unavailable),AnyPath 不会猜测用量或供应商收益。

流式可用范围

  • 共享路由:已通过流式认证的渠道支持流式;未认证渠道的流式请求会被直接拒绝(model_unavailable)。
  • Chat Completions:客户端请求必须自带 stream_options.include_usage: true,网关不会代为注入;缺少该字段的流式请求会被拒绝。非 Anthropic 客户端跨协议调用 Anthropic 渠道的流式(转换后的 Chat)同样要求 include_usage。
  • Anthropic Messages、Gemini :streamGenerateContent 和 Responses:流式终态恒有用量,无需客户端额外操作。Gemini 协议的 Chat 流式不支持。
  • Images:图片家族不支持流式。

流式结算方式

流式与非流式请求共用同一个结算闭环:上游分发前预先冻结部分余额(hold),以 SSE 终态用量证据确定计量用量,再从冻结金额中按实际用量扣费(capture),扣费不超过冻结金额。

如果客户端中途断连,网关会继续把上游流读到终态——供应商仍会完成本次生成——并按终态用量证据对实际生成量正常结算扣费,扣费不超过冻结金额。断连不等于免单:供应商已完成生成,调用方需为实际用量付账。只有在供应商返回错误、流超过 600 秒超时上限,或终态确实缺少用量证据时,冻结金额才会被释放(release),调用方不扣费。流式请求只有一次尝试,不做重试或切换。

选择端点形态

按客户端 API 形态选择流式接口:

  • 已使用 Responses 的客户端:POST /v1/responses
  • OpenAI Chat Completions 兼容客户端:POST /v1/chat/completions
  • 不建议为不同的模型单独写一套流式处理逻辑

注意事项

  • 收到增量内容后应立即追加到界面或终端
  • 长时间连接要处理断开、超时和重试
  • 重试时尽量保持原 API 类型不变

Responses Streaming

curl https://api.anypath.dev/v1/responses \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "input": "Write a short poem.",
    "stream": true
  }'
from openai import OpenAI

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.anypath.dev/v1"
)

stream = client.responses.create(
    model="gpt-4o-mini",
    input="Write a short poem.",
    stream=True
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: 'sk-your-api-key',
  baseURL: 'https://api.anypath.dev/v1'
});

const stream = await client.responses.create({
  model: 'gpt-4o-mini',
  input: 'Write a short poem.',
  stream: true
});

for await (const event of stream) {
  if (event.type === 'response.output_text.delta') {
    process.stdout.write(event.delta);
  }
}

Chat Completions Streaming

如果你的框架仍使用 /v1/chat/completions 的 SSE 分片,可以继续这样调用。请求中需要设置 stream_options.include_usage: true,让流式终态携带用量;缺少该字段的 Chat Completions 流式请求会被网关拒绝:

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Write a short poem"}],
    stream=True,
    stream_options={"include_usage": True}
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

流结束条件

常见结束条件:

  • Responses API: response.completed
  • Chat Completions: finish_reason: "stop"
  • 达到长度上限:finish_reason: "length"
  • 工具调用:按响应里的工具事件继续处理

Web App 模式

async function streamChat(message) {
  const response = await fetch('https://api.anypath.dev/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk-your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      messages: [{ role: 'user', content: message }],
      stream: true,
      stream_options: { include_usage: true }
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split('\\n').filter(line => line.startsWith('data: '));

    for (const line of lines) {
      const data = line.slice(6);
      if (data === '[DONE]') return;
      const parsed = JSON.parse(data);
      const content = parsed.choices?.[0]?.delta?.content;
      if (content) {
        document.getElementById('output').textContent += content;
      }
    }
  }
}

最佳实践

新项目优先选择 Responses streaming

如果客户端需要 Responses streaming,使用 /v1/responses。如果客户端需要 Chat Completions 形态,继续使用 /v1/chat/completions。

增量刷新输出

不要等完整响应结束后再显示内容,收到增量就追加。

处理断开和重试

长连接可能断开。重试时保留相同 API 类型和请求语义,避免重复执行有副作用的操作。客户端中途断连时,网关仍会把上游响应读到终态,并按实际生成量扣费——断连不会取消供应商的生成,也不会免除本次费用。

处理流式错误

流式响应可能在中途失败。此时网关会直接截断流:连接关闭,没有终态事件——不发错误分片,也不发 data: [DONE]。这是刻意设计:被截断的流绝不可能被误认为完整响应。

只要流缺少协议终态事件就结束了(Responses 的 response.completed、Messages 的 message_stop、Chat Completions 的最终 usage 分片加 [DONE]),就按失败处理。重试前记录响应头里的 X-Request-Id,便于事后追踪本次调用。

下一步

  • 选择 API 类型时,请同时参考 API 概览
  • 调试延迟或重复请求时,记录请求 ID
  • 需要更完整的错误处理时,继续阅读错误处理指南