优化后端
This commit is contained in:
@@ -502,6 +502,282 @@
|
||||
followLatest(true);
|
||||
}
|
||||
|
||||
const assistantControlLabels = {
|
||||
"climate.set_power": "空调电源",
|
||||
"climate.set_temperature": "空调温度",
|
||||
"climate.set_fan_level": "空调风量",
|
||||
"climate.set_outlet_mode": "出风模式",
|
||||
"climate.set_control_mode": "空调控制方式",
|
||||
"climate.set_cycle_mode": "空调循环",
|
||||
"interior_light.set_indoor_power": "室内灯",
|
||||
"interior_light.set_reading_power": "阅读灯",
|
||||
"ambient_light.set_power": "氛围灯",
|
||||
"ambient_light.set_color_index": "氛围灯颜色",
|
||||
"ambient_light.set_brightness_level": "氛围灯亮度",
|
||||
"ambient_light.set_illumination": "氛围灯颜色与亮度",
|
||||
"ambient_light.set_mode": "氛围灯模式",
|
||||
"roof.set_shade_motion": "遮阳帘",
|
||||
"roof.set_shade_position": "遮阳帘位置",
|
||||
"window.set_motion": "车窗",
|
||||
"window.set_position": "车窗位置",
|
||||
"door.set_rear_lock": "后排门锁",
|
||||
"door.set_vehicle_lock": "整车门锁",
|
||||
"infrared.emit": "红外外设",
|
||||
};
|
||||
|
||||
function assistantControlCategory(operation = {}, fallback = "控制指令") {
|
||||
const capability = String(operation.capability || "");
|
||||
if (capability === "infrared.emit") {
|
||||
const deviceName = String(
|
||||
operation.target?.name || operation.target?.device || "红外外设",
|
||||
).trim();
|
||||
return deviceName || "红外外设";
|
||||
}
|
||||
if (assistantControlLabels[capability]) return assistantControlLabels[capability];
|
||||
if (capability.startsWith("seat.")) {
|
||||
const target = String(operation.target?.zone || operation.target?.instance || "")
|
||||
.replaceAll("_", "-");
|
||||
return `${["passenger", "front-right"].includes(target) ? "副驾" : "主驾"}座椅`;
|
||||
}
|
||||
if (capability.startsWith("outlet.")) return "空调风口";
|
||||
return String(fallback || capability || "控制指令");
|
||||
}
|
||||
|
||||
function fallbackAssistantControlCommands(result = {}) {
|
||||
return (Array.isArray(result.validated_control_commands)
|
||||
? result.validated_control_commands
|
||||
: [])
|
||||
.map((presentation) => {
|
||||
const operation = presentation?.operation || {};
|
||||
const command = String(presentation?.actual_command || "").trim();
|
||||
if (!command) return null;
|
||||
const capability = String(operation.capability || "");
|
||||
const target = operation.target || {};
|
||||
const targetValue = target.zone || target.instance || target.device;
|
||||
const semanticArguments = [];
|
||||
if (targetValue != null && String(targetValue).trim()) {
|
||||
semanticArguments.push(`target=${String(targetValue).replaceAll("_", "-")}`);
|
||||
}
|
||||
Object.entries(operation.parameters || {})
|
||||
.sort(([left], [right]) => left.localeCompare(right))
|
||||
.forEach(([key, value]) => semanticArguments.push(`${key}=${String(value)}`));
|
||||
return {
|
||||
category: assistantControlCategory(operation, capability),
|
||||
channel: String(presentation?.channel || ""),
|
||||
command,
|
||||
deliveryState: "not_submitted",
|
||||
operation,
|
||||
operationRef: String(operation.id || ""),
|
||||
semantic: `${capability || "control"}(${semanticArguments.join(", ")})`,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function assistantDispatchedCommands(result = {}) {
|
||||
const projections = Array.isArray(result.validated_control_commands)
|
||||
? result.validated_control_commands
|
||||
: [];
|
||||
const projectionByRef = new Map(projections.map((presentation) => [
|
||||
String(presentation?.operation?.id || ""),
|
||||
presentation?.operation || {},
|
||||
]));
|
||||
let commands = [];
|
||||
try {
|
||||
const sharedCommands = window.CognitiveAgentConsoleV2?.getParsedCanCommands?.(result);
|
||||
if (Array.isArray(sharedCommands)) commands = sharedCommands;
|
||||
} catch (_error) {
|
||||
commands = [];
|
||||
}
|
||||
if (!commands.length) return fallbackAssistantControlCommands(result);
|
||||
return commands.map((item, index) => {
|
||||
const operation = projectionByRef.get(String(item.operationRef || ""))
|
||||
|| projections[index]?.operation
|
||||
|| {};
|
||||
return {
|
||||
...item,
|
||||
category: assistantControlCategory(operation, item.category),
|
||||
operation,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function localizedAssistantControlValue(value) {
|
||||
const normalized = String(value ?? "").trim().toLowerCase();
|
||||
return {
|
||||
on: "开启",
|
||||
open: "开启",
|
||||
true: "开启",
|
||||
off: "关闭",
|
||||
close: "关闭",
|
||||
false: "关闭",
|
||||
half: "一半",
|
||||
stop: "停止",
|
||||
lock: "上锁",
|
||||
unlock: "解锁",
|
||||
auto: "自动",
|
||||
manual: "手动",
|
||||
internal: "内循环",
|
||||
external: "外循环",
|
||||
}[normalized] || String(value ?? "").trim();
|
||||
}
|
||||
|
||||
function assistantControlValue(item = {}) {
|
||||
const parameters = item.operation?.parameters || {};
|
||||
const numericValue = (value) => (
|
||||
value !== null && value !== undefined && value !== "" && Number.isFinite(Number(value))
|
||||
? Number(value)
|
||||
: null
|
||||
);
|
||||
const temperature = numericValue(parameters.temperature_c);
|
||||
if (temperature !== null) return `${temperature}°C`;
|
||||
const position = numericValue(parameters.position_pct ?? parameters.shade_position_pct);
|
||||
if (position !== null) return `${position}%`;
|
||||
const illumination = String(parameters.color_hex || "").trim();
|
||||
const brightnessPercent = numericValue(parameters.brightness_pct);
|
||||
if (illumination && brightnessPercent !== null) return `${illumination} · ${brightnessPercent}%`;
|
||||
if (illumination) return illumination;
|
||||
if (brightnessPercent !== null) return `${brightnessPercent}%`;
|
||||
const brightness = numericValue(parameters.brightness_level);
|
||||
if (brightness !== null) return `${brightness}级`;
|
||||
const level = numericValue(parameters.level ?? parameters.fan_level);
|
||||
if (level !== null) return `${level}档`;
|
||||
const state = parameters.motion
|
||||
?? parameters.state
|
||||
?? parameters.power
|
||||
?? parameters.action
|
||||
?? parameters.mode;
|
||||
if (state !== null && state !== undefined && String(state).trim()) {
|
||||
return localizedAssistantControlValue(state);
|
||||
}
|
||||
const commandTail = String(item.command || "").trim().split(/\s+/).at(-1);
|
||||
return localizedAssistantControlValue(commandTail) || String(item.command || "控制");
|
||||
}
|
||||
|
||||
function assistantControlIcon(item = {}) {
|
||||
const capability = String(item.operation?.capability || "");
|
||||
const category = String(item.category || "");
|
||||
if (capability.startsWith("climate.") || category.includes("空调")) return "thermometer";
|
||||
if (capability.includes("reading") || category.includes("阅读灯")) return "book-open";
|
||||
if (capability.includes("light") || category.includes("灯")) return "lightbulb";
|
||||
if (capability.startsWith("roof.") || category.includes("遮阳帘")) return "panel-top";
|
||||
if (capability.startsWith("window.") || category.includes("车窗")) return "panels-top-left";
|
||||
if (capability.startsWith("seat.") || category.includes("座椅")) return "armchair";
|
||||
if (capability.startsWith("door.") || category.includes("门锁")) return "lock-keyhole";
|
||||
if (capability === "infrared.emit") return "radio-tower";
|
||||
return "binary";
|
||||
}
|
||||
|
||||
function appendDispatchedControls(message, result = {}) {
|
||||
if (!message?.body || !result || typeof result !== "object") return;
|
||||
const previous = message.dispatchedControlResult || {};
|
||||
const mergedResult = { ...previous, ...result };
|
||||
if (!Array.isArray(result.validated_control_commands)
|
||||
&& Array.isArray(previous.validated_control_commands)) {
|
||||
mergedResult.validated_control_commands = previous.validated_control_commands;
|
||||
}
|
||||
message.dispatchedControlResult = mergedResult;
|
||||
const commands = assistantDispatchedCommands(mergedResult);
|
||||
if (!commands.length) return;
|
||||
|
||||
const signature = JSON.stringify(commands.map((item) => [
|
||||
item.operationRef,
|
||||
item.category,
|
||||
item.command,
|
||||
item.semantic,
|
||||
item.deliveryState,
|
||||
item.deliveryLabel,
|
||||
item.deliveryDetail,
|
||||
]));
|
||||
const vueRenderer = window.CognitiveAgentVueUI?.renderDispatchedControls;
|
||||
if (typeof vueRenderer === "function") {
|
||||
let host = $(".assistant-dispatched-controls-vue-host", message.body);
|
||||
if (host?.dataset.commandSignature === signature) return;
|
||||
if (!host) {
|
||||
host = document.createElement("div");
|
||||
host.className = "assistant-dispatched-controls-vue-host";
|
||||
message.body.append(host);
|
||||
}
|
||||
const viewCommands = commands.map((item, index) => ({
|
||||
key: item.operationRef || `${item.semantic || item.command}-${index}`,
|
||||
category: String(item.category || "控制指令"),
|
||||
value: assistantControlValue(item),
|
||||
icon: assistantControlIcon(item),
|
||||
semantic: String(item.semantic || ""),
|
||||
command: String(item.command || ""),
|
||||
deliveryState: String(item.deliveryState || "not_submitted"),
|
||||
deliveryLabel: String(item.deliveryLabel || "未下发"),
|
||||
deliveryDetail: String(item.deliveryDetail || ""),
|
||||
}));
|
||||
if (vueRenderer(host, viewCommands)) {
|
||||
host.dataset.commandSignature = signature;
|
||||
message.article?.classList.add("assistant-message-with-dispatched-controls");
|
||||
followLatest(true);
|
||||
return;
|
||||
}
|
||||
host.remove();
|
||||
}
|
||||
let card = $(".assistant-dispatched-controls", message.body);
|
||||
if (card?.dataset.commandSignature === signature) return;
|
||||
if (!card) {
|
||||
card = document.createElement("details");
|
||||
card.className = "assistant-dispatched-controls";
|
||||
card.open = true;
|
||||
const summary = document.createElement("summary");
|
||||
const headingIcon = document.createElement("span");
|
||||
headingIcon.className = "assistant-dispatched-controls-heading-icon";
|
||||
headingIcon.innerHTML = '<i data-lucide="list-tree"></i>';
|
||||
const heading = document.createElement("b");
|
||||
heading.textContent = "下发控制指令";
|
||||
const count = document.createElement("strong");
|
||||
count.className = "assistant-dispatched-controls-count";
|
||||
const chevron = document.createElement("i");
|
||||
chevron.className = "assistant-dispatched-controls-chevron";
|
||||
chevron.setAttribute("data-lucide", "chevron-down");
|
||||
summary.append(headingIcon, heading, count, chevron);
|
||||
const list = document.createElement("ol");
|
||||
list.className = "assistant-dispatched-controls-list";
|
||||
card.append(summary, list);
|
||||
message.body.append(card);
|
||||
}
|
||||
|
||||
card.dataset.commandSignature = signature;
|
||||
const count = $(".assistant-dispatched-controls-count", card);
|
||||
const list = $(".assistant-dispatched-controls-list", card);
|
||||
if (count) count.textContent = String(commands.length);
|
||||
if (!list) return;
|
||||
list.replaceChildren();
|
||||
commands.forEach((item) => {
|
||||
const row = document.createElement("li");
|
||||
row.dataset.deliveryState = String(item.deliveryState || "not_submitted");
|
||||
const titleParts = [];
|
||||
if (item.semantic) titleParts.push(`语义控制指令:${item.semantic}`);
|
||||
if (item.command) titleParts.push(`实际控制指令:${item.command}`);
|
||||
if (item.deliveryLabel) {
|
||||
titleParts.push(`下发状态:${item.deliveryLabel}${item.deliveryDetail ? `(${item.deliveryDetail})` : ""}`);
|
||||
}
|
||||
row.title = titleParts.join("\n");
|
||||
|
||||
const icon = document.createElement("span");
|
||||
icon.className = "assistant-dispatched-control-icon";
|
||||
icon.innerHTML = `<i data-lucide="${assistantControlIcon(item)}"></i>`;
|
||||
const label = document.createElement("span");
|
||||
label.className = "assistant-dispatched-control-label";
|
||||
label.textContent = String(item.category || "控制指令");
|
||||
const value = document.createElement("strong");
|
||||
value.className = "assistant-dispatched-control-value";
|
||||
value.textContent = assistantControlValue(item);
|
||||
const accessibleCommand = document.createElement("small");
|
||||
accessibleCommand.className = "visually-hidden";
|
||||
accessibleCommand.textContent = titleParts.join(";");
|
||||
row.append(icon, label, value, accessibleCommand);
|
||||
list.append(row);
|
||||
});
|
||||
message.article?.classList.add("assistant-message-with-dispatched-controls");
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
followLatest(true);
|
||||
}
|
||||
|
||||
function appendModeCreatedState(message, profile, { updated = false } = {}) {
|
||||
if (!message?.body || !profile?.id) return;
|
||||
const selector = `[data-assistant-mode-created="${CSS.escape(String(profile.id))}"]`;
|
||||
@@ -921,6 +1197,7 @@
|
||||
"cognitive-agent:validated-can-commands",
|
||||
{ detail: event.payload },
|
||||
));
|
||||
appendDispatchedControls(message, event.payload);
|
||||
return;
|
||||
}
|
||||
if (event.type === "answer.delta") {
|
||||
@@ -952,6 +1229,7 @@
|
||||
);
|
||||
if (event.payload) {
|
||||
window.CognitiveAgentConsole?.applySyncedResult?.(event.payload);
|
||||
appendDispatchedControls(message, event.payload);
|
||||
if (
|
||||
event.payload.mode_profile_draft
|
||||
&& event.payload.orchestration_route === "mode_profile_create_preview"
|
||||
@@ -1165,6 +1443,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
function announceModeExecution(profile, result = null, { command = "", error = null } = {}) {
|
||||
const modeName = String(profile?.name || profile?.id || "功能模式");
|
||||
const commandText = String(command || `进入${modeName}`).trim();
|
||||
const status = String(result?.status || "");
|
||||
const dryRun = result?.execution_mode === "dry_run";
|
||||
const waitingForFeedback = result?.execution_outcome === "submitted_unverified";
|
||||
setDrawerOpen(true);
|
||||
createMessage("user", commandText);
|
||||
|
||||
let content = `${modeName}执行未完成,请查看控制指令状态。`;
|
||||
let statusText = "功能模式执行未完成";
|
||||
let statusState = "failed";
|
||||
if (error) {
|
||||
content = `${modeName}执行失败:${error.message || "请稍后重试。"}`;
|
||||
statusText = "功能模式执行失败";
|
||||
} else if (status === "succeeded") {
|
||||
content = dryRun
|
||||
? `已完成模拟编排:${commandText}。`
|
||||
: waitingForFeedback
|
||||
? `已提交执行:${commandText},正在等待车辆状态确认。`
|
||||
: `已完成执行:${commandText}。`;
|
||||
statusText = dryRun
|
||||
? "功能模式模拟编排已完成"
|
||||
: waitingForFeedback
|
||||
? "模式指令已提交,等待车辆状态确认"
|
||||
: "功能模式执行已完成";
|
||||
statusState = "success";
|
||||
} else if (status === "partially_succeeded") {
|
||||
content = `${modeName}仅部分完成,请查看未完成的控制指令。`;
|
||||
statusText = "功能模式部分完成";
|
||||
}
|
||||
|
||||
const message = createMessage("assistant", content);
|
||||
setActionStatus(message, statusText, statusState);
|
||||
if (result) appendDispatchedControls(message, result);
|
||||
persistMessages();
|
||||
}
|
||||
|
||||
function renderInitialMessages() {
|
||||
const container = $("#assistantMessages");
|
||||
if (!container) return;
|
||||
@@ -1221,6 +1537,7 @@
|
||||
open: () => setDrawerOpen(true),
|
||||
close: () => setDrawerOpen(false),
|
||||
startModeCreation,
|
||||
announceModeExecution,
|
||||
announceModeCreated(profile) {
|
||||
if (!profile?.id) return;
|
||||
setDrawerOpen(true);
|
||||
|
||||
Reference in New Issue
Block a user