mqtt切换为远程服务器部署,优化简单指令解析时长

This commit is contained in:
Miayui
2026-09-11 10:22:24 +08:00
parent 51ca12b624
commit 3fc6f2c2a4
34 changed files with 4290 additions and 484 deletions
+497 -41
View File
@@ -23,15 +23,37 @@
let reasoningPickerControl = null;
let validatedCommandsVisible = false;
let validatedCommandContext = { requestId: "", utterance: "" };
let pendingSpeechTiming = null;
let commandTimingState = {
inputMode: "text",
utterance: "",
requestId: "",
requestStartedAt: null,
validatedAt: null,
displayedAt: null,
renderDurationMs: null,
speechStartedAt: null,
speechResultAt: null,
speechTimings: null,
};
function resetValidatedCommandContext() {
validatedCommandsVisible = false;
validatedCommandContext = { requestId: "", utterance: "" };
}
function commandCorrelationIds(result = {}) {
return [...new Set([
result.preview_request_id,
result.supplement_of,
result.request_id,
].map((value) => String(value || "").trim()).filter(Boolean))];
}
function rememberValidatedCommandContext(result = {}) {
const ids = commandCorrelationIds(result);
validatedCommandContext = {
requestId: String(result.request_id || ""),
requestId: ids[0] || "",
utterance: String(result.utterance || "").trim(),
};
}
@@ -39,10 +61,10 @@
function validatedCommandsBelongToTask(task = {}) {
if (!validatedCommandsVisible) return false;
const result = task.result && typeof task.result === "object" ? task.result : {};
const requestId = String(result.request_id || "");
const requestIds = commandCorrelationIds(result);
const utterance = String(task.utterance || result.utterance || "").trim();
if (validatedCommandContext.requestId && requestId
&& validatedCommandContext.requestId !== requestId) return false;
if (validatedCommandContext.requestId && requestIds.length
&& !requestIds.includes(validatedCommandContext.requestId)) return false;
if (validatedCommandContext.utterance && utterance
&& validatedCommandContext.utterance !== utterance
&& !validatedCommandContext.utterance.includes(utterance)
@@ -83,6 +105,123 @@
: null;
};
const monotonicNow = () => (
typeof performance !== "undefined" && typeof performance.now === "function"
? performance.now()
: Date.now()
);
const finiteMilliseconds = (value) => {
const numeric = Number(value);
return Number.isFinite(numeric) && numeric >= 0 ? numeric : null;
};
const semanticValue = (value) => {
if (value == null) return "null";
if (["string", "number", "boolean"].includes(typeof value)) return String(value);
try {
return JSON.stringify(value);
} catch (_error) {
return String(value);
}
};
function semanticControlInstruction(operation = {}) {
const capability = String(operation.capability || "unknown");
const rawTarget = operation.target || {};
const target = rawTarget.zone || rawTarget.instance || rawTarget.device;
const argumentsList = [];
if (target != null && String(target).trim()) {
argumentsList.push(`target=${String(target).replaceAll("_", "-")}`);
}
Object.entries(operation.parameters || {})
.sort(([left], [right]) => left.localeCompare(right))
.forEach(([key, value]) => {
argumentsList.push(`${key}=${semanticValue(value)}`);
});
return `${capability}(${argumentsList.join(", ")})`;
}
function commandPlanEntries(result = {}) {
const plans = result.command_plans && typeof result.command_plans === "object"
? Object.entries(result.command_plans)
: [];
if (!plans.length && result.command_plan) plans.push(["default", result.command_plan]);
return plans;
}
function compiledCommandsForOperation(result, configKey, operation = {}) {
const operationId = String(operation.id || "");
if (!operationId) return [];
const plans = commandPlanEntries(result);
const preferred = plans.filter(([key]) => key === configKey);
const operationRefs = (command) => {
const raw = command?.operation_ref;
const refs = Array.isArray(raw) ? raw : String(raw || "").split(",");
return refs.map((ref) => String(ref).trim()).filter(Boolean);
};
const matches = (entries) => entries.flatMap(([planKey, plan]) => (
(plan?.spec?.commands || [])
.filter((command) => operationRefs(command).includes(operationId))
.map((command) => ({ command, plan, planKey }))
));
if (preferred.length) return matches(preferred);
const configurations = result.function_configs && typeof result.function_configs === "object"
? Object.entries(result.function_configs)
: result.function_config ? [["default", result.function_config]] : [];
return configurations.length === 1 && plans.length === 1 ? matches(plans) : [];
}
function compiledCommandPresentation(entries, fallback = {}) {
if (!entries.length) return fallback;
const actualCommands = [];
const frames = [];
let channel = fallback.channel || "CAN1";
entries.forEach(({ command, plan }) => {
const signals = command?.signals || {};
const message = String(command?.message || "");
const planDomain = String(plan?.spec?.domain || "");
const bus = String(command?.bus || "").toLowerCase();
const slot = boundedInteger(signals.slot, 1, 248);
if (message === "infrared.emit" || planDomain === "self_extension") {
channel = "IR";
} else if (bus.includes("seat") || bus.includes("can0")) {
channel = "CAN0";
} else if (bus.includes("body") || bus.includes("can1")) {
channel = "CAN1";
}
const actual = String(signals.debug_command || "").trim()
|| (message === "infrared.emit" && slot !== null ? `infrared emit ${slot}` : "")
|| String(fallback.command || message).trim();
if (actual && !actualCommands.includes(actual)) actualCommands.push(actual);
const frameId = String(signals.frame_id || "").trim();
const dataHex = String(signals.data_hex || "").trim();
if (frameId && dataHex) {
frames.push({ label: `${channel} 帧`, value: `${frameId} · ${dataHex}` });
} else if (message === "infrared.emit" && slot !== null) {
frames.push({
label: "GSCU1B TCP",
value: JSON.stringify({ action: "emit", data: { no: slot }, type: "infrared" }),
});
}
});
const uniqueFrames = frames.filter((frame, index) => (
frames.findIndex((candidate) => (
candidate.label === frame.label && candidate.value === frame.value
)) === index
));
return {
...fallback,
channel,
command: actualCommands.join(";") || fallback.command || "",
...(uniqueFrames.length ? {
frame: uniqueFrames[0].value,
frameLabel: uniqueFrames[0].label,
frames: uniqueFrames,
} : {}),
};
}
function parsedCanCommandForOperation(operation = {}) {
const capability = String(operation.capability || "");
const parameters = operation.parameters || {};
@@ -397,15 +536,68 @@
};
};
const commands = [];
configurationEntries.forEach(([_configKey, configuration]) => {
(configuration?.spec?.operations || []).forEach((operation) => {
const item = parsedCanCommandForOperation(operation);
if (item) commands.push({ ...item, ...deliveryForOperation(operation) });
const validatedControlCommands = Array.isArray(result.validated_control_commands)
? result.validated_control_commands
: [];
if (validatedControlCommands.length) {
validatedControlCommands.forEach((presentation) => {
const operation = presentation?.operation || {};
const fallback = parsedCanCommandForOperation(operation) || {
channel: presentation?.domain === "self_extension" ? "IR" : "CAN1",
category: String(operation.capability || "控制指令"),
command: "",
...(operation.parse_source ? { parseSource: String(operation.parse_source) } : {}),
};
const channel = new Set(["CAN0", "CAN1", "IR"]).has(presentation?.channel)
? presentation.channel
: fallback.channel;
const frames = (Array.isArray(presentation?.frames) ? presentation.frames : [])
.map((frame) => ({
label: String(frame?.label || "控制帧"),
value: String(frame?.value || ""),
}))
.filter((frame) => frame.value);
const command = String(presentation?.actual_command || fallback.command || "").trim();
if (!command) return;
commands.push({
...fallback,
channel,
command,
...(frames.length ? {
frame: frames[0].value,
frameLabel: frames[0].label,
frames,
} : {}),
semantic: semanticControlInstruction(operation),
operationRef: String(operation.id || ""),
...deliveryForOperation(operation),
});
});
});
} else {
configurationEntries.forEach(([configKey, configuration]) => {
(configuration?.spec?.operations || []).forEach((operation) => {
const fallback = parsedCanCommandForOperation(operation) || {
channel: configuration?.spec?.domain === "self_extension" ? "IR" : "CAN1",
category: String(operation.capability || "控制指令"),
command: "",
...(operation.parse_source ? { parseSource: String(operation.parse_source) } : {}),
};
const compiled = compiledCommandsForOperation(result, configKey, operation);
const item = compiledCommandPresentation(compiled, fallback);
if (item.command) {
commands.push({
...item,
semantic: semanticControlInstruction(operation),
operationRef: String(operation.id || ""),
...deliveryForOperation(operation),
});
}
});
});
}
const unique = new Map();
commands.forEach((item) => {
const key = `${item.channel}:${item.command}`;
const key = `${item.operationRef || item.semantic}:${item.channel}:${item.command}`;
const previous = unique.get(key);
if (!previous
|| (previous.deliveryState !== "submitted" && item.deliveryState === "submitted")
@@ -425,7 +617,192 @@
}[source] || "云端模型";
}
function renderParsedCanCommands(result, { message = "" } = {}) {
const commandTraceStageLabels = {
intent_parse: "意图解析",
context_resolve: "上下文解析",
domain_route: "控制域路由",
function_generate: "语义指令生成",
schema_validate: "Schema 校验",
domain_validate: "领域语义校验",
compile: "实际指令编译",
nfc_peripheral_route: "红外能力路由",
local_peripheral_plan: "红外本地规则编译",
nfc_peripheral_sequence: "红外指令序列生成",
};
const speechTimingLabels = {
audio_read_ms: "音频读取与重采样",
vad_ms: "语音活动检测",
asr_ms: "语音识别 ASR",
assembly_ms: "分段文本组装",
semantic_ms: "语音语义处理",
command_resolution_ms: "本地指令消歧",
recovery_ms: "语音识别恢复",
total_ms: "语音服务总耗时",
};
function parsedCommandTimingRows(result = {}, timingState = {}) {
const rows = [];
const push = (group, key, label, value) => {
const durationMs = finiteMilliseconds(value);
if (durationMs !== null) rows.push({ group, key, label, durationMs });
};
if (timingState.inputMode === "speech") {
Object.entries(speechTimingLabels).forEach(([key, label]) => {
push("speech", key, label, timingState.speechTimings?.[key]);
});
if (timingState.speechStartedAt != null && timingState.speechResultAt != null) {
push(
"speech",
"speech_capture_to_text",
"语音开始 → 文本就绪",
timingState.speechResultAt - timingState.speechStartedAt,
);
}
}
const traceEvents = Array.isArray(result.trace_events) ? result.trace_events : [];
const compileIndex = traceEvents.map((event) => event?.stage).lastIndexOf("compile");
const validatedTraceEvents = compileIndex >= 0
? traceEvents.slice(0, compileIndex + 1)
: traceEvents;
const localRuleResult = String(result.provider_used || "").startsWith("local")
|| parsedCanCommands(result).some((item) => item.parseSource === "local_rule");
const localBudgetMs = finiteMilliseconds(result.local_control_budget_ms);
const localWithinBudget = result.local_control_within_budget === true;
push(
"backend",
"local_control_elapsed",
localBudgetMs === null
? "本地规则解析与下发"
: `本地规则解析与下发(${localWithinBudget ? "1 秒内" : "超出 1 秒"})`,
result.local_control_elapsed_ms,
);
validatedTraceEvents.forEach((event, index) => {
const stage = String(event?.stage || "");
if (!(stage in commandTraceStageLabels)) return;
const label = stage === "intent_parse" && localRuleResult
? "本地规则意图解析"
: commandTraceStageLabels[stage];
push("backend", `${stage}-${index}`, label, event.duration_ms);
});
if (timingState.requestStartedAt != null && timingState.validatedAt != null) {
push(
"frontend",
"request_to_validated",
"请求提交 → 编译结果到达",
timingState.validatedAt - timingState.requestStartedAt,
);
}
push("frontend", "dom_render", "前端指令渲染", timingState.renderDurationMs);
if (timingState.requestStartedAt != null && timingState.displayedAt != null) {
push(
"total",
"request_to_display",
"文本提交 → 指令显示",
timingState.displayedAt - timingState.requestStartedAt,
);
}
if (timingState.inputMode === "speech"
&& timingState.speechStartedAt != null
&& timingState.displayedAt != null) {
push(
"total",
"speech_to_display",
"语音开始 → 指令显示",
timingState.displayedAt - timingState.speechStartedAt,
);
}
return rows;
}
const formattedDuration = (value) => {
const duration = finiteMilliseconds(value);
if (duration === null) return "--";
if (duration > 0 && duration < 0.1) return "<0.1 ms";
return `${duration < 10 ? duration.toFixed(1) : Math.round(duration)} ms`;
};
function renderCommandTimings(result = null, { pending = false } = {}) {
const container = $("#v2CommandTiming");
if (!container) return;
const rows = result ? parsedCommandTimingRows(result, commandTimingState) : [];
if (!rows.length && !pending) {
container.hidden = true;
container.innerHTML = "";
return;
}
container.hidden = false;
if (!rows.length) {
container.innerHTML = `<p>阶段耗时计时中 · ${commandTimingState.inputMode === "speech" ? "语音输入" : "普通文本"}</p>`;
return;
}
const total = [...rows].reverse().find((row) => row.group === "total");
container.innerHTML = `
<header>
<strong>阶段耗时</strong>
<span>${commandTimingState.inputMode === "speech" ? "语音输入" : "普通文本"}</span>
${total ? `<b>${escapeHtml(formattedDuration(total.durationMs))}</b>` : ""}
</header>
<ol>${rows.map((row) => `
<li data-timing-group="${escapeHtml(row.group)}" data-timing-stage="${escapeHtml(row.key)}">
<span>${escapeHtml(row.label)}</span><b>${escapeHtml(formattedDuration(row.durationMs))}</b>
</li>`).join("")}</ol>`;
}
function beginCommandTiming(request = {}) {
const input = $("#utterance");
const utterance = String(request.utterance ?? input?.value ?? "").trim();
const speechUtteranceId = String(
request.speech_utterance_id ?? input?.dataset?.speechUtteranceId ?? "",
);
const speechValue = String(
request.speech_value ?? input?.dataset?.speechValue ?? "",
);
const speechMatch = Boolean(
speechUtteranceId
&& pendingSpeechTiming?.utteranceId === speechUtteranceId
&& speechValue === utterance,
);
commandTimingState = {
inputMode: speechMatch ? "speech" : "text",
utterance,
requestId: "",
requestStartedAt: monotonicNow(),
validatedAt: null,
displayedAt: null,
renderDurationMs: null,
speechStartedAt: speechMatch ? pendingSpeechTiming.startedAt : null,
speechResultAt: speechMatch ? pendingSpeechTiming.resultAt : null,
speechTimings: speechMatch ? { ...(pendingSpeechTiming.timings || {}) } : null,
};
}
function commandResultMatchesTimedRequest(result = {}) {
if (commandTimingState.requestStartedAt == null) return true;
const expectedUtterance = String(commandTimingState.utterance || "")
.replace(/\s+/g, "")
.trim();
const receivedUtterance = String(result.utterance || "")
.replace(/\s+/g, "")
.trim();
if (expectedUtterance && receivedUtterance
&& expectedUtterance !== receivedUtterance
&& !expectedUtterance.includes(receivedUtterance)
&& !receivedUtterance.includes(expectedUtterance)) return false;
const requestIds = commandCorrelationIds(result);
if (commandTimingState.requestId && requestIds.length
&& !requestIds.includes(commandTimingState.requestId)) return false;
if (!commandTimingState.requestId && requestIds.length) {
[commandTimingState.requestId] = requestIds;
}
return true;
}
function renderParsedCanCommands(
result,
{ message = "", pending = false, recordTiming = false, reveal = false } = {},
) {
const renderStartedAt = monotonicNow();
const panel = $("#v2CanCommandPanel");
const list = $("#v2CanCommandList");
const count = $("#v2CanCommandCount");
@@ -442,12 +819,31 @@
<b class="v2-can-command-delivery" data-delivery-state="${escapeHtml(item.deliveryState)}" title="${escapeHtml(`${item.deliveryTransport} · ${item.deliveryLabel}`)}">${escapeHtml(item.deliveryLabel)}</b>
<i class="v2-can-command-category">${escapeHtml(item.category)}</i>
</span>
<code>${escapeHtml(item.command)}</code>
${item.frame ? `<small class="v2-can-command-frame">${escapeHtml(item.frameLabel || "固件原生 CAN0")} · ${escapeHtml(item.frame)}</small>` : ""}
<div class="v2-can-command-layer v2-can-command-semantic">
<small>语义控制指令</small>
<code>${escapeHtml(item.semantic)}</code>
</div>
<div class="v2-can-command-layer v2-can-command-actual">
<small>实际控制指令</small>
<code>${escapeHtml(item.command)}</code>
</div>
${(item.frames || (item.frame ? [{ label: item.frameLabel || "控制帧", value: item.frame }] : [])).map((frame) => `<small class="v2-can-command-frame"><b>${escapeHtml(frame.label)}</b> · ${escapeHtml(frame.value)}</small>`).join("")}
</li>`).join("")}</ol>`;
} else {
list.innerHTML = `<p>${escapeHtml(message || "本次请求未解析出控制指令")}</p>`;
}
if (reveal && commands.length) {
panel.open = true;
if (ui.sidebarCollapsed) setSidebarCollapsed(false);
}
if (recordTiming && commands.length) {
if (commandTimingState.validatedAt == null) commandTimingState.validatedAt = renderStartedAt;
if (commandTimingState.displayedAt == null) {
commandTimingState.displayedAt = monotonicNow();
commandTimingState.renderDurationMs = commandTimingState.displayedAt - renderStartedAt;
}
}
renderCommandTimings(result, { pending });
requestAnimationFrame(syncSidebarLogBaseline);
}
@@ -767,6 +1163,11 @@
logKeys: new Set(),
extensionFingerprint: "",
};
const nonDispatchTaskSources = new Set(["mode_editor_compile"]);
function isDispatchTask(task = {}) {
return !nonDispatchTaskSources.has(String(task.source || "").trim());
}
const readText = (selector, fallback = "--") => $(selector)?.textContent?.trim() || fallback;
@@ -1625,26 +2026,28 @@
if (revision !== displaySync.revision) {
displaySync.revision = revision;
const task = snapshot?.task || {};
setTaskPresentation(task);
const sourceLabel = task.source === "web_console" ? "网页端" : "车机端";
if (task.utterance) {
const submittedUnverified = task.result?.execution_outcome === "submitted_unverified"
|| task.result?.execution_report?.spec?.verification_status === "submitted_unverified";
appendSyncLog({
key: `task:${revision}:${task.status}`,
category: "task",
title: `${sourceLabel}${task.status === "running" ? "下发编排任务" : "任务状态已同步"}`,
detail: `${task.utterance} · ${submittedUnverified ? "指令已提交,等待车辆状态确认" : task.status || "idle"}`,
status: submittedUnverified ? "info" : ["succeeded", "partially_succeeded"].includes(task.status) ? "success" : task.status === "failed" ? "error" : "info",
});
}
const result = task.result;
const requestId = result?.request_id || null;
if (result && requestId !== displaySync.requestId) {
displaySync.requestId = requestId;
window.CognitiveAgentConsole?.applySyncedResult?.(result, {
utterance: task.utterance || "",
});
if (isDispatchTask(task)) {
setTaskPresentation(task);
const sourceLabel = task.source === "web_console" ? "网页端" : "车机端";
if (task.utterance) {
const submittedUnverified = task.result?.execution_outcome === "submitted_unverified"
|| task.result?.execution_report?.spec?.verification_status === "submitted_unverified";
appendSyncLog({
key: `task:${revision}:${task.status}`,
category: "task",
title: `${sourceLabel}${task.status === "running" ? "下发编排任务" : "任务状态已同步"}`,
detail: `${task.utterance} · ${submittedUnverified ? "指令已提交,等待车辆状态确认" : task.status || "idle"}`,
status: submittedUnverified ? "info" : ["succeeded", "partially_succeeded"].includes(task.status) ? "success" : task.status === "failed" ? "error" : "info",
});
}
const result = task.result;
const requestId = result?.request_id || null;
if (result && requestId !== displaySync.requestId) {
displaySync.requestId = requestId;
window.CognitiveAgentConsole?.applySyncedResult?.(result, {
utterance: task.utterance || "",
});
}
}
}
const extensionFingerprint = synchronizedCatalog
@@ -1763,12 +2166,13 @@
insertVisibleControl(simulationFaultField, "v2-sync-simulation-fault-field");
const canCommandPanel = make("details", "v2-can-command-panel", `
<summary>
<span><i data-lucide="binary"></i><b>已解析控制指令</b></span>
<span><i data-lucide="binary"></i><b>下发控制指令</b></span>
<strong id="v2CanCommandCount">0</strong>
<i data-lucide="chevron-down"></i>
</summary>
<section class="v2-command-timing" id="v2CommandTiming" aria-live="polite" hidden></section>
<div class="v2-can-command-list" id="v2CanCommandList">
<p>等待自然语言解析结果</p>
<p>等待准备下发的控制指令</p>
</div>`);
canCommandPanel.id = "v2CanCommandPanel";
syncOverview.append(canCommandPanel);
@@ -1875,17 +2279,57 @@
if (event.target.matches("select[data-device-control]")) storeDeviceControlDraft(event.target);
});
$("#v2LogFilter")?.addEventListener("change", renderSyncLogs);
$("#requestForm")?.addEventListener("submit", () => {
const beginPendingControlRequest = (request = {}) => {
beginCommandTiming(request);
resetValidatedCommandContext();
renderParsedCanCommands(null, { message: "正在解析自然语言并生成控制指令…" });
renderParsedCanCommands(null, {
message: "正在解析自然语言并生成控制指令…",
pending: true,
});
};
$("#requestForm")?.addEventListener("submit", () => {
beginPendingControlRequest();
}, true);
window.addEventListener("cognitive-agent:control-request-start", (event) => {
beginPendingControlRequest(event.detail || {});
});
window.addEventListener("cognitive-agent:speech-state", (event) => {
const phase = event.detail?.phase;
if (phase === "capturing" && !pendingSpeechTiming?.captureActive) {
pendingSpeechTiming = {
utteranceId: "",
startedAt: monotonicNow(),
resultAt: null,
timings: null,
captureActive: true,
};
} else if (["finalizing", "success", "error"].includes(phase)
&& pendingSpeechTiming) {
pendingSpeechTiming.captureActive = false;
}
});
window.addEventListener("cognitive-agent:speech-result", (event) => {
const result = event.detail || {};
pendingSpeechTiming = {
...(pendingSpeechTiming || { startedAt: null }),
utteranceId: String(result.utterance_id || ""),
resultAt: monotonicNow(),
timings: result.timings && typeof result.timings === "object"
? { ...result.timings }
: null,
captureActive: false,
};
});
window.addEventListener("cognitive-agent:validated-can-commands", (event) => {
const result = event.detail || {};
if (!commandResultMatchesTimedRequest(result)) return;
validatedCommandsVisible = parsedCanCommands(result).length > 0;
if (validatedCommandsVisible) rememberValidatedCommandContext(result);
else resetValidatedCommandContext();
renderParsedCanCommands(result, {
message: "控制指令已解析,正在等待传输结果…",
recordTiming: true,
reveal: true,
});
});
window.addEventListener("cognitive-agent:result", (event) => {
@@ -1893,11 +2337,15 @@
const submittedUnverified = result.execution_outcome === "submitted_unverified"
|| result.execution_report?.spec?.verification_status === "submitted_unverified";
const commands = parsedCanCommands(result);
if (commands.length) {
const matchesTimedRequest = commandResultMatchesTimedRequest(result);
if (matchesTimedRequest && commands.length) {
validatedCommandsVisible = true;
rememberValidatedCommandContext(result);
renderParsedCanCommands(result);
} else if (!validatedCommandsBelongToTask({
renderParsedCanCommands(result, {
recordTiming: commandTimingState.displayedAt == null,
reveal: commandTimingState.requestStartedAt != null,
});
} else if (matchesTimedRequest && !validatedCommandsBelongToTask({
utterance: result.utterance,
result,
})) {
@@ -1917,7 +2365,15 @@
});
});
window.addEventListener("cognitive-agent:request-complete", (event) => {
if (event.detail?.error && !validatedCommandsVisible) {
const completedUtterance = String(event.detail?.utterance || "")
.replace(/\s+/g, "")
.trim();
const expectedUtterance = String(commandTimingState.utterance || "")
.replace(/\s+/g, "")
.trim();
const matchesTimedRequest = !expectedUtterance || !completedUtterance
|| expectedUtterance === completedUtterance;
if (matchesTimedRequest && !validatedCommandsVisible) {
renderParsedCanCommands(null, { message: "本次请求未生成控制指令" });
}
if (event.detail?.error) {