feat(tts): add Volcengine (ByteDance) speech provider#55641
Conversation
Greptile SummaryThis PR adds Volcengine (ByteDance/豆包) as a TTS speech provider, integrating it into the existing Key changes:
Issues found:
Confidence Score: 4/5Safe to merge after fixing the missing response error handler, which can cause unhandled exceptions on network failures. The overall implementation is clean, well-typed, and consistent with the existing codebase. The only actionable bug is the missing res.on('error') listener in the HTTP client, which is a reliability gap on network fault paths. The remaining comments are style-level. One targeted fix resolves the primary concern. extensions/volcengine/tts.ts — needs the response stream error handler before merge.
|
| Filename | Overview |
|---|---|
| extensions/volcengine/tts.ts | Core TTS HTTP client — missing res.on("error") handler can cause uncaught exceptions or hung promises on response-stream failures; req referenced in timer closure before declaration. |
| extensions/volcengine/speech-provider.ts | Clean SpeechProviderPlugin implementation; credential resolution, encoding selection, and voice listing look correct. |
| extensions/volcengine/tts.test.ts | Good unit test coverage for isConfigured and listVoices; three unused imports (vi, beforeEach, afterEach) should be removed. |
| extensions/volcengine/index.ts | Adds registerSpeechProvider call alongside the existing LLM registration — correct and minimal. |
| extensions/volcengine/openclaw.plugin.json | Declares speechProviders contract and new TTS env vars correctly. |
| src/config/types.tts.ts | Adds optional volcengine block to TtsConfig with appropriate types including SecretInput for the token. |
| src/config/zod-schema.core.ts | Adds correct Zod schema for volcengine TTS config with sensitive registration on the token and range validation on speedRatio. |
| src/tts/tts.ts | Adds volcengine to ResolvedTtsConfig and resolveTtsConfig() with sensible defaults for voice and cluster. |
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/volcengine/tts.ts
Line: 73-91
Comment:
**Missing `res.on("error")` handler leaks promise**
The response stream (`res`) has no `"error"` listener. In Node.js, if an `IncomingMessage` stream emits `"error"` (e.g. due to a mid-stream connection reset), the event has no handler, which causes an uncaught exception in the process. The outer `req.on("error")` only covers request-side errors, not response-side stream errors. The promise would also hang indefinitely because neither `resolve` nor `reject` would be called.
Add an `"error"` listener on `res`:
```suggestion
(res) => {
const chunks: Buffer[] = [];
res.on("data", (chunk: Buffer) => chunks.push(chunk));
res.on("error", (e) => {
clearTimeout(timer);
reject(new Error(`Volcengine TTS response error: ${e.message}`));
});
res.on("end", () => {
clearTimeout(timer);
try {
const body = JSON.parse(Buffer.concat(chunks).toString());
if (body.code === 3000 && body.data) {
resolve(Buffer.from(body.data, "base64"));
} else {
reject(
new Error(`Volcengine TTS error ${body.code}: ${body.message ?? "unknown"}`),
);
}
} catch (e) {
reject(new Error(`Volcengine TTS: failed to parse response: ${e}`));
}
});
},
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/volcengine/tts.ts
Line: 57-61
Comment:
**`req` captured in timer closure before declaration**
`req` is referenced inside the `setTimeout` callback on line 58, but it's only declared with `const` on line 62. This works at runtime because `setTimeout` is asynchronous and `req` will always be set before the timer fires. However, it creates a temporal dead zone reference that some linters (e.g. `no-use-before-define`) may flag, and it's easy to misread.
Consider swapping the ordering — assign `req` first, then create the timer:
```typescript
const req = https.request({ ... }, (res) => { ... });
const timer = setTimeout(() => {
req.destroy();
reject(new Error(`Volcengine TTS timeout after ${timeoutMs}ms`));
}, timeoutMs);
req.write(payload);
req.end();
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/volcengine/tts.test.ts
Line: 1
Comment:
**Unused imports: `vi`, `beforeEach`, `afterEach`**
`vi`, `beforeEach`, and `afterEach` are imported from `vitest` but never used in the test file. These can be removed to avoid linting warnings.
```suggestion
import { describe, it, expect } from "vitest";
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat(tts): add Volcengine (ByteDance) sp..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e366e78233
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
e366e78 to
f75f77d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f75f77d76f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
f75f77d to
4e2b54f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8cb91c9f2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
d8cb91c to
e99babc
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca1dfe0a2f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ca1dfe0 to
84d00d5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b810a7262d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
b810a72 to
720c60e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 720c60e36f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
720c60e to
9f85d9f
Compare
|
Note on voice catalog: The current |
|
@steipete Would love to get your review on this when you have a moment. This adds Volcengine (ByteDance) as a TTS speech provider — it's the major speech API in China and provides high-quality Chinese neural voices. We're using it in production with OpenClaw on Feishu (Lark) and it works well for native voice message delivery. All CI checks are passing. Thanks! |
7f1ec14 to
6eab06e
Compare
|
Rebased/reworked this PR onto current What changed:
Local proof on pushed head
I still need a matching Volcengine Speech AppID + Access Token to do the real live API call. The ModelArk/OpenAI-compatible key is not enough for this endpoint. I tried the AppID Peter had available with the local token candidates and Volcengine returned Please provide a limited test |
|
Maintainer follow-up pushed in What changed:
Validation:
Dependency/source check: verified against BytePlus Seed Speech unidirectional HTTP docs and voice list; the live API behavior matches the API-key header flow and current default-project entitlement. |
Add Volcengine/BytePlus Seed Speech as a bundled TTS provider with current API-key auth, legacy AppID/token fallback, native Ogg/Opus voice-note output, and MP3 audio-file output. Co-authored-by: Peter Steinberger <[email protected]>
0be3479 to
115f0cf
Compare
|
Rewrote this PR branch history to one clean commit: The tree is identical to the previously validated head ( |
|
Landed via GitHub rebase onto main.
Thanks @xuruiray! |
Summary
volcengineplugin alongside the LLM provideropenspeech.bytedance.com/api/v1/tts)TtsConfig,ResolvedTtsConfig, zod schema) formessages.tts.volcengineconfigurationDetails
New files:
extensions/volcengine/tts.ts— raw Volcengine TTS API call (HTTPS, returns base64-decoded audio buffer)extensions/volcengine/speech-provider.ts—SpeechProviderPluginimplementationextensions/volcengine/tts.test.ts— unit tests for provider configuration and voice listingModified files:
extensions/volcengine/index.ts— register speech provider alongside existing LLM providerextensions/volcengine/openclaw.plugin.json— declarespeechProviderscontract and TTS env varssrc/config/types.tts.ts— addvolcenginesection toTtsConfigsrc/config/zod-schema.core.ts— add volcengine zod validationsrc/tts/tts.ts— addvolcenginesection toResolvedTtsConfigandresolveTtsConfig()Features:
messages.tts.volcengine.appId/tokenconfig orVOLCENGINE_TTS_APPID/VOLCENGINE_TTS_TOKENenv varszh_female_xiaohe_uranus_bigtts)bytedance,doubaoExample config:
Test plan
isConfigured(config vs env var fallback)listVoices(locale, gender)