Models & Providers

dsh-fish-tts

MaRi23333/dsh-fish-tts · ★ 2

MaRi23333

Reads assistant replies aloud via Fish Audio API only (bring your own key): per-message read-aloud, auto-read toggle, and a settings page for model, voice reference_id, encrypted API key, and proxy.

License
MIT
Added
2026-08-21
Maintenance
active
Install kind
github
npm
dsh-fish-tts

No manifest check is available; listing does not imply compatibility or security review.

安装

dsh plugin --profile web add github:MaRi23333/dsh-fish-tts

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: en · Read Chinese README

Table of contents
  1. 30-second comparison vs. typical Edge TTS plugins
  2. Features
  3. Screenshots
  4. Host and desktop compatibility
  5. Install
  6. Desktop Installation (Recommended)
  7. Web Installation
  8. Verify the install
  9. Save and switch your usual voices
  10. Configuration
  11. Config keys
  12. Audio generation (fish_tts_speak)
  13. Security
  14. Develop
  15. License
  16. Compliance & Third-Party Notice

English | 中文

A third-party text-to-speech (TTS) plugin for the DeepSeek Harness (DSH) Web GUI: one-click read-aloud for every assistant reply, an auto-read toggle in the composer, and configurable model / voice / encrypted API key / proxy. Fish Audio API only — bring your own API key. Works with any voice id (reference_id) you are authorized to use, including voices you cloned on Fish Audio. The UI is bilingual (English / 中文, follows the DSH locale).

30-second comparison vs. typical Edge TTS plugins

dsh-fish-tts (this plugin) typical Edge TTS plugins
Engine Fish Audio official API (only; bring your own key) Microsoft Edge built-in voices
Voice Your own reference_id (incl. voices you cloned, must be authorized) Fixed Edge voice library
API key Required (AES-256-GCM encrypted in the settings page) None
Best for Users with a Fish Audio account who want their own or cloned voices Quick free trials with fixed voices

