pi-voice-input 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/ROADMAP.md +1 -0
- package/extensions/voice-input.ts +26 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,9 +18,10 @@ Typing long prompts can slow you down. `pi-voice-input` lets you:
|
|
|
18
18
|
- **One-key dictation**: `Ctrl+Shift+R` starts recording; press it again to stop and insert text.
|
|
19
19
|
- **Editor-safe workflow**: transcription is pasted into the current editor only. It does not auto-submit.
|
|
20
20
|
- **Chinese/English mixed input**: handles prompts that switch between Chinese, English, product names, and technical terms.
|
|
21
|
+
- **Hotword table support**: can pass a VolcEngine boosting table ID to improve recognition of project terms.
|
|
21
22
|
- **Works on Linux and macOS**: uses common system recording tools.
|
|
22
23
|
- **Lowers sound while you speak**: automatically turns down system audio during recording, then restores it afterwards.
|
|
23
|
-
- **No hidden rewriting**: inserts the raw ASR transcript, prefixed with a short note that it may contain voice-recognition errors.
|
|
24
|
+
- **No hidden rewriting**: inserts the raw ASR transcript, prefixed with a short note that it may contain voice-recognition errors. If the editor already contains that note, later dictation inserts only the transcript.
|
|
24
25
|
- **Simple setup commands**: configure from inside pi with `/voice init` and `/voice key`.
|
|
25
26
|
|
|
26
27
|
Current speech provider: **VolcEngine Speech ASR**. A VolcEngine Speech API key is required.
|
|
@@ -98,19 +99,22 @@ Useful commands:
|
|
|
98
99
|
|
|
99
100
|
## Inserted text format
|
|
100
101
|
|
|
101
|
-
The extension does not call a model to modify or translate your transcript. It inserts a
|
|
102
|
+
The extension does not call a model to modify or translate your transcript. It inserts a concise, location-neutral caveat saying the current conversation may include voice transcription errors, asking the model to correct them from context or ask the user if the meaning is unclear, then appends the raw ASR transcript unchanged. When you dictate multiple times in the same unsent editor draft, the caveat is kept to a single copy.
|
|
102
103
|
|
|
103
104
|
User config keys are:
|
|
104
105
|
|
|
105
106
|
```json
|
|
106
107
|
{
|
|
107
108
|
"volcApiKey": "",
|
|
109
|
+
"boostingTableId": "",
|
|
108
110
|
"duckSystemVolume": true,
|
|
109
111
|
"duckSystemVolumeFactor": 0.5,
|
|
110
112
|
"duckSystemVolumeFadeMs": 300
|
|
111
113
|
}
|
|
112
114
|
```
|
|
113
115
|
|
|
116
|
+
Set `boostingTableId` to a VolcEngine hotword/boosting table ID to send it as `boosting_table_id` on ASR requests. Leave it empty to disable hotword-table boosting. Boosting table name is not configured yet.
|
|
117
|
+
|
|
114
118
|
## System requirements
|
|
115
119
|
|
|
116
120
|
Linux needs one recording tool:
|
|
@@ -135,7 +139,7 @@ On macOS, allow microphone access for your terminal or pi host app when prompted
|
|
|
135
139
|
## Troubleshooting
|
|
136
140
|
|
|
137
141
|
- Run `/voice status` to see whether recording is active.
|
|
138
|
-
- Run `/voice config` to confirm the API key
|
|
142
|
+
- Run `/voice config` to confirm the API key and optional boosting table ID are detected.
|
|
139
143
|
- Run `/voice key` again if the key was changed or expired.
|
|
140
144
|
- On macOS, check microphone permission if recording immediately fails.
|
|
141
145
|
- On Linux, make sure `pw-record` or `arecord` is installed and your microphone works in other apps.
|
package/ROADMAP.md
CHANGED
|
@@ -7,6 +7,7 @@ This roadmap lists user-visible work planned for pi Voice Input. It is intention
|
|
|
7
7
|
- Linux voice input through `pw-record` or `arecord`
|
|
8
8
|
- macOS voice input through the system `afrecord` command
|
|
9
9
|
- VolcEngine WebSocket ASR
|
|
10
|
+
- Optional VolcEngine hotword boosting table ID
|
|
10
11
|
- Raw ASR transcript insertion with a voice-input caveat wrapper
|
|
11
12
|
|
|
12
13
|
## Planned
|
|
@@ -26,11 +26,7 @@ const VOLC_API_KEY_URL = "https://console.volcengine.com/speech/new/setting/apik
|
|
|
26
26
|
// the local machine, so force those subprocesses to a known-local cwd.
|
|
27
27
|
const LOCAL_AUDIO_PROCESS_CWD = homedir();
|
|
28
28
|
const DEFAULT_SHORTCUT = Key.ctrlShift("r");
|
|
29
|
-
const VOICE_TRANSCRIPT_NOTICE =
|
|
30
|
-
"以下内容来自用户通过语音输入的原始转写,可能包含语音识别错误、错别字、同音词、断句或标点不准确,以及中英文、术语、代码名、文件名误识别等问题。",
|
|
31
|
-
"请明确注意:这段转写不一定完全准确。请结合上下文理解用户意图;必要时可以先澄清、翻译或推断,再继续处理。",
|
|
32
|
-
"原始语音转写如下:",
|
|
33
|
-
].join("\n");
|
|
29
|
+
const VOICE_TRANSCRIPT_NOTICE = "当前会话中的内容包含语音转写,可能存在识别错误;请结合上下文纠正,若不确定或明显偏离主题无法理解,请先询问用户。";
|
|
34
30
|
|
|
35
31
|
const MSG_TYPE_CLIENT_FULL_REQUEST = 0b0001;
|
|
36
32
|
const MSG_TYPE_CLIENT_AUDIO_ONLY_REQUEST = 0b0010;
|
|
@@ -46,6 +42,7 @@ type JsonObject = Record<string, unknown>;
|
|
|
46
42
|
|
|
47
43
|
type VoiceInputConfigFile = {
|
|
48
44
|
volcApiKey: string;
|
|
45
|
+
boostingTableId: string;
|
|
49
46
|
duckSystemVolume: boolean;
|
|
50
47
|
duckSystemVolumeFactor: number;
|
|
51
48
|
duckSystemVolumeFadeMs: number;
|
|
@@ -59,6 +56,7 @@ type VoiceConfig = {
|
|
|
59
56
|
language: string;
|
|
60
57
|
uid: string;
|
|
61
58
|
prompt: string;
|
|
59
|
+
boostingTableId: string;
|
|
62
60
|
segmentMs: number;
|
|
63
61
|
requestTimeoutMs: number;
|
|
64
62
|
finalizeDelayMs: number;
|
|
@@ -118,6 +116,7 @@ function ensureDir(dir: string) {
|
|
|
118
116
|
function defaultConfigFile(): VoiceInputConfigFile {
|
|
119
117
|
return {
|
|
120
118
|
volcApiKey: "",
|
|
119
|
+
boostingTableId: "",
|
|
121
120
|
duckSystemVolume: true,
|
|
122
121
|
duckSystemVolumeFactor: 0.5,
|
|
123
122
|
duckSystemVolumeFadeMs: 300,
|
|
@@ -152,6 +151,11 @@ function normalizeConfigFile(input: unknown): VoiceInputConfigFile {
|
|
|
152
151
|
const root = isObject(input) ? input : {};
|
|
153
152
|
return {
|
|
154
153
|
volcApiKey: stringField(root, "volcApiKey", defaults.volcApiKey).trim(),
|
|
154
|
+
boostingTableId: stringField(
|
|
155
|
+
root,
|
|
156
|
+
"boostingTableId",
|
|
157
|
+
stringField(root, "boosting_table_id", defaults.boostingTableId),
|
|
158
|
+
).trim(),
|
|
155
159
|
duckSystemVolume: booleanField(root, "duckSystemVolume", defaults.duckSystemVolume),
|
|
156
160
|
duckSystemVolumeFactor: clamp(numberField(root, "duckSystemVolumeFactor", defaults.duckSystemVolumeFactor), 0, 1),
|
|
157
161
|
duckSystemVolumeFadeMs: Math.round(clamp(numberField(root, "duckSystemVolumeFadeMs", defaults.duckSystemVolumeFadeMs), 0, 3000)),
|
|
@@ -185,6 +189,7 @@ function getConfig(): VoiceConfig {
|
|
|
185
189
|
language: "",
|
|
186
190
|
uid: "pi-voice-input",
|
|
187
191
|
prompt: "",
|
|
192
|
+
boostingTableId: fileConfig.boostingTableId,
|
|
188
193
|
segmentMs: 5000,
|
|
189
194
|
requestTimeoutMs: 90000,
|
|
190
195
|
finalizeDelayMs: 100,
|
|
@@ -794,6 +799,7 @@ async function transcribePcm(pcm: Buffer, durationMs: number, config: VoiceConfi
|
|
|
794
799
|
"X-Api-Resource-Id": config.resourceId,
|
|
795
800
|
"X-Api-Connect-Id": connectId,
|
|
796
801
|
"X-Api-Request-Id": connectId,
|
|
802
|
+
"X-Api-Sequence": "-1",
|
|
797
803
|
},
|
|
798
804
|
handshakeTimeout: 15_000,
|
|
799
805
|
});
|
|
@@ -871,6 +877,10 @@ async function transcribePcm(pcm: Buffer, durationMs: number, config: VoiceConfi
|
|
|
871
877
|
audioPayload.language = config.language;
|
|
872
878
|
}
|
|
873
879
|
|
|
880
|
+
const corpusPayload: Record<string, unknown> = {};
|
|
881
|
+
if (config.boostingTableId) corpusPayload.boosting_table_id = config.boostingTableId;
|
|
882
|
+
if (config.prompt) corpusPayload.context = config.prompt;
|
|
883
|
+
|
|
874
884
|
const requestPayload: Record<string, unknown> = {
|
|
875
885
|
user: { uid: config.uid || "pi-voice-input" },
|
|
876
886
|
audio: audioPayload,
|
|
@@ -881,7 +891,7 @@ async function transcribePcm(pcm: Buffer, durationMs: number, config: VoiceConfi
|
|
|
881
891
|
enable_ddc: config.enableDdc,
|
|
882
892
|
show_utterances: config.showUtterances,
|
|
883
893
|
result_type: "full",
|
|
884
|
-
...(
|
|
894
|
+
...(Object.keys(corpusPayload).length ? { corpus: corpusPayload } : {}),
|
|
885
895
|
},
|
|
886
896
|
};
|
|
887
897
|
|
|
@@ -928,9 +938,14 @@ async function transcribePcm(pcm: Buffer, durationMs: number, config: VoiceConfi
|
|
|
928
938
|
};
|
|
929
939
|
}
|
|
930
940
|
|
|
931
|
-
function
|
|
941
|
+
function editorHasVoiceTranscriptNotice(editorText: string): boolean {
|
|
942
|
+
return editorText.replace(/\r\n/g, "\n").includes(VOICE_TRANSCRIPT_NOTICE);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function wrapVoiceTranscript(rawText: string, currentEditorText = ""): string {
|
|
932
946
|
const trimmed = rawText.trim();
|
|
933
947
|
if (!trimmed) return "";
|
|
948
|
+
if (editorHasVoiceTranscriptNotice(currentEditorText)) return trimmed;
|
|
934
949
|
return `${VOICE_TRANSCRIPT_NOTICE}
|
|
935
950
|
|
|
936
951
|
${trimmed}`;
|
|
@@ -1084,7 +1099,7 @@ async function stopRecording(ctx: ExtensionContext, transcribe = true) {
|
|
|
1084
1099
|
return;
|
|
1085
1100
|
}
|
|
1086
1101
|
|
|
1087
|
-
const finalText = wrapVoiceTranscript(result.text);
|
|
1102
|
+
const finalText = wrapVoiceTranscript(result.text, ctx.ui.getEditorText());
|
|
1088
1103
|
|
|
1089
1104
|
ctx.ui.setStatus("voice-input", undefined);
|
|
1090
1105
|
insertIntoEditor(ctx, finalText);
|
|
@@ -1107,6 +1122,7 @@ function setupHelp(config = getConfig()): string {
|
|
|
1107
1122
|
"- Current provider: VolcEngine WebSocket ASR",
|
|
1108
1123
|
`- Config file: ${config.configPath}`,
|
|
1109
1124
|
`- API key: ${config.apiKey ? "set" : "missing"}`,
|
|
1125
|
+
`- Hotword boosting table ID: ${config.boostingTableId ? "set" : "not set"}`,
|
|
1110
1126
|
"- To create/update the JSON config file, run: /voice init",
|
|
1111
1127
|
"- To save/update the key, run: /voice key",
|
|
1112
1128
|
"- Output: raw ASR transcript wrapped with a short voice-input caveat",
|
|
@@ -1146,12 +1162,13 @@ function configSummary(config: VoiceConfig): string {
|
|
|
1146
1162
|
"Voice input config:",
|
|
1147
1163
|
`- config file: ${config.configPath}${existsSync(config.configPath) ? "" : " (missing; run /voice init to create it)"}`,
|
|
1148
1164
|
`- volcApiKey: ${config.apiKey ? "set" : "missing"} (update with /voice key)`,
|
|
1165
|
+
`- boostingTableId: ${config.boostingTableId ? "set" : "not set"}`,
|
|
1149
1166
|
"- outputMode: raw transcript with voice-input caveat wrapper",
|
|
1150
1167
|
`- duckSystemVolume: ${config.duckSystemVolume ? "enabled" : "disabled"}`,
|
|
1151
1168
|
`- duckSystemVolumeFactor: ${config.duckSystemVolumeFactor}`,
|
|
1152
1169
|
`- duckSystemVolumeFadeMs: ${config.duckSystemVolumeFadeMs}`,
|
|
1153
1170
|
`- current recording device: ${currentDevice}`,
|
|
1154
|
-
"Config keys: volcApiKey, duckSystemVolume, duckSystemVolumeFactor, duckSystemVolumeFadeMs.",
|
|
1171
|
+
"Config keys: volcApiKey, boostingTableId, duckSystemVolume, duckSystemVolumeFactor, duckSystemVolumeFadeMs.",
|
|
1155
1172
|
`VolcEngine API key URL: ${VOLC_API_KEY_URL}`,
|
|
1156
1173
|
].join("\n");
|
|
1157
1174
|
}
|