文本流式生成
实时流式输出生成文本。
基本用法
typescript
import { NovelAI } from 'novelai-sdk-unofficial';
const client = new NovelAI({ apiKey: 'your-api-key' });
const stream = client.text.generateStream({
input: 'Once upon a time',
maxLength: 100,
});
for await (const chunk of stream) {
process.stdout.write(chunk);
}
console.log(); // 结尾换行工作原理
generateStream 方法返回一个异步生成器,在生成时逐块产出文本。每个块是包含一个或多个 token 的字符串。
typescript
const stream = client.text.generateStream({
input: 'Hello',
maxLength: 50,
});
// 数据块在生成时到达
for await (const chunk of stream) {
console.log('收到:', JSON.stringify(chunk));
}带所有参数
流式生成支持所有标准生成参数:
typescript
const stream = client.text.generateStream({
input: 'The story begins',
model: 'llama-3-erato-v1',
temperature: 1.1,
maxLength: 100,
topP: 0.95,
repetitionPenalty: 1.1,
stopSequences: ['\n\n'],
});
for await (const chunk of stream) {
process.stdout.write(chunk);
}说明:使用
stopSequences/banSequences时,会在开始流式生成前额外调用 token-count 端点进行 token 化。
收集完整输出
typescript
async function generateText(input: string): Promise<string> {
const stream = client.text.generateStream({ input, maxLength: 100 });
let fullText = '';
for await (const chunk of stream) {
fullText += chunk;
}
return fullText;
}
const result = await generateText('Once upon a time');
console.log(result);进度回调
typescript
async function generateWithCallback(
input: string,
onChunk: (chunk: string, accumulated: string) => void
): Promise<string> {
const stream = client.text.generateStream({ input, maxLength: 100 });
let accumulated = '';
for await (const chunk of stream) {
accumulated += chunk;
onChunk(chunk, accumulated);
}
return accumulated;
}
// 使用
await generateWithCallback('Hello', (chunk, total) => {
console.log(`新增: "${chunk}" | 总长度: ${total.length}`);
});错误处理
typescript
import { NetworkError, AuthenticationError } from 'novelai-sdk-unofficial';
try {
const stream = client.text.generateStream({
input: 'Hello',
maxLength: 100,
});
for await (const chunk of stream) {
process.stdout.write(chunk);
}
} catch (error) {
if (error instanceof NetworkError) {
console.error('流式传输过程中连接丢失');
} else if (error instanceof AuthenticationError) {
console.error('API Key 无效');
} else {
console.error('生成失败:', error);
}
}使用场景
聊天界面
typescript
async function streamResponse(userMessage: string) {
const context = `User: ${userMessage}\nAssistant:`;
const stream = client.text.generateStream({
input: context,
stopSequences: ['\nUser:', '\n\n'],
maxLength: 150,
});
process.stdout.write('Assistant: ');
for await (const chunk of stream) {
process.stdout.write(chunk);
}
console.log();
}打字机效果
typescript
async function typewriterEffect(input: string, delayMs: number = 50) {
const stream = client.text.generateStream({ input, maxLength: 100 });
for await (const chunk of stream) {
for (const char of chunk) {
process.stdout.write(char);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
}Web 服务器(Express)
typescript
import express from 'express';
const app = express();
app.get('/generate', async (req, res) => {
const input = req.query.input as string;
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const stream = client.text.generateStream({ input, maxLength: 100 });
for await (const chunk of stream) {
res.write(`data: ${JSON.stringify({ text: chunk })}\n\n`);
}
res.write('data: [DONE]\n\n');
res.end();
});取消
使用 AbortController 显式取消流式生成:
typescript
const controller = new AbortController();
const stream = client.text.generateStream(
{ input: 'Once upon a time', maxLength: 100 },
controller.signal,
);
// 5 秒后取消
setTimeout(() => controller.abort(), 5000);
try {
for await (const chunk of stream) {
process.stdout.write(chunk);
}
} catch (error) {
if (error.name === 'AbortError') {
console.log('\n生成已取消');
} else {
throw error;
}
}提示
流式传输适合用户体验 - 用户可以立即看到输出,而不是等待。
处理断开连接 - 网络问题可能中断流。
内存高效 - 流式传输不会缓冲整个响应。
相同参数 - 所有生成参数都适用于流式传输。