# @breeze\.blue/sdk 中文文档

规范地址：https://npmcn.com/package/%40breeze.blue/sdk/

版本：0\.23\.0
npm 元数据更新时间：2026-10-31T16:53:33.656Z
中文译文更新时间：2026-10-31T17:30:36.380Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40breeze.blue/sdk
原文对照：https://npmcn.com/package/%40breeze.blue/sdk/?lang=original
项目主页：https://breezeblue.ai/
源码仓库：https://github.com/breezebluestudio/BreezeBlueStudio

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：239 kB，30 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：0 个
- 首次发布：2026-05-13T13:19:23.881Z
- 最新版本发布：2026-10-31T16:53:33.486Z
- 已发布版本数：34（近 12 个月 34 个）

## 中文简介

微风蓝开发者API的ESM-first TypeScript SDK。

## 安装

```sh
npm install @breeze.blue/sdk
```

也可以使用：`pnpm add @breeze.blue/sdk`、`yarn add @breeze.blue/sdk`、`bun add @breeze.blue/sdk`

## README

# @breeze.blue / sdk

微风蓝开发者API的ESM-first TypeScript SDK。涵盖了 文本转语音，语音管理，语音预览生成，历史音频， 模型和帐户使用情况。

在Node、浏览器和edge运行时中导入根入口点是安全的。当地的 音频回放助手只存在于node中 `@breeze.blue/sdk/node` 子路径。

TTS指导接受1-10；如果省略，则使用默认的1。声音设计接受1-10分；省略指导或传递空样本均匀在1-4之间为每个预览，四舍五入到一个小数点。HTTP速度和容量按每个请求计算，默认为1。使用 `voiceSettings: { useSpeakerBoost: true }` 为了与参考声音更接近（默认为false）：非空指令和大于1的指导被忽略，否则指导被应用为1；指令总是被应用。实时修复了会话创建时的增强；当boost应用于上面1所请求的指导时，创建一个新会话来添加指令。通过 `removeBackgroundNoise: true` 对克隆样本进行降噪（默认为false）。

## 安装

```sh
pnpm add @breeze.blue/sdk
```

或者:

```sh
npm install @breeze.blue/sdk
```

## API密匙

在Breeze Blue Developer Console中创建一个API密钥，然后导出它：

```sh
export BREEZE_API_KEY=brz_...
```

SDK发送密钥时使用 `xi-api-key` 头。它还发送 `x-breeze-sdk` 对于服务器端的可观察性。

每个REST方法都需要API密钥。唯一的例外是 `textToSpeech.realtime.connect(...)` 与一个 `clientSecret`，这让 浏览器客户端在没有密钥的情况下运行（请参阅下面的实时部分）。

## 快速入门

```ts
import { BreezeBlueClient } from "@breeze.blue/sdk";
import { save } from "@breeze.blue/sdk/node";

const client = new BreezeBlueClient();

const audio = await client.textToSpeech.convert(
  "voc_...",
  { text: "Hello from Breeze Blue." },
  { outputFormat: "mp3" },
);

await save(audio, "hello.mp3");
console.log(audio.contentType);
console.log(audio.historyItemId);
```

`new BreezeBlueClient()` 读取 `BREEZE_API_KEY` 默认情况下，并将请求发送到 `https://api.breeze.blue`． 指向另一个环境，pass `baseUrl` 或者 集 `BREEZE_BASE_URL`.

```ts
const client = new BreezeBlueClient({
  apiKey: "brz_...",
  baseUrl: "https://api.breeze.blue",
  timeout: 120_000,
});
```

每个请求选项接受 `timeout`， `signal`，以及额外的 `headers`.

## 文本到语音

```ts
const audio = await client.textToSpeech.convert("voc_...", {
  text: "Render this line.",
});

const audioStream = await client.textToSpeech.stream("voc_...", {
  text: "Stream this line.",
});

const enhanced = await client.textToSpeech.enhance({
  instruction: "Calm, warm, bedtime narration.",
  languageCode: "en",
});
```

对于长文本、大量引用的语音或批处理，使用异步文本到语音 生产中，调用者不应该保持HTTP连接打开：

