@gang-of-beads/pi-web-voice
PI WEB plugin for voice dictation: a Dictate button in the composer that turns speech into prompt text through Azure Speech, with the subscription key kept on the machine.
Package details
Install @gang-of-beads/pi-web-voice from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@gang-of-beads/pi-web-voice- Package
@gang-of-beads/pi-web-voice- Version
0.1.0- Published
- Oct 17, 2026
- Downloads
- 238/mo · 20/wk
- Author
- hanxiaodu
- License
- MIT
- Types
- package
- Size
- 47.3 KB
- Dependencies
- 0 dependencies · 0 peers
Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-web-voice
A PI WEB plugin for dictation: a Dictate button in the composer turns what you say into prompt text.
- Live dictation streams to Azure Speech, and words appear in the draft while you speak.
- Without streaming, the clip is recorded and sent to a speech-to-text endpoint when you stop, such as Azure Speech or a Whisper-compatible service.
- The Azure Speech key stays on the machine; the page only gets a token that expires.
Requirements
- A PI WEB release later than 2.202610.12. Earlier releases bundle their own copy of this plugin, which wins over an installed one (Settings → PI WEB plugins reports the duplicate id).
- PI WEB opened over HTTPS (or on
localhost): browsers allow the microphone only on a secure page. See remote access. - A speech-to-text service you configure, such as an Azure Speech resource.
Install
pi install npm:@gang-of-beads/pi-web-voice
The installer's Gang of Beads preset installs it too. To follow main instead of releases, install git:github.com/Gang-of-Beads/pi-web-voice.
The plugin has a server part, so restart the PI WEB session daemon, then reload the PI WEB tab. To remove it, run pi remove npm:@gang-of-beads/pi-web-voice and restart the daemon again.
Configure
The composer shows Dictate only once plugins.voice.settings names a service with an endpoint. For Azure Speech, name the service in settings and put the key in serverSettings, which no browser receives (private plugin settings):
{
"plugins": {
"voice": {
"settings": {
"endpoint": "https://westeurope.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1",
"language": "en-US",
"streaming": {
"protocol": "azure-speech",
"url": "wss://westeurope.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1"
},
"azureSpeech": { "region": "westeurope", "resource": "example-speech" }
},
"serverSettings": {
"azureSpeech": { "key": "replace-with-your-key" }
}
}
}
}
Then restart the session daemon and reload the tab; a changed settings or serverSettings block applies at the daemon's next start, and the plugin's card shows Restart required until then.
| Key | Meaning |
|---|---|
settings.endpoint |
Required. Where a recorded clip is uploaded when you stop. |
settings.language |
Sent with the clip, and with the live connection to Azure Speech, which refuses the connection without it. |
settings.model |
Sent with the clip, for services that choose a model per request. |
settings.streaming |
Makes dictation live. protocol must be azure-speech and url its socket address. Without this block, dictation records a clip. |
settings.azureSpeech.region |
The resource's region. Required for Azure Speech. |
settings.azureSpeech.resource |
The resource's custom subdomain; tokens are requested from it, or from the region's endpoint when it is absent. |
serverSettings.azureSpeech.key |
The subscription key. |
- Live protocols. Only
azure-speechstreams in this version. Astreamingblock that namesbrowser,openai-realtimeordeepgram, or has nourl, shows the reason in the composer instead of recording a clip.streaming.tokenEndpointis ignored. - Other services. An
endpointwith noazureSpeechblock, such as a Whisper-compatible service on the same host, receives the clip as a form upload (file, plusmodelandlanguagewhen set), without a credential. - Sessions. A dictation and its status line belong to the session you started it in. Switching to another session or machine stops the dictation and releases the microphone, and text that arrives afterwards goes into the draft of the session it was started in. A new session finishing its start while you dictate is not a switch: the dictation goes on in it.
Move a key out of public settings
Earlier PI WEB releases read the key from settings.azureSpeech.key. A key there still works, but every browser that opens PI WEB can read it. To move it:
- Add
"serverSettings": { "azureSpeech": { "key": "…" } }to thevoiceentry, by editing the config file or with aserverSettingsUpdateswrite. - Remove
keyfromsettings.azureSpeech. - Restart the session daemon, then reload the tab.
The old key has been readable by browsers; rotate it in Azure if that matters for your deployment.
Health
The plugin's card in Settings → PI WEB Plugins says where the key comes from:
| Card | Meaning |
|---|---|
| Healthy | The key is in serverSettings. |
| Healthy, "not configured" | settings.azureSpeech.region is not set, so Azure Speech is not used on this machine. |
| Degraded, key readable by browsers | The key, or a copy of it, is in settings.azureSpeech.key. Move it as above. |
| Degraded, no key | A region is set but serverSettings.azureSpeech.key is not. |
| Degraded, older PI WEB | The host does not pass private settings to plugins. Upgrade PI WEB. |
When no token can be had, the composer says why and sends nothing.
Privacy
Voice sends your audio only to the services you configure, and only while you dictate.
- Microphone. Pressing Dictate opens the microphone, after the browser's permission prompt; it is released when the dictation stops.
- Live dictation connects from the page straight to
settings.streaming.url. - Recorded clips are uploaded from the page to
settings.endpoint, withAuthorization: Bearer <token>when anazureSpeechblock is set. - Tokens. The server part runs in the PI WEB session daemon, keeps the key, and exchanges it for a ten-minute token at
https://<resource>.cognitiveservices.azure.com/sts/v1.0/issueToken, orhttps://<region>.api.cognitive.microsoft.com/sts/v1.0/issueTokenwithout aresource. The page asks for a token through the plugin'sspeech.tokenoperation. - On disk. The key sits in PI WEB's config file in plaintext;
serverSettingskeeps it from browsers, not from the machine.
The plugin is machine-specific: with several machines, each machine's own copy uses that machine's settings and key.
Development
npm install
npm run check
npm run check type-checks, runs the tests, and builds two bundles with esbuild: dist/browser/pi-web-plugin.js for the page and dist/server-plugin.js for the daemon. dist/ is committed so that pi install git: works without a build step, and the check fails when it is out of date.
To try a change in a running PI WEB, link the working copy as a local plugin, restart the session daemon and reload the tab:
mkdir -p ~/.pi-web/plugins
ln -s "$PWD" ~/.pi-web/plugins/voice
See PI WEB's plugin documentation for the plugin contract.
Contributing
Issues and pull requests are welcome. Please keep changes small and focused, run npm run check before opening a pull request, and describe what you changed and how you checked it in a running PI WEB.
Releasing
Update version in package.json and package-lock.json, give CHANGELOG.md a section for it, commit, and push a matching v<version> tag. The release workflow checks the package, publishes it to npm, and creates the GitHub Release from that CHANGELOG section.