Skip to content

流式生成

通过流式传输实时监控图像生成进度。支持两种流式格式:

  • SSE (Server-Sent Events): 文本格式,默认选项,兼容性好
  • msgpack: 二进制格式,更高效,适合高性能场景

基本用法

typescript
import { NovelAI } from 'novelai-sdk-unofficial';
import { writeFileSync } from 'fs';

const client = new NovelAI({ apiKey: 'your-api-key' });

const stream = client.image.generateStream({
  prompt: '1girl, detailed background',
  model: 'nai-diffusion-4-5-full',
  steps: 28,
});

for await (const chunk of stream) {
  if (chunk.event_type === 'intermediate') {
    console.log(`步骤 ${chunk.step_ix}/${chunk.steps}: sigma=${chunk.sigma}`);
    
    // 可选:保存中间图像
    const preview = Buffer.from(chunk.image, 'base64');
    writeFileSync(`preview_${chunk.step_ix}.png`, preview);
  } else if (chunk.event_type === 'final') {
    console.log('生成完成!');
    const finalImage = Buffer.from(chunk.image, 'base64');
    writeFileSync('final.png', finalImage);
  }
}

流式格式选择

SSE 格式(默认)

typescript
const stream = client.image.generateStream({
  prompt: '1girl',
  stream: 'sse',  // 默认值,可省略
});

msgpack 格式

msgpack 是二进制格式,传输效率更高:

typescript
const stream = client.image.generateStream({
  prompt: '1girl',
  stream: 'msgpack',  // 使用 msgpack 格式
});

for await (const chunk of stream) {
  // 使用方式与 SSE 完全相同
  if (chunk.event_type === 'final') {
    const image = Buffer.from(chunk.image, 'base64');
  }
}

选择建议:

  • 一般场景使用 SSE(默认),调试方便
  • 高性能/低延迟场景使用 msgpack

数据块类型

中间数据块

生成过程中接收:

typescript
{
  event_type: 'intermediate',
  step_ix: number,      // 当前步骤(从 0 开始)
  steps: number,        // 总步数
  sigma: number,        // 当前 sigma 值
  image: string,        // Base64 编码的预览图像
}

最终数据块

生成完成时接收:

typescript
{
  event_type: 'final',
  image: string,        // Base64 编码的最终图像
}

进度显示

typescript
const stream = client.image.generateStream({
  prompt: '1girl',
  steps: 28,
});

for await (const chunk of stream) {
  if (chunk.event_type === 'intermediate') {
    const progress = ((chunk.step_ix + 1) / chunk.steps * 100).toFixed(1);
    console.log(`进度: ${progress}%`);
  }
}

带所有参数

流式生成支持所有标准生成参数:

typescript
const stream = client.image.generateStream({
  prompt: '1girl, cat ears',
  model: 'nai-diffusion-4-5-full',
  size: 'portrait',
  steps: 28,
  scale: 5.0,
  sampler: 'k_euler_ancestral',
  seed: 12345,
  
  // 高级功能也支持
  characterReferences: [...],
  controlnet: {...},
  i2i: {...},
});

错误处理

typescript
try {
  const stream = client.image.generateStream({
    prompt: '1girl',
  });

  for await (const chunk of stream) {
    // 处理数据块...
  }
} catch (error) {
  if (error instanceof NetworkError) {
    console.error('流式传输过程中连接丢失');
  } else {
    console.error('生成失败:', error.message);
  }
}

使用场景

进度条 UI

typescript
async function generateWithProgress(prompt: string) {
  const stream = client.image.generateStream({ prompt, steps: 28 });
  
  for await (const chunk of stream) {
    if (chunk.event_type === 'intermediate') {
      updateProgressBar(chunk.step_ix + 1, chunk.steps);
    } else if (chunk.event_type === 'final') {
      return Buffer.from(chunk.image, 'base64');
    }
  }
}

实时预览

typescript
async function generateWithPreview(prompt: string, onPreview: (img: Buffer) => void) {
  const stream = client.image.generateStream({ prompt });
  
  for await (const chunk of stream) {
    if (chunk.event_type === 'intermediate') {
      const preview = Buffer.from(chunk.image, 'base64');
      onPreview(preview);
    } else if (chunk.event_type === 'final') {
      return Buffer.from(chunk.image, 'base64');
    }
  }
}

// 使用
await generateWithPreview('1girl', (preview) => {
  // 用预览图像更新 UI
  displayImage(preview);
});

取消

使用 AbortController 显式取消流式生成:

typescript
const controller = new AbortController();

const stream = client.image.generateStream(
  { prompt: '1girl' },
  controller.signal,  // 传入 signal
);

// 5 秒后取消
setTimeout(() => controller.abort(), 5000);

try {
  for await (const chunk of stream) {
    if (chunk.event_type === 'intermediate') {
      console.log(`步骤 ${chunk.step_ix}/${chunk.steps}`);
    } else if (chunk.event_type === 'final') {
      console.log('生成完成');
    }
  }
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('生成已取消');
  } else {
    throw error;
  }
}

取消会:

  • 立即停止接收数据
  • 释放网络连接
  • 抛出 AbortError 异常

## 提示

1. **中间图像质量较低** - 它们用于预览,不是最终用途。

2. **更多步数 = 更多数据块** - 更高的步数会产生更多的中间更新。

3. **网络考虑** - 流式传输需要在整个生成过程中保持稳定连接。

4. **内存使用** - 如果保存所有中间图像,请注意内存/磁盘使用。

基于 MIT 许可证发布