```ts
const job = await client.textToSpeech.createJob(
  "voc_...",
  { text: "Render this longer script." },
  { outputFormat: "mp3" },
);

const status = await client.generationJobs.get(job.generationJobId);
if (status.status === "ready") {
  const audio = await client.generationJobs.downloadAudio(job.generationJobId);
  await save(audio, "async.mp3");
}
```

如果作业仍处于活动状态， `downloadAudio(...)` 拒绝与 `BreezeBlueGenerationNotReadyError`;读 `error.retryAfter` 之前进行重试。

当一个WebSocket连接应该处理多个连接时，使用实时文本到语音 谈话。实时支持PCM（默认）或MP3在24千赫单声道。 PCM使用原始签名的16位小端采样。通过 `outputFormat: "mp3"` 来 `realtime.createSession`， `realtime.connect`，或者 `realtime.connectManaged` 选择“MP3”。 每个回合最后的MP3字节到达之前 `turn.done`.

实时需要 `WebSocket` 实现。浏览器、边缘运行时和 Node 22+提供了一个全局的；在Node 20上，传递一个构造函数(例如 `ws` 包的 `WebSocket`)通过 `new BreezeBlueClient({ webSocket })`.

在添加文本之前开始消费。音频可以在 `flush()` 和5月 后继续 `endTurn()`;保持单个消费者运行，直到 `turn.done`.

```ts
const connection = await client.textToSpeech.realtime.connect("voc_...", {
  modelId: "breeze-tts-2",
});

const pcmChunks: Uint8Array[] = [];
const consumer = (async () => {
  for await (const message of connection) {
    if (message.type === "audio") {
      // Forward this chunk to your playback or transport layer immediately.
      pcmChunks.push(message.audio);
    } else if (message.type === "error") {
      throw new Error(`Realtime TTS failed: ${message.code}: ${message.message}`);
    } else if (message.type === "turn.done") {
      return;
    }
  }
  throw new Error("Realtime session closed before turn.done");
})();

connection.startTurn("turn_1");
connection.appendText("Hello from Breeze.");
connection.flush();
connection.endTurn();

await consumer;
connection.close();
```

始终使用连接（或其属性） `audio()` / `events()` 迭代器) 回合是活跃的，呼叫 `close()` 当你做完的时候。当SDK检测到 连接失败-无效的服务器帧，或超过16 MiB的消息 堆积未消耗的连接——关闭连接，迭代器拒绝使用 一个 `BreezeBlueRealtimeError`。这个 `audio()` Helper也会抛出 `BreezeBlueRealtimeError` 对于服务器 `error` 事件和异常WebSocket 关闭而不是无声地结束。

当 `BreezeBlueRealtimeError.recoverable` 是 `true`，同样的连接 仍然是可用的。大多数可恢复的转弯命令错误取消了活动的转弯。 一个active-turn `session.update` 拒绝是例外：turn keep 运行。如果 `connection.audio()` 抛出错误，重新开始 `audio()` 迭代器立即在同一连接上继续收集 当前的转变;不要开始替换转弯。

事件是一个类型区分联合(`session.ready`， `session.updated`， `turn.started`， `audio.started`， `turn.done`， `turn.cancelled`， `usage.committed`， `session.expiring`， `session.closed`， `error`， `pong`)，还有骆驼的田野 如 `turnId`， `historyItemId`， `expiresAt`, `ttfaMs`． 把工会看作 非穷举：服务器可能添加事件类型，SDK交付未知 JSON事件不变——忽略你不认识的事件类型 切换详尽。

在回合之间，更新合成指令而不替换 WebSocket。 `updateInstructions(...)` 只发送请求；匹配的 `session.updated` 确认仍然在同一个有序迭代器上。等待 在开始下一个转弯之前：

```ts
connection.updateInstructions("Speak faster and with more energy.");
for await (const message of connection) {
  if (message.type === "session.updated") {
    if (message.instructions !== "Speak faster and with more energy.") {
      throw new Error("Unexpected instructions acknowledgement");
    }
    break;
  }
}

connection.startTurn("turn_2");
```

指令必须为不超过1000个字符的非空字符串。只有一个 更新可能等待确认，并且只有在没有轮询时才接受更新 是活跃的。在一个回合中尝试的更新将被拒绝而不会中断 这把。保持一个流消费者：如果一个长寿命的消费者拥有 迭代器，让它在 `session.updated` 到达 而不是开始第二个迭代器。

