流式输出
使用 Server-Sent Events (SSE) 实时接收响应流
流式响应
Token101 支持所有文本生成端点的实时流式输出。流式传输在 Token 生成时即刻推送,用户无需等待完整响应,体验更流畅、更即时。
流式传输基于 Server-Sent Events (SSE) 标准 — 与 Anthropic 和 OpenAI API 所使用的标准相同。
工作原理
设置 stream: true(JSON 中为 "stream": true)后,服务器将通过持久 HTTP 连接持续推送部分响应块。每个块是一个以 data: 为前缀的 JSON 对象,流以 data: [DONE] 结束。
data: {"type":"content_block_delta","delta":{"text":"你好"}}
data: {"type":"content_block_delta","delta":{"text":",世界"}}
data: {"type":"message_stop"}
data: [DONE]Anthropic Messages API
Python — 流式输出
import anthropic
import os
client = anthropic.Anthropic(
api_key=os.environ.get("TOKEN101_API_KEY"),
base_url="https://token.ppthub.shop/api",
)
with client.messages.stream(
model="gpt-5.2",
max_tokens=512,
messages=[{"role": "user", "content": "写一首关于海洋的俳句。"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print() # 末尾换行Python — 底层流式处理(手动事件处理)
import anthropic
import os
client = anthropic.Anthropic(
api_key=os.environ.get("TOKEN101_API_KEY"),
base_url="https://token.ppthub.shop/api",
)
with client.messages.stream(
model="gpt-5.2",
max_tokens=512,
messages=[{"role": "user", "content": "写一首短诗。"}],
) as stream:
for event in stream:
if hasattr(event, 'delta') and hasattr(event.delta, 'text'):
print(event.delta.text, end="", flush=True)Node.js / TypeScript — 流式输出
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
apiKey: process.env.TOKEN101_API_KEY,
baseURL: 'https://token.ppthub.shop/api',
});
const stream = await client.messages.stream({
model: 'gpt-5.2',
max_tokens: 512,
messages: [{ role: 'user', content: '写一首关于海洋的俳句。' }],
});
for await (const event of stream) {
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
process.stdout.write(event.delta.text);
}
}
console.log(); // 末尾换行curl — 原始 SSE
curl https://token.ppthub.shop/api/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN101_API_KEY" \
-d '{
"model": "gpt-5.2",
"max_tokens": 512,
"stream": true,
"messages": [
{"role": "user", "content": "写一首关于海洋的俳句。"}
]
}'OpenAI Chat Completions API
Python — 流式输出
from openai import OpenAI
import os
client = OpenAI(
api_key=os.environ.get("TOKEN101_API_KEY"),
base_url="https://token.ppthub.shop/api/v1",
)
stream = client.chat.completions.create(
model="gpt-5.2",
messages=[{"role": "user", "content": "写一首关于海洋的俳句。"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
print()Node.js / TypeScript — 流式输出
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.TOKEN101_API_KEY,
baseURL: 'https://token.ppthub.shop/api/v1',
});
const stream = await client.chat.completions.create({
model: 'gpt-5.2',
messages: [{ role: 'user', content: '写一首关于海洋的俳句。' }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
}
console.log();curl — 原始 SSE
curl https://token.ppthub.shop/api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN101_API_KEY" \
-d '{
"model": "gpt-5.2",
"stream": true,
"messages": [
{"role": "user", "content": "写一首关于海洋的俳句。"}
]
}'在 Web 应用中使用流式输出
React — 自定义 Hook
import { useState, useCallback } from 'react';
export function useStream() {
const [content, setContent] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const streamMessage = useCallback(async (prompt: string) => {
setContent('');
setIsStreaming(true);
const response = await fetch('https://token.ppthub.shop/api/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.NEXT_PUBLIC_TOKEN101_KEY}`,
},
body: JSON.stringify({
model: 'gpt-5.2',
stream: true,
messages: [{ role: 'user', content: prompt }],
}),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split('\n');
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const data = JSON.parse(line.slice(6));
const text = data.choices?.[0]?.delta?.content;
if (text) setContent((prev) => prev + text);
}
}
}
setIsStreaming(false);
}, []);
return { content, isStreaming, streamMessage };
}永远不要在客户端代码中暴露 Token101 API Key。请使用服务端 API 路由代理请求,确保 Key 的安全。
Next.js — 服务端流式 API 路由
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const response = await fetch('https://token.ppthub.shop/api/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.TOKEN101_API_KEY}`,
},
body: JSON.stringify({ model: 'gpt-5.2', stream: true, messages }),
});
// 将 SSE 流直接透传给客户端
return new Response(response.body, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
},
});
}处理流事件
Anthropic Messages API 事件类型
| 事件类型 | 说明 |
|---|---|
message_start | 包含初始消息元数据 |
content_block_start | 内容块开始(text 或 tool_use) |
content_block_delta | 增量文本或工具输入 |
content_block_stop | 内容块结束 |
message_delta | 最终消息统计(stop_reason、usage) |
message_stop | 消息结束 |
OpenAI Chat Completions 事件字段
| 字段 | 说明 |
|---|---|
choices[0].delta.content | 文本内容块(可能为 null) |
choices[0].delta.role | 角色(仅在第一个块中出现) |
choices[0].finish_reason | 流结束原因(最后一个块前为 null) |
使用建议
始终刷新输出 — 在终端打印流式 Token 时,使用 flush=True(Python)或 process.stdout.write()(Node.js)避免缓冲延迟。
处理流错误 — 用 try/catch 包裹流式代码。连接中途断开时 SDK 会抛出错误。
设置合理超时 — 长响应的流式传输比非流式耗时更长。请为 HTTP 客户端配置较大的读取超时(建议 60 秒以上)。
Token 计数 — 流式响应在最终事件中包含用量元数据(Anthropic 为 message_delta,OpenAI 为最后一个块的 usage)。