---
name: gen-voice
description: |
  Text-to-speech (TTS) skill: synthesize narration audio from text via ab-api's unified `/tool/tts` endpoint with `provider: "minimax"` (Minimax TTS).

  Use this skill immediately whenever the user asks for any of:
  - AI voice-over, TTS, text-to-speech, generate narration audio
  - Use Minimax for speech synthesis
  - Read a piece of text aloud / produce an audio file from text

  Even when the user does not explicitly say "AI", any request that turns text into speech should route here.
triggers:
  - AI voice-over, TTS, text-to-speech, generate narration audio
  - Use Minimax for speech synthesis
  - Read a piece of text aloud / produce an audio file from text
---

# Text-to-Speech (TTS) Skill

Wraps ab-api's unified `POST /tool/tts` with `provider: "minimax"` (the same endpoint the web studio uses), authenticated with the **Tianyan privateToken**, backed by the **Minimax TTS** model.

## Auth & environment

There is no skill-local env file — the executing process inherits the system environment.

- **Enterprise OpenClaw**: auth is already injected, **no need** to set `PRIV_TOKEN` / `--priv-token`.
- **Other environments**: configure the token. See the Tianyan privateToken doc internally. Without a token, non-interactive runs fail; interactive runs prompt.

| Env var | Description | Default |
|---------|-------------|---------|
| `PRIV_TOKEN` | Tianyan token; `--priv-token` overrides | none |
| `MM_API_BASE_URL` | API root; `--api-base-url` overrides | `https://api.remixmate.ai/api` |
| `AGENT_NAME` | Optional `x-invoke-agent` header | none |

## Operations

> This skill was migrated from a Python script to an remixmate CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_voice`, same params as in `skill.json`); local repro goes through `remixmate gen-voice ...`. The legacy `--download` flag has been removed — audio URLs are persisted in the cloud and play directly.

1. **Text**: confirm what to synthesize. Punctuation drives pauses (commas short, periods long).
2. **Voice**: if the user prefers a specific voice, run `--list-voices` first and pick a matching id.
3. **Output**: by default the audio URL is printed; add `--json-output` for a structured result that includes per-line subtitle timestamps.

### List available voices

```bash
remixmate gen-voice --list-voices
```

The list above is the **authoritative** catalog — a voice id is invalid only if it is missing from it.

For offline development (no token / no network) there is a `--local` escape hatch. It prints the resolver's two hardcoded fallback ids, **not** the available voices, so never use it to decide whether a voice exists:

```bash
remixmate gen-voice --list-voices --local
```

It prints one line per entry as `<voice-id>\t<lang>\t<display-name>`.

### Default synthesis (URL output)

```bash
remixmate gen-voice --text "<text-to-synthesize>"
```

### With voice + speed

```bash
remixmate gen-voice \
  --text "<text-to-synthesize>" \
  --voice-id "female-shaonv" \
  --speed 1.2
```

### JSON output (with subtitle timestamps)

```bash
remixmate gen-voice --text "<text-to-synthesize>" --json-output
```

## Common CLI flags

| Flag | Description | Default |
|------|-------------|---------|
| `--text` | Text to synthesize (required, except with `--list-voices`) | — |
| `--voice-id` | Voice id (use `--list-voices` to discover) | `Chinese (Mandarin)_Male_Announcer` |
| `--speed` | Speech rate, 0.5–2.0 | `1.0` |
| `--language-boost` | CLI-only. Language hint for MiniMax (`auto` / `Chinese` / `English` / …). Mixed-script narration ("只有四个字：Agent Loop") mispronounces the Latin terms without one, so the service already applies its own default — pass this only to pin a specific language. Usually set from the DSL (`global.narration.languageBoost`, scene-level overrides) rather than by hand. Not exposed to the agent — see the handler comment. | service default |
| `--list-voices` | List available voices and exit | — |
| `--local` | CLI-only. With `--list-voices`: print the 2-entry hardcoded fallback list instead of querying the service (offline dev). Not exposed to the agent — see the handler comment. | off |
| `--json-output` | Emit JSON (url, audio_length_ms, subtitles) | off |
| `--priv-token` | Override token | env var |

## Speed guidance

| Use case | Recommended speed |
|----------|-------------------|
| Reading / audiobook | 0.8–1.0 |
| Voice-over / narration | 1.0–1.2 |
| Fast announcement | 1.2–1.5 |

## Credits

Every run charges credits. The CLI prints a footer on stdout when it does:

```
💳 Charged 31 credits · balance 1,240
```

Relay it to the user whenever it appears — it is the only signal they get about what a
generation cost, and the balance is the only warning before a run fails with
`insufficient_credits`. Do not drop it from your summary.

## Error handling

- **401** / **token missing** (non-OpenClaw): set `PRIV_TOKEN`.
- **Business `code != 0`**: read `msg` on stderr.
- **429**: rate-limited; retry later.
- **Invalid voice id**: re-check via `--list-voices`.
- **Network**: verify connectivity and `MM_API_BASE_URL`.
