feat: integrate production cockpit AI assistant
This commit is contained in:
@@ -169,6 +169,7 @@ function createHarness(events) {
|
||||
const storage = new Map();
|
||||
const requestCalls = [];
|
||||
const completed = [];
|
||||
const refreshedModeProfiles = [];
|
||||
windowEvents.addEventListener("cognitive-agent:request-complete", (event) => {
|
||||
completed.push(event.detail);
|
||||
});
|
||||
@@ -207,7 +208,12 @@ function createHarness(events) {
|
||||
sandbox.addEventListener = windowEvents.addEventListener.bind(windowEvents);
|
||||
sandbox.removeEventListener = windowEvents.removeEventListener.bind(windowEvents);
|
||||
sandbox.dispatchEvent = windowEvents.dispatchEvent.bind(windowEvents);
|
||||
sandbox.CognitiveAgentConsole = { getExecutionMode: () => "dry_run" };
|
||||
sandbox.CognitiveAgentConsole = {
|
||||
getExecutionMode: () => "dry_run",
|
||||
async refreshModeProfiles(modeProfileId) {
|
||||
refreshedModeProfiles.push(modeProfileId);
|
||||
},
|
||||
};
|
||||
sandbox.CognitiveAgentNetwork = {
|
||||
async request(url, options) {
|
||||
requestCalls.push({ url, options });
|
||||
@@ -225,15 +231,22 @@ function createHarness(events) {
|
||||
};
|
||||
vm.createContext(sandbox);
|
||||
vm.runInContext(ASSISTANT_SOURCE, sandbox, { filename: "assistant-chat.js" });
|
||||
return { completed, input, messages, requestCalls, sandbox };
|
||||
return {
|
||||
completed,
|
||||
input,
|
||||
messages,
|
||||
refreshedModeProfiles,
|
||||
requestCalls,
|
||||
sandbox,
|
||||
};
|
||||
}
|
||||
|
||||
test("a late NDJSON model error is rendered after an HTTP 200 stream", async () => {
|
||||
const publicMessage = "Codex AI 助手暂时没有响应,请稍后再试。";
|
||||
const publicMessage = "小迪暂时没有响应,请稍后再试。";
|
||||
const harness = createHarness([
|
||||
{ type: "message.accepted", route: "agent", engine: "codex-app-server" },
|
||||
{ type: "work.started", display_text: "Codex 正在理解并处理……" },
|
||||
{ type: "work.progress", display_text: "Codex 正在重新连接……" },
|
||||
{ type: "work.started", display_text: "小迪正在理解并处理……" },
|
||||
{ type: "work.progress", display_text: "小迪正在重新连接……" },
|
||||
{ type: "error", error: "assistant_model_failed", message: publicMessage },
|
||||
]);
|
||||
harness.input.value = "解释一下 CAN FD";
|
||||
@@ -261,7 +274,7 @@ test("a completed NDJSON answer remains the authoritative visible response", asy
|
||||
const harness = createHarness([
|
||||
{ type: "message.accepted", route: "agent", engine: "codex-app-server" },
|
||||
{ type: "answer.delta", delta: "流式草稿" },
|
||||
{ type: "work.completed", success: true, display_text: "Codex 已完成" },
|
||||
{ type: "work.completed", success: true, display_text: "小迪已完成" },
|
||||
{ type: "answer.completed", route: "agent", answer: "最终回答" },
|
||||
]);
|
||||
harness.input.value = "普通问题";
|
||||
@@ -274,6 +287,138 @@ test("a completed NDJSON answer remains the authoritative visible response", asy
|
||||
assert.equal(harness.completed[0].error, null);
|
||||
});
|
||||
|
||||
test("unsupported-control suggestions render once and click through as a new command", async () => {
|
||||
const suggestions = [
|
||||
{ id: "climate-on", label: "打开空调", command: "打开空调", domain: "climate" },
|
||||
{ id: "shade-close", label: "关闭遮阳帘", command: "关闭遮阳帘", domain: "roof" },
|
||||
{ id: "climate-copy", label: "重复项", command: " 打开 空调 ", domain: "climate" },
|
||||
];
|
||||
const events = [
|
||||
{
|
||||
type: "control.suggestions",
|
||||
message_id: "msg-unsupported",
|
||||
suggestions,
|
||||
},
|
||||
{
|
||||
type: "answer.completed",
|
||||
route: "unsupported_control",
|
||||
engine: "n7web-capability-registry",
|
||||
answer: "当前车端暂不支持该功能控制哦,您可以尝试以下操作。",
|
||||
suggestions,
|
||||
},
|
||||
];
|
||||
const harness = createHarness(events);
|
||||
harness.input.value = "打开后备箱";
|
||||
|
||||
await harness.sandbox.CognitiveAgentAssistant.submit();
|
||||
|
||||
const suggestionGroups = harness.messages.querySelectorAll(".assistant-control-suggestions");
|
||||
assert.equal(suggestionGroups.length, 1);
|
||||
const buttons = suggestionGroups[0].querySelectorAll("button");
|
||||
assert.equal(buttons.length, 2);
|
||||
assert.equal(buttons[0].type, "button");
|
||||
assert.equal(buttons[0].dataset.suggestionCommand, "打开空调");
|
||||
assert.equal(buttons[0].attributes.get("aria-label"), "执行控制指令:打开空调");
|
||||
assert.equal(buttons[0].attributes.get("aria-busy"), "false");
|
||||
|
||||
buttons[0].dispatchEvent({ type: "click" });
|
||||
buttons[0].dispatchEvent({ type: "click" });
|
||||
assert.equal(buttons.every((button) => button.disabled), true);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
const streamCalls = harness.requestCalls.filter(
|
||||
(call) => call.url === "/api/assistant/stream",
|
||||
);
|
||||
assert.equal(streamCalls.length, 2);
|
||||
assert.equal(JSON.parse(streamCalls[1].options.body).message, "打开空调");
|
||||
assert.equal(harness.messages.children[2].dataset.messageContent, "打开空调");
|
||||
assert.equal(buttons.every((button) => !button.disabled), true);
|
||||
assert.equal(buttons[0].attributes.get("aria-busy"), "false");
|
||||
});
|
||||
|
||||
test("answer fallback accepts recommended_commands and caps unsafe suggestion payloads", async () => {
|
||||
const harness = createHarness([
|
||||
{
|
||||
type: "answer.completed",
|
||||
route: "unsupported_control",
|
||||
answer: "当前车端暂不支持该功能控制哦。",
|
||||
recommended_commands: [
|
||||
"打开空调",
|
||||
{ id: "seat", label: "调节座椅", command: "副驾座椅调到80%", domain: "seat" },
|
||||
{ id: "empty", label: "空指令", command: "" },
|
||||
{ id: "window", label: "关闭车窗", command: "关闭所有车窗", domain: "window" },
|
||||
{ id: "light", label: "打开氛围灯", command: "打开氛围灯", domain: "lighting" },
|
||||
{ id: "extra", label: "不应显示", command: "空调调到25度", domain: "climate" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
harness.input.value = "打开后备箱";
|
||||
|
||||
await harness.sandbox.CognitiveAgentAssistant.submit();
|
||||
|
||||
const group = harness.messages.querySelector(".assistant-control-suggestions");
|
||||
assert.ok(group);
|
||||
const buttons = group.querySelectorAll("button");
|
||||
assert.equal(buttons.length, 4);
|
||||
assert.deepEqual(
|
||||
buttons.map((button) => button.dataset.suggestionCommand),
|
||||
["打开空调", "副驾座椅调到80%", "关闭所有车窗", "打开氛围灯"],
|
||||
);
|
||||
buttons.forEach((button) => {
|
||||
assert.equal(button.type, "button");
|
||||
assert.match(button.attributes.get("aria-label"), /^执行控制指令:/);
|
||||
});
|
||||
});
|
||||
|
||||
test("a natural-language saved-mode update refreshes the mode gallery", async () => {
|
||||
const harness = createHarness([
|
||||
{ type: "message.accepted", route: "mode_profile_management", engine: "n7web-mode-registry" },
|
||||
{
|
||||
type: "action.completed",
|
||||
success: true,
|
||||
payload: {
|
||||
status: "succeeded",
|
||||
execution_mode: "dry_run",
|
||||
orchestration_route: "mode_profile_update",
|
||||
mode_profile: { id: "mode-reading", name: "阅读模式" },
|
||||
},
|
||||
},
|
||||
{ type: "answer.completed", route: "mode_profile_management", answer: "已更新阅读模式。" },
|
||||
]);
|
||||
harness.input.value = "把阅读模式中空调温度调整到26度";
|
||||
|
||||
await harness.sandbox.CognitiveAgentAssistant.submit();
|
||||
|
||||
assert.deepEqual(harness.refreshedModeProfiles, ["mode-reading"]);
|
||||
const state = harness.messages.querySelectorAll(".assistant-mode-created");
|
||||
assert.equal(state.length, 1);
|
||||
assert.equal(state[0].children[1].children[0].textContent, "已更新「我的模式」");
|
||||
});
|
||||
|
||||
test("a natural-language saved-mode deletion refreshes the mode gallery", async () => {
|
||||
const harness = createHarness([
|
||||
{ type: "message.accepted", route: "mode_profile_management", engine: "n7web-mode-registry" },
|
||||
{
|
||||
type: "action.completed",
|
||||
success: true,
|
||||
payload: {
|
||||
status: "succeeded",
|
||||
execution_mode: "dry_run",
|
||||
orchestration_route: "mode_profile_delete",
|
||||
deleted_mode_profile: { id: "mode-reading", name: "阅读模式" },
|
||||
},
|
||||
},
|
||||
{ type: "answer.completed", route: "mode_profile_management", answer: "已删除阅读模式。" },
|
||||
]);
|
||||
harness.input.value = "删除阅读模式";
|
||||
|
||||
await harness.sandbox.CognitiveAgentAssistant.submit();
|
||||
|
||||
assert.deepEqual(harness.refreshedModeProfiles, [undefined]);
|
||||
assert.equal(harness.messages.querySelectorAll(".assistant-mode-created").length, 0);
|
||||
});
|
||||
|
||||
function modeDraftEvents(sourceRequestId, temperatureC) {
|
||||
return [
|
||||
{ type: "message.accepted", route: "vehicle_control", engine: "n7web-mode-builder" },
|
||||
|
||||
Reference in New Issue
Block a user