在会话内部发送一个keepalive ping `inactivityTimeoutSeconds` 窗口; 服务器返回一个 `pong` 事件。在主动转弯时也要保持运行。 没有音频的长TTFA或上游失速不会暂停空闲截止日期。

```ts
const keepalive = setInterval(() => connection.ping(), 10_000);
// ... run turns ...
clearInterval(keepalive);
connection.close();
```

对于长逻辑会话，请使用可选择的托管连接。它源于 从的安全存活间隔 `session.ready` 并且需要服务器响应 在每次ping之后。丢失的确认将替换一个空闲的半打开的套接字； 如果一个回合处于活动状态，它就会报告 `TURN_INTERRUPTED` 而且从不重放命令。 之后，管理器还会标记一个物理WebSocket以进行回合边界旋转 默认情况下为10分钟（或在服务器截止日期要求时更早）。一个 主动转向可以延迟超过该阈值的切换；的server-advertised 艰苦的一生依然适用。经理 每个中断执行最多三次意外的空闲重新连接尝试 新鲜的会话和紧张的指数回退：

```ts
const connection = await client.textToSpeech.realtime.connectManaged("voc_...", {
  modelId: "breeze-tts-2",
});

const consumer = (async () => {
  for await (const message of connection) {
    if (message.type === "audio") {
      // Play or forward this PCM chunk immediately.
    } else if (message.type === "session.ready") {
      // A new physical epoch is ready; the logical connection remains the same.
    } else if (message.type === "turn.done") {
      return;
    }
  }
})();

await connection.startTurnWhenReady("turn_1");
connection.appendText("Hello from a long-running conversation.");
connection.endTurn();
await consumer;
connection.close();
```

使用 `heartbeatTimeoutMs` 调整确认截止日期和 `maxPhysicalSessionMs` 来调优客户端epoch上限 `maxPhysicalSessionMs: 0` 只有当你有意想要独自依靠的时候 服务器发布的截止日期。

当空闲的重新连接被成功处理时，逻辑迭代器将保留 打开，抑制被替换的物理套接字的终端事件，并发出 新时代的 `session.ready`． 替换设备发送第一次心跳 立即并在入站确认后恢复派生的节奏。 每次中断都限制重新连接尝试。 `startTurnWhenReady(...)` 只在这些空闲替换中的一个已经在进行时等待 `turn.start` 没有被派遣；在发送任何其他回合命令之前等待它。 经过一次有计划的轮转，一个具有突出意义的旧时代 `usage.committed` 事件最多并行消耗5秒，并在所有事件结束后立即关闭 已知已完成的回合。这个活动是尽最大努力的；使用历史和用法 api是真理的持久来源。

托管连接从不缓冲转内容或重放命令。这个 有界的 `startTurnWhenReady(...)` wait发生在它的第一个WebSocket之前 写。如果WebSocket在活动时被中断，它的迭代器拒绝使用 `BreezeBlueRealtimeError` 和 `error.code === "TURN_INTERRUPTED"`;决定从 您的应用程序会话说明是否以及如何开始一个新的回合。 将经理视为一个活动的实时呼叫，而不是一个状态通道。总是 呼叫 `connection.close()` 当通话结束时，用户离开，或离开页面 进入一个长期存在的背景状态；否则它的心跳是故意的 占用服务器WebSocket插槽。

为了缩短第一个音频的时间，可以提前创建会话（例如，在第一个音频出现时） 你的应用程序还在准备转弯，并与其连接 `clientSecret` 当 第一个文本已经准备好了-只剩下WebSocket握手：

```ts
const session = await client.textToSpeech.realtime.createSession("voc_...", {
  modelId: "breeze-tts-2",
});

// Later, when the first text is ready:
const connection = await client.textToSpeech.realtime.connect("voc_...", {
  clientSecret: session.clientSecret,
  websocketUrl: session.websocketUrl,
  directWebsocketUrl: session.directWebsocketUrl,
});
```

当会话响应包含 `directWebsocketUrl`， SDK更喜欢它的 无需查询的来源，并使用WebSocket子协议进行身份验证。它落 回 `websocketUrl` 对于未启用直接传输的服务。

相同的 `clientSecret` 切换让浏览器连接而不需要看到你的 API密钥：手动在服务器上创建会话 `session.clientSecret` 到 页面，并在那里构建一个无密钥客户端：

