@flowingspring/dsh-voco 0.2.2 → 0.2.3
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/lib/client.js +24 -24
- package/lib/plugins/voice-assistant.js +80 -21
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -19,8 +19,8 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
}
|
|
20
20
|
var VoiceControl_module_css_default = {
|
|
21
21
|
"voice-pulse": "PapC8q_voice-pulse",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"active": "PapC8q_active",
|
|
23
|
+
"button": "PapC8q_button"
|
|
24
24
|
};
|
|
25
25
|
//#endregion
|
|
26
26
|
//#region ../ui-voice/src/client/VoiceControl.tsx
|
|
@@ -89,17 +89,17 @@ window.__ModuleLoader__.load({
|
|
|
89
89
|
document.head.appendChild(tag);
|
|
90
90
|
}
|
|
91
91
|
var VoiceHistoryAction_module_css_default = {
|
|
92
|
-
"
|
|
92
|
+
"trigger": "hhHVeG_trigger",
|
|
93
|
+
"meta": "hhHVeG_meta",
|
|
94
|
+
"list": "hhHVeG_list",
|
|
95
|
+
"close": "hhHVeG_close",
|
|
93
96
|
"panel": "hhHVeG_panel",
|
|
94
97
|
"row": "hhHVeG_row",
|
|
95
|
-
"trigger": "hhHVeG_trigger",
|
|
96
98
|
"wave": "hhHVeG_wave",
|
|
97
|
-
"header": "hhHVeG_header",
|
|
98
|
-
"empty": "hhHVeG_empty",
|
|
99
99
|
"rowText": "hhHVeG_rowText",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
100
|
+
"empty": "hhHVeG_empty",
|
|
101
|
+
"header": "hhHVeG_header",
|
|
102
|
+
"title": "hhHVeG_title"
|
|
103
103
|
};
|
|
104
104
|
//#endregion
|
|
105
105
|
//#region ../ui-voice/src/client/VoiceHistoryAction.tsx
|
|
@@ -211,23 +211,23 @@ window.__ModuleLoader__.load({
|
|
|
211
211
|
document.head.appendChild(tag);
|
|
212
212
|
}
|
|
213
213
|
var VoiceNodeViews_module_css_default = {
|
|
214
|
-
"
|
|
214
|
+
"taskCard": "KSbXkW_taskCard",
|
|
215
|
+
"taskUpdate": "KSbXkW_taskUpdate",
|
|
216
|
+
"taskChevron": "KSbXkW_taskChevron",
|
|
217
|
+
"taskSummary": "KSbXkW_taskSummary",
|
|
218
|
+
"miniWave": "KSbXkW_miniWave",
|
|
215
219
|
"bubble": "KSbXkW_bubble",
|
|
220
|
+
"taskActions": "KSbXkW_taskActions",
|
|
221
|
+
"taskTitle": "KSbXkW_taskTitle",
|
|
216
222
|
"taskInput": "KSbXkW_taskInput",
|
|
217
|
-
"taskStatus": "KSbXkW_taskStatus",
|
|
218
223
|
"taskDetails": "KSbXkW_taskDetails",
|
|
219
224
|
"taskDot": "KSbXkW_taskDot",
|
|
220
|
-
"
|
|
221
|
-
"taskCard": "KSbXkW_taskCard",
|
|
222
|
-
"taskSummary": "KSbXkW_taskSummary",
|
|
223
|
-
"miniWave": "KSbXkW_miniWave",
|
|
224
|
-
"utterance": "KSbXkW_utterance",
|
|
225
|
-
"meta": "KSbXkW_meta",
|
|
225
|
+
"voiceBadge": "KSbXkW_voiceBadge",
|
|
226
226
|
"taskLink": "KSbXkW_taskLink",
|
|
227
|
+
"taskStatus": "KSbXkW_taskStatus",
|
|
227
228
|
"taskCancel": "KSbXkW_taskCancel",
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"taskChevron": "KSbXkW_taskChevron"
|
|
229
|
+
"utterance": "KSbXkW_utterance",
|
|
230
|
+
"meta": "KSbXkW_meta"
|
|
231
231
|
};
|
|
232
232
|
//#endregion
|
|
233
233
|
//#region ../ui-voice/src/client/VoiceNodeViews.tsx
|
|
@@ -355,12 +355,12 @@ window.__ModuleLoader__.load({
|
|
|
355
355
|
document.head.appendChild(tag);
|
|
356
356
|
}
|
|
357
357
|
var VoiceOverlay_module_css_default = {
|
|
358
|
-
"wave": "AAuDqa_wave",
|
|
359
|
-
"root": "AAuDqa_root",
|
|
360
358
|
"link": "AAuDqa_link",
|
|
361
|
-
"stop": "AAuDqa_stop",
|
|
362
359
|
"status": "AAuDqa_status",
|
|
363
|
-
"
|
|
360
|
+
"root": "AAuDqa_root",
|
|
361
|
+
"stop": "AAuDqa_stop",
|
|
362
|
+
"voice-wave": "AAuDqa_voice-wave",
|
|
363
|
+
"wave": "AAuDqa_wave"
|
|
364
364
|
};
|
|
365
365
|
//#endregion
|
|
366
366
|
//#region ../ui-voice/src/client/VoiceOverlay.tsx
|
|
@@ -724,7 +724,7 @@ function apply(ctx, config = {}) {
|
|
|
724
724
|
}, false);
|
|
725
725
|
agent.steer(message);
|
|
726
726
|
};
|
|
727
|
-
const onTaskCommand = async (binding, voiceSessionId, call,
|
|
727
|
+
const onTaskCommand = async (binding, voiceSessionId, call, delegationOverride) => {
|
|
728
728
|
const complete = (result) => {
|
|
729
729
|
ctx.voice.completeTaskCommand(voiceSessionId, call.id, result);
|
|
730
730
|
};
|
|
@@ -753,10 +753,7 @@ function apply(ctx, config = {}) {
|
|
|
753
753
|
route = await routeFrontendInput(ctx, requireSourceSession(binding).events, call.command.input);
|
|
754
754
|
} catch (error) {
|
|
755
755
|
ctx.logger.warn(error instanceof Error ? error : new Error(String(error)));
|
|
756
|
-
route =
|
|
757
|
-
action: "delegate",
|
|
758
|
-
acknowledgement: DEFAULT_DELEGATION_ACKNOWLEDGEMENT
|
|
759
|
-
};
|
|
756
|
+
route = fallbackDelegation(call.command.input);
|
|
760
757
|
}
|
|
761
758
|
if (route.action === "delegate") {
|
|
762
759
|
const taskId = VoiceTaskId(randomUUID());
|
|
@@ -765,13 +762,16 @@ function apply(ctx, config = {}) {
|
|
|
765
762
|
id: call.id,
|
|
766
763
|
command: {
|
|
767
764
|
type: "realtime_delegation",
|
|
768
|
-
input:
|
|
765
|
+
input: delegationPrompt(route)
|
|
769
766
|
}
|
|
770
|
-
},
|
|
767
|
+
}, {
|
|
768
|
+
taskId,
|
|
769
|
+
requestText: call.command.input
|
|
770
|
+
});
|
|
771
771
|
return;
|
|
772
772
|
}
|
|
773
773
|
complete({ kind: "handled" });
|
|
774
|
-
speakFragment(binding, VoiceTaskId(randomUUID()), route.reply);
|
|
774
|
+
speakFragment(binding, VoiceTaskId(randomUUID()), route.action === "tool" ? executeFrontendTool(route) : route.reply);
|
|
775
775
|
return;
|
|
776
776
|
}
|
|
777
777
|
case "realtime_delegation": {
|
|
@@ -784,7 +784,8 @@ function apply(ctx, config = {}) {
|
|
|
784
784
|
return;
|
|
785
785
|
}
|
|
786
786
|
cancelRewrite(binding);
|
|
787
|
-
const taskId =
|
|
787
|
+
const taskId = delegationOverride?.taskId ?? VoiceTaskId(randomUUID());
|
|
788
|
+
const requestText = delegationOverride?.requestText ?? call.command.input;
|
|
788
789
|
const continuous = (config.taskSessionPolicy ?? "isolated") === "continuous";
|
|
789
790
|
let created;
|
|
790
791
|
try {
|
|
@@ -824,7 +825,7 @@ function apply(ctx, config = {}) {
|
|
|
824
825
|
interactionMode: "frontend-agent",
|
|
825
826
|
taskSessionId: created.taskSessionId,
|
|
826
827
|
messageIds: /* @__PURE__ */ new Set([message.id]),
|
|
827
|
-
requestText
|
|
828
|
+
requestText,
|
|
828
829
|
agent: created.agent,
|
|
829
830
|
...continuous ? {} : { disposeVoiceMessage: created.disposeVoiceMessage },
|
|
830
831
|
cancelling: false,
|
|
@@ -836,7 +837,7 @@ function apply(ctx, config = {}) {
|
|
|
836
837
|
requireSourceSession(binding).append("voice/task-delegated", {
|
|
837
838
|
taskId,
|
|
838
839
|
taskSessionId: created.taskSessionId,
|
|
839
|
-
input:
|
|
840
|
+
input: requestText
|
|
840
841
|
});
|
|
841
842
|
if (contextMessage !== void 0) created.agent.inject(contextMessage);
|
|
842
843
|
created.agent.followup(message);
|
|
@@ -1172,7 +1173,18 @@ function speechFragments(text, flush) {
|
|
|
1172
1173
|
}
|
|
1173
1174
|
const DEFAULT_DELEGATION_ACKNOWLEDGEMENT = "好的,我先查看一下。";
|
|
1174
1175
|
const MAX_DELEGATION_ACKNOWLEDGEMENT_LENGTH = 40;
|
|
1176
|
+
const MAX_DELEGATED_TASK_LENGTH = 1e3;
|
|
1177
|
+
const MAX_DELEGATION_BACKGROUND_LENGTH = 2e3;
|
|
1175
1178
|
const VOICE_LATENCY_DEBUG_PREFIX = "[DEBUG-VOICE-LATENCY]";
|
|
1179
|
+
const WEEKDAY_NAMES = [
|
|
1180
|
+
"星期日",
|
|
1181
|
+
"星期一",
|
|
1182
|
+
"星期二",
|
|
1183
|
+
"星期三",
|
|
1184
|
+
"星期四",
|
|
1185
|
+
"星期五",
|
|
1186
|
+
"星期六"
|
|
1187
|
+
];
|
|
1176
1188
|
function debugVoiceLatency(...values) {
|
|
1177
1189
|
if (globalThis.process?.env?.DSH_VOICE_LATENCY_DEBUG !== "1") return;
|
|
1178
1190
|
console.info(VOICE_LATENCY_DEBUG_PREFIX, (/* @__PURE__ */ new Date()).toISOString(), ...values);
|
|
@@ -1183,6 +1195,43 @@ function delegationAcknowledgement(value) {
|
|
|
1183
1195
|
if (normalized === "" || normalized.length > MAX_DELEGATION_ACKNOWLEDGEMENT_LENGTH) return DEFAULT_DELEGATION_ACKNOWLEDGEMENT;
|
|
1184
1196
|
return normalized;
|
|
1185
1197
|
}
|
|
1198
|
+
function delegationField(value, name, maxLength) {
|
|
1199
|
+
if (typeof value !== "string") throw new Error(`voice frontend router omitted ${name}`);
|
|
1200
|
+
const normalized = value.trim();
|
|
1201
|
+
if (normalized === "" || normalized.length > maxLength) throw new Error(`voice frontend router returned an invalid ${name}`);
|
|
1202
|
+
return normalized;
|
|
1203
|
+
}
|
|
1204
|
+
function fallbackDelegation(input) {
|
|
1205
|
+
return {
|
|
1206
|
+
action: "delegate",
|
|
1207
|
+
acknowledgement: DEFAULT_DELEGATION_ACKNOWLEDGEMENT,
|
|
1208
|
+
task: input.trim() || input,
|
|
1209
|
+
background: "没有可用的额外背景。",
|
|
1210
|
+
userRequest: input
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
function delegationPrompt(route) {
|
|
1214
|
+
return [
|
|
1215
|
+
"当前任务:",
|
|
1216
|
+
route.task,
|
|
1217
|
+
"",
|
|
1218
|
+
"前置背景:",
|
|
1219
|
+
route.background,
|
|
1220
|
+
"",
|
|
1221
|
+
"用户原话:",
|
|
1222
|
+
route.userRequest,
|
|
1223
|
+
"",
|
|
1224
|
+
"请以“当前任务”为最高优先级执行;前置背景只用于理解和消歧,不要把旧对话当成待执行任务。"
|
|
1225
|
+
].join("\n");
|
|
1226
|
+
}
|
|
1227
|
+
function executeFrontendTool(route) {
|
|
1228
|
+
switch (route.tool) {
|
|
1229
|
+
case "local_datetime": {
|
|
1230
|
+
const now = /* @__PURE__ */ new Date();
|
|
1231
|
+
return `根据本机系统时间,现在是 ${now.getFullYear()}年${now.getMonth() + 1}月${now.getDate()}日,${WEEKDAY_NAMES[now.getDay()]},${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}。`;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1186
1235
|
async function routeFrontendInput(ctx, events, input) {
|
|
1187
1236
|
let llm;
|
|
1188
1237
|
try {
|
|
@@ -1190,10 +1239,7 @@ async function routeFrontendInput(ctx, events, input) {
|
|
|
1190
1239
|
} catch {
|
|
1191
1240
|
llm = void 0;
|
|
1192
1241
|
}
|
|
1193
|
-
if (llm === void 0) return
|
|
1194
|
-
action: "delegate",
|
|
1195
|
-
acknowledgement: DEFAULT_DELEGATION_ACKNOWLEDGEMENT
|
|
1196
|
-
};
|
|
1242
|
+
if (llm === void 0) return fallbackDelegation(input);
|
|
1197
1243
|
const selection = ctx.agentDefaultModel.currentSelection();
|
|
1198
1244
|
const recentConversation = events.flatMap((event) => {
|
|
1199
1245
|
if (event.type !== "voice/utterance-end" || event.data.state !== "completed") return [];
|
|
@@ -1204,15 +1250,21 @@ async function routeFrontendInput(ctx, events, input) {
|
|
|
1204
1250
|
content: [{
|
|
1205
1251
|
type: "text",
|
|
1206
1252
|
text: [
|
|
1207
|
-
"
|
|
1208
|
-
"
|
|
1209
|
-
"
|
|
1210
|
-
"
|
|
1253
|
+
"结合最近对话判断当前用户原话应该由语音前台聊天、轻量工具还是后台编码 Agent 处理。",
|
|
1254
|
+
"普通寒暄、日常对话、解释性知识以及无需外部事实或工具即可回答的问题,选择 chat,并直接给出自然简洁的中文回复。",
|
|
1255
|
+
"时间、日期等会随现实变化的事实不能猜测。查询本机当前时间、日期或星期时必须选择 tool=local_datetime。",
|
|
1256
|
+
"其他必须查询才能确认的动态事实,如果没有对应的前台工具,应选择 delegate,不能选择 chat 编造答案。",
|
|
1257
|
+
"需要查看或修改工作区文件、运行命令、测试、安装依赖或执行其他复杂工具操作时,选择 delegate。",
|
|
1258
|
+
"选择 delegate 时,必须结合最近对话补全省略指代,输出自包含的 task、只用于消歧的 background,并原样复制 user_request。",
|
|
1259
|
+
"task 是后台当前唯一要执行的任务;background 不能包含新的要求,也不要把旧任务写成待办。",
|
|
1260
|
+
"同时给出一句简短自然的 acknowledgement,表示接下来要做什么。不能声称任务已经完成、已经找到结果,也不要提后台 Agent、工具或路由。",
|
|
1211
1261
|
"acknowledgement 只能有一句,最多 40 个字符,例如“好的,我先检查一下相关代码。”。",
|
|
1212
1262
|
"只输出一行 JSON,不要 Markdown。格式只能是:",
|
|
1213
1263
|
"{\"action\":\"chat\",\"reply\":\"...\"}",
|
|
1214
1264
|
"或:",
|
|
1215
|
-
"{\"action\":\"
|
|
1265
|
+
"{\"action\":\"tool\",\"tool\":\"local_datetime\"}",
|
|
1266
|
+
"或:",
|
|
1267
|
+
"{\"action\":\"delegate\",\"acknowledgement\":\"...\",\"task\":\"...\",\"background\":\"...\",\"user_request\":\"...\"}",
|
|
1216
1268
|
"",
|
|
1217
1269
|
"最近对话:",
|
|
1218
1270
|
recentConversation || "(无)",
|
|
@@ -1239,9 +1291,16 @@ async function routeFrontendInput(ctx, events, input) {
|
|
|
1239
1291
|
action: "chat",
|
|
1240
1292
|
reply: parsed.reply.trim()
|
|
1241
1293
|
};
|
|
1294
|
+
if (parsed.action === "tool" && parsed.tool === "local_datetime") return {
|
|
1295
|
+
action: "tool",
|
|
1296
|
+
tool: parsed.tool
|
|
1297
|
+
};
|
|
1242
1298
|
if (parsed.action === "delegate") return {
|
|
1243
1299
|
action: "delegate",
|
|
1244
|
-
acknowledgement: delegationAcknowledgement(parsed.acknowledgement)
|
|
1300
|
+
acknowledgement: delegationAcknowledgement(parsed.acknowledgement),
|
|
1301
|
+
task: delegationField(parsed.task, "task", MAX_DELEGATED_TASK_LENGTH),
|
|
1302
|
+
background: delegationField(parsed.background, "background", MAX_DELEGATION_BACKGROUND_LENGTH),
|
|
1303
|
+
userRequest: input
|
|
1245
1304
|
};
|
|
1246
1305
|
throw new Error("voice frontend router returned an invalid decision");
|
|
1247
1306
|
}
|
package/package.json
CHANGED