stepfun-tts
Generate Chinese / Japanese speech with StepFun's stepaudio-2.5-tts — Contextual TTS that replaces step-tts-2's `voice_label` with natural-language `instruction` (≤200 chars) plus inline `()` parentheses for句内 prosody. Use when the user wants emotional / prosody control over voice synthesis (whisper, pause, stress, mood pivot mid-sentence), batch-generates game / app voice lines, migrates from `step-tts-2` (the `voice_label → instruction` breaking change), or hits StepFun's stricter 2.5-era censorship (死/消失/political terms). Triggers on 阶跃 TTS, StepAudio 合成, 语音合成, 配音, 文本转语音, TTS 升级, 迁移 step-tts-2. For transcription with the sibling stepaudio-2.5-asr model, use the stepfun-asr skill instead.
1How It Works & Mechanism
The core knowledge centers on two major changes in the 2.5 era: voice_label is no longer accepted (replaced by natural-language instruction ≤200 characters + inline () prosody instructions within the text), and censorship is stricter (words like 死/消失/sensitive political terms trigger censorship_block).
**What `stepaudio-2.5-tts` will NOT accept** — `voice_label` parameter. Error: `voice_label is not supported for v2 models`. This is the #1 migration gotcha from step-tts-2.
The script sends a request to /v1/audio/speech: JSON body (model/input/voice/response_format/speed/volume + optional instruction), Authorization Bearer, and directly writes the mp3 bytes to disk upon success.
body: dict[str, Any] = { "model": MODEL, "input": text, "voice": voice, "response_format": response_format, "speed": speed, "volume": volume, }
Hard validation on instruction length + inline () prosody passed via text: Bracketed contents within the sentence, such as (试探着问)/(轻声)/(停顿一下), are consumed by the model as performance directions rather than read aloud.
if instruction: if len(instruction) > 200: return {"ok": False, "status": 0, "err": f"instruction too long: {len(instruction)} > 200 chars"}
Line-by-line fallback for censorship_block: Detects 'censorship_block'/'blocked' to mark failed lines; batch mode logs skipped IDs and proceeds without interrupting the entire batch.
censored = "censorship_block" in raw or "blocked" in raw.lower()
Batch processing: --batch reads JSONL ({id, text, instruction?} one object per line) and synthesizes line by line into --out-dir/<id>.mp3, sleeping delay-ms between lines to avoid rate limits.
out_path = args.out_dir / f"{line_id}.mp3" if result["ok"]: out_path.write_bytes(result["audio_bytes"])
Non-destructive A/B testing is a design invariant: Regenerated voice lines are written to a parallel directory (voice/zh_v25/) without ever overwriting production assets; ab_compare.sh uses ffprobe/stat to compare mp3 file size and duration across both directories, outputting a markdown table.
1. **Non-destructive A/B output** — when regenerating a corpus with a new model, write to a parallel directory (`voice/zh_v25/`), never overwrite the production corpus.
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| network | api.stepfun.com(StepAudio TTS 端点) |
| network | platform.stepfun.com(取 key/定价/voice 文档,仅指引) |
| cli | python3(stdlib urllib,无第三方包) |
| cli | ffprobe(ffmpeg,ab_compare 用) |
4Recommended Governance & Evidence
- scripts/: Execute local script or command: daymade-audio/stepfun-tts/scripts/tts_generate.py
- 网络端点: Declared external network access: POST https://api.stepfun.com/v1/audio/speech (request body containing text to synthesize + instruction + Auth)
- 本地工作目录: File read/write and artifact output: tts_generate.py writes --out (single line) or --out-dir/<id>.mp3 (batch, mkdir parents) to user-specified paths
5Second-Pass Verification
- [ok] 端点/body/模型名(/v1/audio/speech + stepaudio-2.5-tts) — tts_generate.py API_URL/MODEL 常量与 body dict 逐字存在;api_reference.md 同。
- [ok] voice_label 拒绝处理(迁移 gotcha) — 脚本对 raw 含 'voice_label is not supported' 时追加 HINT 文案;SKILL.md 错误表同描述。
- [ok] instruction ≤200 硬校验 — synthesize() 内 'if len(instruction) > 200: return {ok: False, status: 0, err: ...}' 存在,超长不发请求。
- [ok] censorship 逐行降级(不整批失败) — censored 列表单独收集、failed 汇总、循环 continue;main 返回码只按 failed 定。
- [ok] API key 解析(env → CLAUDE_PLUGIN_DATA/config.json) — load_api_key 顺序与 asr 版一致(env 优先、config 兜底、fail-fast)。
- [ok] A/B 脚本无网络、本地文件对比 — ab_compare.sh 仅 comm/stat/ffprobe;--noproxy/网络 token 零命中。
- [ok] 功能声明 vs 夸大检查(voice cloning 未验证声明) — known_issues.md 第 56 行明确 'Zero-shot voice cloning (9.9 元/音色) … was not verified in this skill's test pass'——SKILL.md 也未把克隆列为核心能力,无夸大。
- [ok] 元数据(license/stars/commit) — 仓库级 MIT、stars=1385、pushed_at=2026-09-09T12:33:29Z;本地 HEAD==pin d5c4678cb5d4fd6acc9c922690df035dbd33d247。
6Verdict & Scenarios
f7fa276a8ae68d1a…