```ts
// Browser — no API key required for clientSecret connections.
const browserClient = new BreezeBlueClient();
const connection = await browserClient.textToSpeech.realtime.connect("voc_...", {
  clientSecret, // received from your server
});
```

对于托管浏览器连接，提供 `sessionFactory`． SDK调用它 对于每个物理纪元，所以回调必须从 您的后端而不是缓存第一个响应。会话配置属于 在使用自定义工厂的后端请求中：

```ts
import { BreezeBlueRealtimeError } from "@breeze.blue/sdk";

const connection = await browserClient.textToSpeech.realtime.connectManaged("voc_...", {
  sessionFactory: async ({ signal }) => {
    const response = await fetch("/api/breeze-realtime-session", {
      method: "POST",
      signal,
    });
    if (!response.ok) {
      const reconnect =
        [408, 425, 429].includes(response.status) || response.status >= 500;
      throw new BreezeBlueRealtimeError("Could not create realtime session", {
        code: "SESSION_FACTORY_ERROR",
        reconnect,
      });
    }
    return response.json(); // { clientSecret, websocketUrl?, directWebsocketUrl? }
  },
});
```

尊重工厂 `signal`：当逻辑连接关闭时，SDK将终止它 或者物理纪元超时过期。的自定义回调 信号仍然必须强制它自己的有界请求超时。

在收到动态指令更新后 `session.updated`，托管SDK 将确认的值传递到以后的物理时代。SDK-created替代品 将其包含在会话请求中；工厂创建的替代品接收一个 自动 `session.update` 在下一个之前 `session.ready` 暴露在 逻辑迭代器。

`modelId`， `languageCode`， `voiceSettings`， `inactivityTimeoutSeconds`, `enableLogging` 在创建会话时固定。最初的 `instructions` 价值也在创建时提供，但可以稍后提供 匝间替换为 `updateInstructions(...)`. `connect` 忽略了 初始会话选项 `clientSecret` 或者 `websocketUrl` 提供和 记录一个警告。

如果实时WebSocket因网络变化而中断，服务部署， 或者上游实时工作者重启， `connectManaged(...)` 处理有界 只有在没有主动回合时才会重新连接。与较低层次的 `connect(...)`、处理 `error.meta.reconnect === true` 或者一个 `session.closed` 事件与 `reconnect === true` 通过创建新会话并启动新会话 离开你自己的谈话状态。主动回合永远不会原地恢复。 在托管重连期间，瞬态会话创建响应(408,425,429， 和5xx)共享相同的有界重连预算；其他4xx响应失败 逻辑连接立即。一个自定义 `sessionFactory` 应该保留它 与的区别 `new BreezeBlueRealtimeError(message, { reconnect })`;一个普通 `Error` 不能通信终端策略响应，并被视为 在同一有限预算内的瞬态。 `GENERATION_CAPACITY_EXCEEDED` 是 轮作用域：等待以下内容 `turn.cancelled`，停止使用 `meta.retryAfterSeconds`，并在相同的托管逻辑上开始新的打开 不回放文本的连接。

API在以下情况下使用默认的文本到语音模型 `modelId` 是省略了。如果 您需要显式地选择一个模型，调用 `client.models.list()` 然后通过一个 归来的人 `modelId` 值。

所有字段名都遵循TypeScript约定(`modelId`， `voiceSettings`， `outputFormat`， `historyItemId`,……)。SDK将它们转换为 snake\_case发送时的HTTP连线格式，并将JSON响应转换回 骆驼箱子收到了。

音频响应暴露了方便的助手：

```ts
await audio.arrayBuffer();
await audio.bytes();
await audio.blob();

audio.contentType;
audio.historyItemId;
```

节点回放帮助程序：

```ts
import { play, save, stream } from "@breeze.blue/sdk/node";

await play(audio);        // ffplay, with macOS afplay fallback
await save(audio, "x.mp3");
await stream(audioStream); // mpv
```

流式文本到语音默认为 `pcm` 减少到第一个音频的时间。通过 `{ outputFormat: "wav" }` 或者 `{ outputFormat: "mp3" }` 当你需要电线的时候 显式格式。

同步和异步文本到语音也采用采样率，对于有损编码，采用比特率，例如 `{ outputFormat: "wav_24000" }`． 携带采样率的配置文件适用于同步和异步转换；流使用赤裸的编码名称。

