Introduction¶
Most TTS plugins in the DSH ecosystem run on the Harness web version, relying on the browser’s Web Audio for sound. If the target is a mobile App’s Flutter native interface, these plugins simply won’t trigger—playback capability is tied to the browser.
27Synthesis/dsh-go-local-voice (Chinese name “JingJing Reading” 鲸鲸朗读) solves exactly this problem: it adds voice reading to the DeepSeek Harness / dsh-go mobile App native interface, using Xiaomi MiMo TTS or ByteDance Doubao Seed TTS to synthesize replies and playing them directly within the App. It falls under model inference and is maintained by 27Synthesis. Below, we introduce it in order of functionality, installation, configuration, and usage.
Core Capabilities¶
First, clarify what it manages—these are the actionable capabilities listed in the README:
- Long press any assistant reply; the menu appears with “Read Out” and “Save as Audio”; the former plays directly, the latter exports an MP3 to phone storage.
- Xiaomi MiMo uses
mimo-v2.5-tts, with 8 built-in tones (Bing Tang / Jasmine / Soda / Birch / Mia / Chloe / Milo / Dean), supporting voice commands (tone, dialect, emotion). - ByteDance Doubao Seed TTS uses
seed-tts-2.0, Voice Cloning usesseed-icl-2.0, using HTTP v3 unidirectional stream. - Long texts are read in one go: segments are prefetched in the background after a turn ends; one click delivers a whole segment of audio, not “click to read 120 characters”.
- The “Stop” button actually stops, relying on a fixed playback instance and a stable lease.
- There is a status bar above the input box (
Synthesizing voice.../Reading 0:12 / 1:40) and a progress bar in the panel. - Exported files are listed in the panel; click to copy the path.
- Markdown cleaning: code blocks, links, tables, and inline code are not read aloud; links are read as “Link”.
Installation and Enablement¶
The plugin directory is placed under $DSH_HOME/dsh-go/plugins/ (default ~/.dsh). The two commands below clone the directory and register the plugin, respectively.
git clone https://github.com/27Synthesis/dsh-go-local-voice.git \
"$HOME/.dsh/dsh-go/plugins/local-voice"
dsh plugin --profile web add "$HOME/.dsh/dsh-go/plugins/local-voice"
After installation, the engine in the App needs to be restarted once (Settings → Engine → Restart Engine) before the entry points are visible:
- Long press the assistant message; the menu appears with “Read Out” and “Save as Audio”;
- In the “Plugin” capsule above the input box, click “JingJing Reading”.
Uninstallation is the reverse:
dsh plugin --profile web remove local-voice
Then simply delete the local directory.
Configuration¶
On first use, click “Open Settings” in the main panel and enter the API Key. The fields are described in the table below.
| Item | Description |
|---|---|
| Xiaomi Key | Apply on Xiaomi MiMo console, format like sk-... |
| Doubao Key | Apply on Doubao Voice console, supports standard tones and Agent Plan |
| Doubao Voice ID | Format like zh_female_...; fill in the cloning voice ID when using Voice Cloning |
| Words per segment | Default 140. Approx 0.08s/word, 140 words ≈ 11s, stably within the production budget of 30s per single segment |
| Auto prefetch | Default ON. Synthesize the whole reply in the background immediately after it ends, so clicking “Read Out” plays almost instantly |
Keys are only stored locally in data/settings.json (permissions 0600), will not go into the repository, will not appear in the interface, and will not be sent to the model; data/ is already in .gitignore.
Typical Usage¶
After installation and configuration, there are only three steps to operate:
- Open the App and initiate a conversation.
- After the turn ends, the plugin has already segmented and synthesized concurrently (3 at a time) at 140 words per segment in the background, writing to
data/cache/<sha1>/sNNN.mp3, and merging intoall.mp3when complete. - Long press the latest reply, select “Read Out” to start playback, or select “Save as Audio” to export.
When using a cold cache, the plugin waits at most 15 seconds, first delivering the already synthesized early segments (approx 30–60 seconds of audio), and continues the rest in the background; clicking again within 10 minutes will resume reading from where it left off. The status bar and panel progress bar will display progress synchronously.
Implementation Highlights¶
For those who want to modify the code: the bridging protocol for dsh-go native plugins (v1.2) has three hard constraints that determine the implementation approach. First, the media route must produce bytes within 30 seconds (CAP_LIMITS.mediaMs); this is a production budget, not an audio duration limit. Second, the audio itself can be very long (≤16 MiB); the App gets the whole segment and plays it itself; play / stop act on the same instance. Third, the plugin cannot actively push playback effects; the return values of onCapability are not executed as effects, so “auto play the next segment after finishing” is not possible.
Based on this, the plugin splits the work into prefetch and playback: when session/event turn/end (completed), it segments and synthesizes concurrently to the cache directory; when the user clicks read, it creates a task and immediately returns {type:'audio', op:'play', instance:'read', src:'media:speech', key}. When the App comes back to GET the media route, the plugin merges the cache into a whole MP3 (MP3 raw frames can be concatenated directly, no ID3 header needed).
Regarding why the segment length is 140: the README provides empirical data (Xiaomi MiMo v2.5 TTS, non-stream MP3, mobile network): 15 words ≈ 2.4s, 60 words ≈ 4.7s, 120 words ≈ 8.3s, 240 words ≈ 38s (over budget), 400 words ≈ 32.6s, 800 words ≈ 54.9s. To ensure a single segment stably falls within the 30-second budget, the default segment length is set to 140 words.
Known Limitations¶
You need to know these boundaries before using them; they are listed in the “Known Limitations” section of the README:
- Cannot “automatically start speaking at reply end”: the protocol only allows user clicks to trigger playback.
- Xiaomi currently has no speed parameter; Doubao has
speech_rate, but it has not been exposed in the panel. - Pause / Resume: The bridged
pauseis tested to be unreliable and has been removed. - Single playback audio limit is 16 MiB (approx 5.5 minutes of audio). Long replies will continue synthesizing in the background; the next click will resume reading.
- The Doubao path has not been tested with a Key (written according to the v3 unidirectional stream interface); please try short sentences first when using it for the first time.
Additionally, several implementation constraints are mentioned in the “pitfalls” section:
- The instance for playback must be fixed (here using read); changing the instance name every time causes stop to fail to find the lease.
- The panel’s select dropdown is unreliable; the tone panel has been changed to list (one per line), clicking immediately writes settings and refreshes.
- Status must use data binding ({"$": "data.info.status"}); if text is hardcoded in the view, it won’t update even if data is pushed to the interface every second.
- The main panel only uses v1 components; items requiring input boxes are placed in the “Reading Settings” panel (v2).
Troubleshooting and Self-Check¶
When problems occur, start from these three places:
data/debug.log: records clicks, media byte counts and time,audio.ended/stopped/error, prefetch status. It does not record Keys or the body.data/settings.json: current configuration (permissions 0600).- Plugin structure self-check:
node $DSH_HOME/skills/dsh-go-plugin/kit/bin/check.mjs \
<plugin_directory> --load
Applicable Scenarios and Notes¶
Suitable for those who want to add voice to assistant replies in the dsh-go mobile native interface without relying on browser Web Audio; not suitable for scenarios requiring auto-read or pause/resume.
Two reminders: this plugin runs with the permissions of the current dsh process. You should read the source code (see repository address below) and confirm the license before installing. The plugin format and bridging capabilities come from the deepseek-harness dsh-go plugin protocol. Ideas and interface details reference the web project dsh-xiaomi-tts (MIT). This repository is a standalone implementation for the dsh-go native interface.
Directory page: https://www.skillhub.cn/plugins/27Synthesis/dsh-go-local-voice
Source code: https://github.com/27Synthesis/dsh-go-local-voice
License: MIT (Repository version 1.0.0, engines.dsh requires >=0.2.1-rc.2 <0.3.0-0).
Summary¶
The value of dsh-go-local-voice is focused: it provides a truly audible TTS path in the DSH mobile native interface, covering both Xiaomi MiMo and Doubao, and handles details like long texts, stopping, status, export, and Markdown cleaning. Installation commands and configuration fields come from the README, and functional boundaries are clearly written in the known limitations. Follow the instructions to use.