Features

  • Read-aloud action: every finalized assistant message gets a speaker button in its action strip (same icon style as the native actions). Click to synthesize and play that reply; click the same message's button again while playing to stop (no restart from the top), clicking another message's button switches playback straight over, and a second click while synthesis is still in flight cancels it. Markdown is cleaned before speaking: paths, URLs, long ids and code blocks are replaced with placeholders instead of being read out.
  • Auto-read: a small speaker toggle in the composer tool row (synced with the settings page). When enabled, new replies that arrive while the DSH interface is open are read automatically.
  • Saved voices: bookmark voice IDs with readable names and multiline notes, then select a name to switch immediately. Bookmarks stay on this machine; switching preserves other unsaved settings, and removing a bookmark does not clear the current voice.
  • Audio generation tool fish_tts_speak: lets agents generate audio files from spoken lines; when the session has a chat channel attached (e.g. an IM bot), the file is handed to the channel plugin for delivery as a voice bubble. Line cleaning, per-conversation rate limiting, server-side loudness normalization and the file lifecycle all live in this plugin; Fish emotion and sound bracket tags ([soft], [laughing], …) pass through verbatim and are never read out. Channel plugins register a delivery hook through ctx.fishTts.registerDelivery; outbound filtering stays channel-side.
  • Settings page (Settings → Voice (Fish TTS)):
    • TTS model (datalist suggestions + free text; e.g. s2.1-pro-free / s2.1-pro / s2-pro; saved values apply immediately; default s2.1-pro-free)
    • Voice reference_id (enter, edit or clear it directly without saving a bookmark; synthesis requires a valid ID, and the plugin ships no default voice)
    • API key (AES-256-GCM encrypted in $DSH_HOME/fish-tts/settings.json on this machine; key.bin is generated once and ACL-tightened on Windows; the key never appears in any GET response, log line or the repository)
    • HTTP proxy (e.g. http://127.0.0.1:7890, leave empty for direct)
    • Audio generation (agent calls): enable toggle, format (mp3/wav/opus), max chars per message, max sends per minute (0 disables sending), server-side loudness normalization toggle, volume trim (-20..+20 dB) — a collapsed group at the end of the settings page; if you do not use it you can ignore it entirely
    • Test / stop test playback, auto-read toggle, volume slider (default 60%, affects playback inside DSH only, not the generated files), playback-speed slider (0.5–2.0×, pitch-preserving; fixed at 1× where the environment lacks support)

Screenshots

Host and desktop compatibility

On 2026-09-30, the development team reported that dsh-fish-tts 0.2.11 works with DSH 0.2.0-rc.2 and the desktop client of the same version. The plugin uses the Web client interface; its desktop UI does not require a separate desktop-specific package.

0.2.12 adds English and Chinese names and descriptions to the plugin manager, following the client language. Synthesis and settings are unchanged. See CHANGELOG.md for update notes.

Version 0.2.13 adds saved voices, redesigns the settings flow and appearance, and fixes playback ownership, settings failure recovery and test feedback. Stopping or switching cancels the interface's request, but does not guarantee that Fish has stopped processing it or charging for it.

This statement reflects maintainer usage feedback, not verification of every operating system or Fish model. Online synthesis still requires a valid API key, an available voice and network access. Host interface changes require renewed validation.

Install

Desktop and Web have separate plugin installations and activation states. Choose the method matching your environment:

Install directly within the desktop client UI (no command-line setup required):

  1. Open Plugins in the desktop left sidebar, then click Add plugin;
  2. Enter the npm package name dsh-fish-tts@latest and install;
  3. Once installation completes, click Enable now (closing the dialog without enabling leaves the plugin installed but disabled);
  4. Only if the UI indicates a restart is required, fully quit the application via the menu (Quit) and reopen it (closing the window may only hide it to the background);
  5. Verification: Open Settings and confirm Voice (Fish TTS) appears in the settings sidebar.

If you prefer using the command line, use the desktop-bundled CLI:

  1. In the desktop application menu, select Manage dsh Command… → Install (register the bundled CLI and follow any reported PATH conflict instructions), then open a new terminal;
  2. Ensure the desktop app has been launched at least once to initialize, and fully exit via Quit;
  3. Run the desktop-profile commands in your terminal to install and verify:
    dsh plugin --profile desktop add dsh-fish-tts@latest
    dsh plugin --profile desktop list
    
  4. Reopen the desktop application.

Note: Standalone dsh installed via npm or run via npx cannot manage the desktop profile. For details on how the bundled command runtime works, see bundled-command-runtime in the official desktop documentation.

Web Installation

Manage Web plugins using the matching CLI. Prefer using the same host-matched dsh CLI that launches your Web instance:

dsh plugin --profile web add dsh-fish-tts@latest

Then restart dsh web (stop the process, run dsh web again), refresh the page, and open Settings → Voice (Fish TTS).

Other install sources:

# From GitHub (the repository includes lib artifacts; no local build needed)
dsh plugin --profile web add github:MaRi23333/dsh-fish-tts

# From a local checkout
git clone https://github.com/MaRi23333/dsh-fish-tts.git
cd dsh-fish-tts
pnpm install && pnpm run build
dsh plugin --profile web add /absolute/path/to/dsh-fish-tts

The repo commits lib/ build artifacts, so git installs need no local build; after changing sources run pnpm run build and restart.

Note: If using npx, pin it to the exact version matching your Web host (e.g. npx @deepseek-ai/dsh@<version>). When switching from the GitHub install to npm, a bare add dsh-fish-tts is a silent no-op when the git version is already installed (pnpm considers the same-name dependency satisfied). Use dsh plugin --profile web add dsh-fish-tts@latest instead, or remove first and then add.

When installing a freshly published npm version, pnpm's supply-chain protection may automatically add minimumReleaseAgeExclude: [dsh-fish-tts@…] to the profile's pnpm-workspace.yaml. This is expected and harmless.

Verify the install

  1. Open Settings → Voice (Fish TTS);
  2. Enter your API key under Connection settings, then click Save connection settings;
  3. Enter the voice reference_id under Voice ID, then click Apply voice (no name or bookmark required);
  4. Click Preview current voice — hearing the test sentence in your voice means the install works.

Preview current voice uses the saved voice and connection settings without saving any form drafts. It requests preview audio and calls the Fish Audio API when no cached result is available. A voice and an available key are required; pending or playing previews can be stopped.

Save and switch your usual voices

Voice ID is always visible. Paste or edit it and click Apply voice, or empty the field and click Clear voice to stop using the current voice. These actions update only the current ID, require no bookmark and leave saved voices unchanged. Apply any pending ID changes before previewing.

Bookmarks are optional shortcuts. A matching saved voice displays its name and note. Select a name under Switch voice to switch immediately and update the ID field. Save voice or Name the current voice opens a separate form for a name, ID and optional note; Add and use saves and selects it. Existing voice IDs are preserved, and up to 100 voices can be saved.

Expand Manage saved voices to use, edit or remove a voice. Editing changes only its name and note without switching the active voice. Removal requires confirmation and does not clear the current voice. Adding, editing and switching leave connection drafts untouched. Connection settings have their own save button. Playback preferences need no save button; volume and speed apply to subsequent read-aloud playback.

Configuration

First run: open Settings → Voice (Fish TTS), save the connection settings (Fish Audio API key, model and optional proxy), enter and apply a voice ID, then preview it. Saved settings take effect immediately — no restart required.

You may also add a config to the fish-tts row in your profile's cordis.patch.yml (settings-page values take precedence):

- id: fish-tts
  config:
    model: s2.1-pro-free
    format: wav
    stateDir: /custom/state/dir

Config keys

Key Default Description
model '' Default model (settings-page value wins)
voice '' Default voice reference_id (settings-page value wins)
format wav wav / mp3 / opus / pcm
apiKey '' Usually empty; the encrypted settings-page key wins, then env FISH_API_KEY
apiKeyFile '' Read FISH_API_KEY from a dotenv file
proxy '' HTTP(S) proxy (settings-page value wins)
stateDir $DSH_HOME/fish-tts Settings / key-file directory

Audio generation (fish_tts_speak)

An audio-file generation tool for agents: the model passes an utterance and this plugin runs clean → synthesize → write to disk. When the session has a chat channel attached, the channel's delivery hook takes over the send; outbound filtering and target-session resolution stay channel-side:

// inside the channel plugin (any channel id; the example uses "im")
const fishTts = ctx.get('fishTts')
ctx.effect(() => fishTts.registerDelivery('im', {
  // optional: claim the sessions this channel owns (omit to claim everything)
  claims: (sessionId, agent) => manager.findByAgent(agent) !== undefined,
  // outbound filter → throw on hit (nothing delivered) → resolve target → send voice
  deliver: async ({ audioPath, mime, text, sessionId, agent }) => { /* … */ },
}), 'im: fish-tts delivery')

The tool is enabled by default. Calls use the configured Fish Audio key and may consume quota; turn it off in Settings if not needed. Input is { text, voice?, format? } (voice accepts a saved-voice name or a raw reference_id); output carries sizes, format, MIME and delivery status, plus a filename when undelivered — no absolute paths, no spoken content. Errors surface with code prefixes (rate-limited / text-too-long / auth / quota / network / voice-rejected, …) so the model can fall back to a text reply. The per-conversation per-minute limit (default 1; 0 disables) claims the slot before synthesis and releases it on failure. Files are removed after successful delivery or explicit rejection. Undelivered audio and files from timed-out deliveries stay in $DSH_HOME/fish-tts/out/; files older than 24 hours are cleaned up at startup or on later calls, not by a scheduled deletion. Loudness normalization runs server-side at Fish Audio (prosody.normalize_loudness; unsupported on s1 models), with a -20..+20 dB volume trim on top.

Security

  • The API key is persisted only in encrypted form (AES-256-GCM, per-machine random key.bin, 0600/ACL tightened) and never written to the repo, logs or any GET response.
  • Write routes (synthesize/config) require application/json and validate same-origin/loopback Origin, blocking cross-site form abuse.
  • Local-only: every /fish-tts/* route rejects non-loopback peers (127.0.0.1 / ::1 / ::ffff:127.0.0.1) with 403, even if the host listens on 0.0.0.0.
  • Proxy URLs with username/password are refused on save; credentialed HTTPS_PROXY/HTTP_PROXY env vars are likewise ignored (no leak, no fallback) — use a credential-less proxy or direct connection.
  • Proxy addresses, models and voices are machine-local user settings; the repo carries no personal data.
  • Synthesis text is capped at 12000 characters; results are cached in-process (max 200 entries), cleared on restart.
  • Audio-generation results do not echo absolute local paths, spoken lines, or account information. Local files live in $DSH_HOME/fish-tts/out/; logs record sizes, format, and delivery status without session identifiers. Compatible channels can send the generated file to the conversation's recipient.
  • Cancellation during delivery is handled by the channel; audio already sent cannot be recalled. A delivery timeout does not prove the send failed. Check the channel before retrying to avoid duplicate messages.

Develop

pnpm install
pnpm run typecheck
pnpm run test       # node:test suite (upstream Fish API is locally mocked, no network)
pnpm run build      # host: lib/index.js; client: lib/client.js (ModuleLoader CJS closure)
pnpm run smoke      # host entry + client ModuleLoader smoke tests
pnpm run check:pack # npm pack content whitelist check

Requires Node >= 22 (Node 20 is EOL). CI (.github/workflows/ci.yml) runs the full gate chain on Node 22 and 24 and verifies lib/ artifacts match the committed ones.

  • Host side lives in src/index.ts (Node; registers the /fish-tts/* routes and the settings store).
  • Client side lives in src/client/ (React; registers the conversation.chat.assistant-actions, conversation.input.left and settings.section slots).
  • Development dependencies and CI type checks still use the DSH 0.1.2-rc.1 baseline, retaining its session API and UI icon adaptations. See "Host and desktop compatibility" above for current usage feedback; if the API drifts on other versions, align with the matching tag of the deepseek-harness repo.

License

MIT

Compliance & Third-Party Notice

  • This is a third-party open-source plugin, not affiliated with, sponsored, or endorsed by Fish Audio / Hanabi AI Inc. "Fish Audio" is a trademark of its owner and is used here descriptively only.
  • The plugin does not distribute or host API keys — use your own Fish Audio account and key, and keep it safe.
  • Fish Audio's free tier is for personal, non-commercial use only; commercial use requires a paid plan. See the Terms of Use.
  • Only use voices (reference_id) you are authorized to use. Do not clone or imitate the voice of public figures, celebrities, or private individuals without permission. See the Acceptable Use Policy.
  • When distributing generated audio, disclose that it is AI-synthesized; do not mislead listeners into believing it is a real human recording.
  • Using this plugin means you agree to Fish Audio's terms; the official pages prevail if updated.

Independent community project — not affiliated with or endorsed by DeepSeek.