## 的声音

搜索该账户可用的所有声音，并检查单个声音。公共 目录结果将语义相似性与精确、前缀和子字符串结合起来 名称匹配;保存的个人声音仍然可以通过姓名，描述， 或语音识别。

```ts
const voices = await client.voices.search({ search: "calm documentary narrator" });
const newestFirst = await client.voices.search();
const dailyTrend = await client.voices.search({
  voiceType: "default",
  sort: "trend",
  languageCode: "zh",
  gender: ["female", "neutral"],
  tone: ["warm"],
});
const taggedFavorites = await client.voices.search({
  favoritesOnly: true,
  tags: ["narration", "novel=三体"],
});
const firstVoiceId = voices.voices[0].voiceId;

const voice = await client.voices.get(firstVoiceId);

const randomVoice = await client.voices.random();
console.log(randomVoice.voiceId, randomVoice.name);
// Narrow the random pool by language and/or voice source.
const randomCatalogVoice = await client.voices.random({ languageCode: "zh", voiceType: "default" });

// Discover the current code-only Voice Metadata contract before building a form.
const metadataOptions = await client.voices.metadataOptions();
console.log(metadataOptions.languageCodes);
console.log(metadataOptions.accentCodesByLanguage.en);
```

语音列表保持已发布的默认稳定创建时间顺序 从最新到最老。使用 `{ sort: "trend", voiceType: "default" }` 对于共享的 每日趋势订单。尚未排名的合格声音跟随快照成员； 返回的页令牌固定初始补充候选。搜索查询保持相关度排名；趋势只是一种 同一关联层中的弱先验。

微风的声音创作总是两个步骤：产生一个 **预览**，让…… 用户接受它，然后 **保存预览** 作为一个真实的声音。三种方法 制作预览：

克隆样本接受MP3， WAV, M4A， FLAC， AAC， OGG, Opus， WebM音频，WMA， 或AIFF，至少3秒，最大5mb。克隆调用是同步的。 提供匹配 `filename` 或者 `contentType` 上传。

```ts
import { readFile } from "node:fs/promises";

// Option A — clone preview from an audio sample
const clonePreview = await client.voices.createClonePreview({
  name: "Demo voice",
  file: {
    data: await readFile("sample.wav"),
    filename: "sample.wav",
    contentType: "audio/wav",
  },
});
let generatedVoiceId = clonePreview.generatedVoiceId;

// Option B — design preview from a text description (no audio)
const design = await client.voices.createDesignPreview({
  voiceDescription: "Warm documentary narrator with clear articulation.",
});
generatedVoiceId = design.previews[0].generatedVoiceId;
```

选项C保留现有的声音，并使其说另一种语言。 本地化作为后台作业运行：启动它，然后轮询，直到状态为 `ready`. `name` 默认为源语音名。

```ts
const job = await client.voices.createLocalizePreview({
  voiceId: firstVoiceId,
  languageCode: "es",
  name: "Documentary narrator (Spanish)",
});

let localized = await client.voices.getLocalizePreview(job.generationJobId);
while (localized.status !== "ready") {
  if (localized.status === "failed" || localized.status === "cancelled") {
    throw new Error(localized.error?.detail ?? localized.status);
  }
  await new Promise((resolve) => setTimeout(resolve, 2000));
  localized = await client.voices.getLocalizePreview(job.generationJobId);
}

generatedVoiceId = localized.generatedVoiceId!;
```

Voice Remix从拥有的或官方的声音中创建一个相同语言的候选人 以及自定义描述或预设。每个成功的候选人都使用语音设计候选人 价格。源不变。

```ts
const remixJob = await client.voices.createRemixPreview({
  voiceId: firstVoiceId,
  prompt: "Make the voice warmer and more expressive.",
  guidanceScale: 4,
});
let remix = await client.voices.getRemixPreview(remixJob.generationJobId);
while (remix.status !== "ready") {
  if (remix.status === "failed" || remix.status === "cancelled") {
    throw new Error(remix.error?.detail ?? remix.status);
  }
  await new Promise((resolve) => setTimeout(resolve, 2000));
  remix = await client.voices.getRemixPreview(remixJob.generationJobId);
}
generatedVoiceId = remix.generatedVoiceId!;
```

