← Back to Plugins
Channels

Tts Language Router

reisball By reisball 👁 134 views ▲ 0 votes

Opt-in inbound-language reply guidance and final-reply speech routing for OpenClaw 2026.8.x

GitHub

Install

openclaw plugins install npm-pack:"$PWD/openclaw-tts-language-router-0.1.0.tgz"

README

# OpenClaw TTS Language Router

[![CI](https://github.com/reisball/openclaw-tts-language-router/actions/workflows/ci.yml/badge.svg)](https://github.com/reisball/openclaw-tts-language-router/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

`openclaw-tts-language-router` is a plugin for **OpenClaw 2026.8.x**, starting
with `2026.8.1-beta.2`, with two independent layers:

1. An optional inbound layer uses the current processed user prompt to add a
   one-turn system-context instruction for explicitly selected agents.
2. The speech provider always detects the *actual final assistant reply* and
   selects its TTS backend/voice from that text.

The second layer is enabled by selecting `tts-language-router`; the first is
disabled by default. A model that does not follow an inbound instruction still
gets a voice matching what it actually wrote.

This repository is the canonical development source. The package is **not
published to ClawHub or npm yet**. For v0.1.0, build and review an npm-pack
tarball from the GitHub source, then give that artifact to OpenClaw.

## Requirements

- **OpenClaw** in the range `>=2026.8.1-beta.2 <2026.9.0`. The package is built
  and tested against the `2026.8.1-beta.2` plugin SDK and has been run against a
  `2026.8.1-beta.3` host. Compatibility outside that range is untested and not
  claimed.
- **Node.js** matching the same range OpenClaw supports: `>=22.22.3 <23`,
  `>=24.15.0 <25`, or `>=25.9.0`, plus npm 10 or newer to build and pack the
  artifact.
- **At least one OpenAI-compatible `/audio/speech` backend** you can reach from
  the OpenClaw host, plus its credential if it requires one.
- **Optional:** the system `opus-tools` package, only if you enable native
  Telegram voice-note transcoding (see below). The plugin ships no runtime
  dependency and downloads no binary.

## Install from GitHub source

Use the v0.1.0 tag to build, review, test, and install the exact npm-pack
artifact yourself:

```bash
git clone https://github.com/reisball/openclaw-tts-language-router.git
cd openclaw-tts-language-router
git checkout v0.1.0
npm ci
npm run check            # full gate, including the packed-install smoke test
npm pack                 # writes openclaw-tts-language-router-<version>.tgz

openclaw plugins install npm-pack:"$PWD/openclaw-tts-language-router-0.1.0.tgz" --force
```

`npm-pack:` uses the same managed npm project path as a registry install, so it
is the closest local equivalent to a published package. `--force` confirms the
reviewed non-ClawHub source and permits replacing an existing installation.

Do not give OpenClaw the Git repository or a GitHub-generated source archive:
neither contains `dist/`, and OpenClaw installs git sources with lifecycle
scripts disabled and development dependencies omitted. This repository keeps a
`prepare` script so `npm pack` builds the compiled files before assembling the
tarball; compiled output is not committed.

## Enable and configure

A newly installed plugin that still needs configuration stays disabled. Add the
configuration below to your OpenClaw config, then enable the plugin:

```bash
openclaw plugins enable openclaw-tts-language-router
```

```json5
{
  plugins: {
    entries: {
      "openclaw-tts-language-router": {
        enabled: true,
        // Required by OpenClaw for an external plugin that reads the prompt
        // and appends one turn-scoped system context.
        hooks: {
          allowConversationAccess: true,
          allowPromptInjection: true
        },
        config: {
          inboundLanguage: {
            enabled: true,
            agentIds: ["support-agent"]
          },
          profiles: {
            multilingual: {
              kind: "openai-compatible",
              endpoint: "https://tts.example.invalid/v1/audio/speech",
              model: "multilingual-tts-v1",
              voice: "narrator",
              // A backend that emits WAV can transcode only voice-note
              // requests to native Telegram Ogg/Opus using this host's
              // system opusenc.
              voiceNoteResponseFormat: "wav",
              voiceNoteTranscode: {
                outputFormat: "opus",
                // opusencPath: "/usr/bin/opusenc", // defaults to "opusenc"
                bitrateKbps: 32
              },
              streaming: true,
              telephony: { responseFormat: "pcm", sampleRate: 8000 }
            }
          },
          routes: {
            de: { profile: "multilingual", voice: "de-narrator" },
            en: { profile: "multilingual", voice: "en-narrator" },
            es: { profile: "multilingual", voice: "es-narrator" },
            zh: { profile: "multilingual", voice: "zh-narrator" },
            "zh-TW": { profile: "multilingual", voice: "zh-tw-narrator" }
          },
          fallback: { profile: "multilingual" }
        }
      }
    }
  },
  tts: { auto: "always", provider: "tts-language-router" }
}
```

The `hooks` permissions are only needed for the optional inbound layer. Omit
`inboundLanguage` and those permissions if you want speech routing alone.

`de`, `en`, `es`, `zh`, and `fallback` are required. `zh-CN` and `zh-TW` are
optional: a detected locale uses its explicit route when present, otherwise it
uses the required base `zh` route. Global `fallback` is reserved for ambiguous
or unsupported text.

A route contains `profile`, plus optional `model`, `voice`, and non-secret
request `overrides`. `model` and `voice` override profile defaults. A profile
can set optional headers and request overrides, declares `streaming: true` to
enable response streaming, and declares a PCM or μ-law telephony format plus
sample rate to enable telephony.

Keep credentials in the profile, not in routes, and prefer an
environment-variable reference where your host supports one.

## Verify

Installing or removing plugin code needs a Gateway restart; a managed Gateway
with config reload restarts itself.

```bash
openclaw gateway restart                # only if your Gateway is unmanaged
openclaw plugins list --json | jq '.plugins[] | select(.id == "openclaw-tts-language-router")'
openclaw plugins inspect openclaw-tts-language-router --runtime --json
```

`plugins list` is a cold inventory check. `inspect --runtime` is the one that
proves the running module registered its surfaces: expect the
`tts-language-router` speech provider, and — only when `inboundLanguage.enabled`
is `true` — a `before_prompt_build` hook. Then send a message in a language you
routed and confirm the reply audio uses that language's voice.

## Update

For a later GitHub-source version, check out its reviewed tag, repeat the build,
checks, and `npm pack` steps above, then install the newly named tarball with
`npm-pack:` and `--force`. Restart the Gateway if it is unmanaged and repeat the
runtime verification. The generic `plugins update` command is not the update
path for this locally built artifact flow.

## Uninstall

```bash
openclaw plugins uninstall openclaw-tts-language-router --dry-run
openclaw plugins uninstall openclaw-tts-language-router
```

This removes the install record, the plugin's config entry, and its managed
files. Afterwards, point `tts.provider` at another provider (or remove the
setting) so speech synthesis has a valid target.

## API decision

This plugin does **not** delegate to arbitrary registered OpenClaw speech
providers. In the 2026.8.x API, provider registration (`api.registerSpeechProvider`) and
the provider type are public contracts, but the registry lookup/delegation
helpers are private-local SDK surfaces. This package therefore has no registry
lookup, no private SDK subpath imports, and no copied internal helpers.

Each backend is an explicit `openai-compatible` profile containing the full
`/audio/speech` endpoint, its credential (if any), defaults, and declared
streaming/telephony capabilities. Routes reference profile names, so a shared
credential or endpoint is configured once rather than repeated per language.

## Native Telegram voice notes (optional)

By default, voice notes retain the backend response exactly as before. To
produce a native Telegram voice message from a backend that returns WAV, set
both `voiceNoteResponseFormat` (the backend source format) and
`voiceNoteTranscode`. The source format must be `wav`; the latter has one fixed
`outputFormat: "opus"`, an optional direct executable `opusencPath` (default
`opusenc`), and an optional `bitrateKbps` from 6 to 128 (default 32). The plugin
buffers the source in a private temporary directory and calls `opusenc` directly
with fixed argv to emit a mono Ogg/Opus speech stream without copying WAV
comments. Opus
uses a 48 kHz codec clock, but decoders determine their actual presentation rate;
the plugin makes no stronger decoded-rate promise. It reports
`outputFormat: "opus"`, `.opus`, and `voiceCompatible: true` to OpenClaw.

This path requires the host's system `opus-tools` package (on Ubuntu 24.04,
`opus-tools` plus its codec/container libraries). The plugin adds no production
dependency, does not download a binary, and never uses a shell. Its unique
process-owned temporary directory is removed on success, failure, timeout, and
cancellation. The buffered path caps backend source audio at 16 MiB, generated
audio at 2 MiB, and `opusenc` diagnostics at 16 KiB. A missing/unexecutable
`opusenc`, encoding failure, timeout, invalid/empty media, or cap violation
fails with the normal sanitized provider error—there is no WAV fallback and no
backend retry. The complete request timeout covers backend receipt, buffering,
and conversion. Streaming voice-note synthesis is rejected while this option is
enabled because a streaming converter is not implemented; audio-file streaming
and telephony behavior are otherwise unchanged.

## Inbound response-language guidance

Set `inboundLanguage.enabled: true` only with a non-empty `agentIds` allowlist.
The public 2026.8.x hook prov

... (truncated)
channels

Comments

Sign in to leave a comment

Loading comments...