配置
为两个 SDK 客户端配置 base URL、超时与重试,并了解响应解包、ListenHubError 与 429 自动重试的行为。
两个 SDK 客户端都在构造函数里接收一个 options 对象。二者共用相同的选项名与相同的响应处理逻辑,但默认值不同——OpenAPIClient 面向服务端场景,超时更长。本页覆盖每一个选项、客户端为你解包的响应信封、错误如何以 ListenHubError 形式抛出、429 的自动重试,以及调用 SDK 尚未封装端点时的 client.api 逃生通道。
选项
ListenHubClient 接收 ClientOptions;OpenAPIClient 接收 OpenAPIClientOptions。所有字段都是可选的。
| 选项 | 类型 | OpenAPIClient 默认值 | ListenHubClient 默认值 |
|---|---|---|---|
apiKey | string | 环境变量 LISTENHUB_API_KEY | —(不适用) |
accessToken | string | (() => string | undefined) | —(不适用) | 无(匿名) |
baseURL | string | https://api.marswave.ai/openapi | https://api.listenhub.ai/api |
timeout | number(毫秒) | 60000 | 30000 |
maxRetries | number | 2 | 2 |
凭证因客户端而异:OpenAPIClient 用 apiKey 鉴权,ListenHubClient 用 accessToken。如何获取各自的凭证见 鉴权。
baseURL
把客户端指向特定的 API 主机。默认值已指向生产环境,所以你很少需要手动设置。当你不传 baseURL 时,每个客户端还会读取一个环境变量作为兜底:
OpenAPIClient→LISTENHUB_OPENAPI_URLListenHubClient→LISTENHUB_API_URL
显式传入的 baseURL 选项始终优先于环境变量。
import { OpenAPIClient } from '@marswave/listenhub-sdk';
const client = new OpenAPIClient({
baseURL: 'https://api.marswave.ai/openapi',
});accessToken(ListenHubClient)
accessToken 接收一个静态字符串,或一个 getter 函数 () => string | undefined。当你传入 getter 时,SDK 会在每次请求前调用它,因此一个长生命周期的客户端无需重建就能始终发送当前的 token。当 token 会随时间刷新时,推荐使用这种形式。
import { ListenHubClient } from '@marswave/listenhub-sdk';
// 静态 token——适用于短生命周期的客户端
const client = new ListenHubClient({ accessToken: tokens.accessToken });
// getter——每次请求前调用,返回你手头最新的 token
const live = new ListenHubClient({ accessToken: () => store.accessToken });token 刷新的生命周期(何时以及如何换取新 token)见 鉴权。
apiKey(OpenAPIClient)
apiKey 是服务端场景的凭证。可以显式传入,也可以省略并让构造函数从环境读取 LISTENHUB_API_KEY。两者都没有时,构造函数会抛出异常。
import { OpenAPIClient } from '@marswave/listenhub-sdk';
const client = new OpenAPIClient({ apiKey: process.env.LISTENHUB_API_KEY });API key 是拥有账户完整权限的密钥。请放在服务端——绝不要打进浏览器或移动端代码。面向用户的应用请改用带 OAuth 的 ListenHubClient,让每个请求都在用户自己的账户下执行。
timeout
单次请求的超时时间,单位毫秒。超过该时长的请求会被中止并 reject。OpenAPIClient 默认 60000(60 秒),因为服务端的生成调用可能耗时较长;ListenHubClient 默认 30000(30 秒)。
const client = new OpenAPIClient({ timeout: 120_000 }); // 2 分钟超时作用于 HTTP 请求本身,而非长耗时的生成任务。生成是异步的——创建调用会很快返回一个 episodeId 或 taskId,随后你轮询结果。见 快速开始 中的创建并轮询循环。
maxRetries
客户端在放弃前对 429 Too Many Requests 响应的重试次数。默认 2。设为 0 可禁用重试。只有 429 会被重试——其他错误状态会立即抛出。见下文 429 限流重试。
const client = new OpenAPIClient({ maxRetries: 0 }); // 快速失败,不重试响应解包
每个 ListenHub 响应都包裹在一个信封中:
{ "code": 0, "message": "", "data": { } }code 为 0 表示成功;任何非零 code 都是错误。两个客户端都会在 afterResponse hook 中为你处理这个信封,因此你的代码可以直接使用 data:
code 0时,方法 resolve 为data——你永远看不到那层包装。code非零时,方法抛出一个ListenHubError,携带信封中的code、message与request_id。204 No Content响应(以及任何非 JSON 的 body)会原样透传。
// SDK 直接返回 data——无需剥离信封
const { items } = await client.listSpeakers({ language: 'en' });少数方法返回原始的 Response 而非解包后的 JSON,因为它们是流式或二进制——例如 TTS 音频(tts、audioSpeech)以及 text-stream 端点。这些是例外,其余方法都 resolve 为 data。
错误处理
当请求失败时——凭证被拒、校验错误、非零 code,或 HTTP 错误状态——方法会抛出一个 ListenHubError。捕获它并检查其字段:
| 字段 | 类型 | 含义 |
|---|---|---|
status | number | HTTP 状态码(如 401、429、500) |
code | string | 信封中的后端错误码;非 JSON 失败时为 GATEWAY_ERROR / UNKNOWN_ERROR |
message | string | 可读的错误信息 |
requestId | string | undefined | 请求标识符——联系支持时请附上 |
import { OpenAPIClient, ListenHubError } from '@marswave/listenhub-sdk';
const client = new OpenAPIClient();
try {
const sub = await client.getSubscription();
console.log(sub);
} catch (err) {
if (err instanceof ListenHubError) {
// 来自 API 的结构化错误
console.error(`[${err.status}] ${err.code}: ${err.message}`);
if (err.requestId) console.error(`request ${err.requestId}`);
if (err.status === 401 || err.status === 403) {
// 凭证被拒——轮换 API key,或刷新 OAuth token
}
} else {
// 网络故障、超时,或其他非 API 错误
throw err;
}
}非 JSON 的失败同样会抛出 ListenHubError。网关或代理返回 HTML 错误页时,错误码为 GATEWAY_ERROR(以页面的 <title> 作为 message);其他无法解析的情形则为 UNKNOWN_ERROR。两种情况下 status 仍反映 HTTP 状态。
429 限流重试
429 Too Many Requests 响应会被自动重试——你无需自己写重试逻辑。客户端会:
- 读取
Retry-After头来决定等待多久。 - 最多重试
maxRetries次(默认2)。 - 若所有重试都用尽,抛出一个
status: 429的ListenHubError。
因此短暂的限流通常会自行恢复,根本不会以错误形式出现。如果你希望 429 立即抛出,把 maxRetries 设为 0。只有 429 会触发这条路径;其他错误状态在首个响应时就会抛出。
client.api 逃生通道
ListenHubClient 通过 client.api 暴露其底层的 ky 实例。用它来调用 SDK 尚未封装的端点——相同的鉴权头、base URL、信封解包与 429 重试仍然生效,因为你复用的是已配置好的客户端。
import { ListenHubClient } from '@marswave/listenhub-sdk';
const client = new ListenHubClient({ accessToken: tokens.accessToken });
// 路径相对于 baseURL——不带前导斜杠(ky 的要求)
const data = await client.api.get('v1/some/new-endpoint').json();
const created = await client.api
.post('v1/some/new-endpoint', { json: { foo: 'bar' } })
.json();传给 client.api 的路径相对于 baseURL,且不能以前导 / 开头——这是 ky 的要求。用 v1/...,而非 /v1/...。
逃生通道在 ListenHubClient 上可用。在 OpenAPIClient 上,优先使用类型化的方法;若你需要某个它未覆盖的端点,请查阅 OpenAPI reference,并用你自己的 HTTP 客户端、以相同的 Authorization: Bearer 头去调用。