使用 `streamPreview` 和 `savePreview` 使用生成的ID进行试听和保存 私人的、独立的声音。客户端超时使服务器作业继续运行。

自定义输入被增强为源代码中的指令和匹配脚本 语言。列出模板 `client.voices.listRemixPresets()` 并通过 `presetId` 而不是 `prompt` 用一个。编辑模板得到增强； 只有当源语言不同时，才会翻译未更改的模板。

要比较优势，打电话 `client.voices.prepareRemixPreview(...)` 第一次和 通过它 `preparationToken` 每个具有相同输入的create调用。令牌 持续24小时。数值2 / 4 / 6 / 8代表稳定/富有表现力/创造性/强烈； 4为默认值。Web Auto比较4、6和8的候选项。 使用 `options.headers["Idempotency-Key"]` 要安全地重试不确定的提交， 每个候选人都有一个不同的键。状态包括 `comparisonAudioUrl`， `comparisonText`, `sourceVoice`． 节省不 `voiceName` 或者 `voiceDescription` 使用源值。 看到 [语音混音指南](https://docs.breezeblue.ai/guides/voice-remix).

对于最快的第一个音频，生成一个设计预览作为一个活24千赫单声道 PCM的回应。的节点 `stream()` Helper直接使用响应体； 干净的结束方式 `generatedVoiceId` 准备保存：

```ts
import { stream } from "@breeze.blue/sdk/node";

const livePreview = await client.voices.streamDesignPreview({
  voiceDescription: "Warm documentary narrator with clear articulation.",
  text: "This is a short preview script.",
});
await stream(livePreview);
generatedVoiceId = livePreview.generatedVoiceId!;
```

`files` 也只接受一个项目。

下载一个完整的预览，以便用户可以试听，然后保存他们的一个 选择:

```ts
const audio = await client.voices.streamPreview(generatedVoiceId);
await save(audio, "preview.mp3");

const saved = await client.voices.savePreview({
  generatedVoiceId,
  voiceName: "Documentary narrator",
  languageCode: "en",
  gender: "neutral",
  age: "middle_aged",
  tone: ["warm", "articulate"],
  accent: "american",
  tags: ["narration", "novel=三体"],
});
```

保存预览对您的帐户是私有的。编辑语音的元数据可以保持其可见性。

编辑已保存语音的名称、描述、标签、语音元数据或删除：

```ts
await client.voices.edit(saved.voiceId, {
  name: "Renamed narrator",
  languageCode: "en",
  tone: ["calm", "measured"],
});
// null clears nullable metadata; [] clears tone. Omitted fields remain unchanged.
await client.voices.edit(saved.voiceId, { accent: null, tone: [] });
await client.voices.updateTags(firstVoiceId, ["narration", "novel=三体"]);
await client.voices.updateTags(firstVoiceId, []); // Resume owner tag inheritance.
await client.voices.delete(saved.voiceId);
```

语音元数据使用稳定的代码值。 `gender`， `age`, `accent` 可能是 `null`； `tone` 包含最多三个不同的代码。中英文用法 不同的重音码集，与其他语言要求不同 `accent: null`. 呼叫 `client.voices.metadataOptions()` 去发现每一个被接受的代码和 运行时的语言到口音级联。

## 历史、模型和账目

```ts
const models = await client.models.list();
const balance = await client.account.balance();
const entitlements = await client.account.entitlements();
console.log(entitlements.planCode, entitlements.maxConcurrentGenerations);
const currentKey = (await client.account.currentApiKey()).apiKey;
console.log(currentKey.status, currentKey.creditRemaining);
const usage = await client.account.usage({ days: 7 });
const keyUsage = await client.account.usage({
  apiKeyId: "key_01hprod",
  clientType: "sdk",
});

const history = await client.history.list({ pageSize: 10 });
const item = await client.history.get(history.history[0].historyItemId);
const audio = await client.history.downloadAudio(item.historyItemId);
```

## 错误

API失败抛出 `BreezeBlueAPIError` 子类:

-   `BreezeBlueAuthenticationError`
-   `BreezeBlueBadRequestError`
-   `BreezeBlueConflictError`
-   `BreezeBlueForbiddenError`
-   `BreezeBlueNotFoundError`
-   `BreezeBlueValidationError`
-   `BreezeBlueRateLimitError`
-   `BreezeBlueInsufficientCreditsError`
-   `BreezeBlueUpstreamError` 对于上游发电/存储/模型故障， 常见的502/504
-   `BreezeBlueServiceUnavailableError` 容量或服务配置 失败，通常是503次

```ts
import { BreezeBlueClient, BreezeBlueRateLimitError } from "@breeze.blue/sdk";

try {
  await new BreezeBlueClient().models.list();
} catch (error) {
  if (error instanceof BreezeBlueRateLimitError) {
    console.log(error.headers.get("retry-after"));
  }
}
```

每个API错误都会暴露 `status`， `code`， `detail`， `meta`, `headers`.

克隆预览会自动用检测到的参考音频语言生成一个简短的脚本。它们接受参考样本、名称和可选的描述。将预览保存为语音，然后对自定义脚本、说明或目标语言使用“文本到语音”。

### HTTP TTS速度

通过 `voiceSettings: { speed: 1.25 }` 在不改变音高的情况下控制语速，从0.5到2.0。遗漏总是意味着1.0。同步、异步和HTTP流支持此参数；而实时则不然。

通过 `voiceSettings: { volume: 1.5 }` 应用请求级线性幅度乘法器。取值范围从0.01到2.0；遗漏意味着1.0。高于1.0的值可能会削去峰值。同步、异步和HTTP流支持此参数；而实时则不然。

## 词时机

流音频和单词/令牌时间戳 `client.textToSpeech.streamWithTimestamps(...)`。使用 `for await` 使用包含以下内容的块 `audioBase64` 和 `wordTimestamps`;中断迭代将关闭流。分别解码音频并合并重复的单词索引。看到 [演讲时间](https://docs.breezeblue.ai/guides/speech-timing) 获取完整的示例和计时语义。

要获得完整的响应，请使用 `client.textToSpeech.convertWithTimestamps(...)`． 它返回base64音频、其内容类型和完整的单词/令牌时间戳列表。在保存或播放之前解码音频。看到 [演讲时间](https://docs.breezeblue.ai/guides/speech-timing) 和 [使用时间戳进行转换](https://docs.breezeblue.ai/api-reference/text-to-speech/convert-with-timestamps).

对于带有时序的后台生成，使用 `client.textToSpeech.createJobWithTimestamps(...)`． 调查与 `client.generationJobs.get(jobId)`;现成的工作包括 `wordTimestamps` 以及音频下载网址。

克隆预览创建接受省略的名称。服务器使用文件名干或 `Cloned Voice`;保存没有名称的克隆预览将保留其预览名称。其他预览类型仍然需要保存名称。长度超过80个Unicode字符的显式名称将被拒绝。

## 工作室管理

`client.studio` 公开 `projects`， `chapters`， `clips`， `tracks`， `instances`, `files`． 导入已有语音id的脚本，编辑继承的指令和静音，上传MP3/WAV素材，整理背景音频。项目写入需要最新的编辑令牌和UUID操作ID。冲突保留错误元数据，不会自动重试。

看到 [工作室初始化指南](https://docs.breezeblue.ai/guides/studio-projects) 对于一个完整的工作流。签名上传使用单独的请求而不需要API密钥。管理操作不生成语音或导出项目。

`client.studio.files.upload(blob, { operationId, name, projectId })` 创建文件记录，上传到签名URL，完成上传并等待 `ready`;失败拒绝 `BreezeBlueStudioFileError`． 时间线位置、修剪和长度是48 kHz帧；使用 `secondsToFrames` 和 `framesToSeconds` 转换。

## 常见问题

### @breeze\.blue/sdk 是什么？

微风蓝开发者API的ESM-first TypeScript SDK。

### 如何安装 @breeze\.blue/sdk？

在项目目录运行 npm install @breeze\.blue/sdk，或使用 pnpm add / yarn add / bun add。

### @breeze\.blue/sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @breeze\.blue/sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @breeze\.blue/sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @breeze\.blue/sdk 可以商用吗？

@breeze\.blue/sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @breeze\.blue/sdk 还在维护吗？

最新版本 0\.23\.0 发布于 2026/11/01（2 天前）；至今共发布 34 个版本，其中过去 12 个月发布了 34 个；首次发布于 2026/05/13。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

