fix: support conversational mode draft updates
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
(function initializeUnifiedAssistant() {
|
||||
const STORAGE_KEY = "cognitive-agent.assistant-messages";
|
||||
const CONVERSATION_STORAGE_KEY = "cognitive-agent.conversation-id";
|
||||
const ACTIVE_MODE_DRAFT_STORAGE_KEY = "cognitive-agent.active-mode-draft";
|
||||
const MAX_STORED_MESSAGES = 24;
|
||||
const COMPOSER_MIN_HEIGHT = 64;
|
||||
const COMPOSER_MAX_HEIGHT = 116;
|
||||
@@ -8,6 +9,7 @@
|
||||
let pendingController = null;
|
||||
let pendingMessage = null;
|
||||
let latestControlResult = null;
|
||||
let activeModeDraftContext = null;
|
||||
let initialized = false;
|
||||
|
||||
const $ = (selector, root = document) => root.querySelector(selector);
|
||||
@@ -106,6 +108,108 @@
|
||||
return created;
|
||||
}
|
||||
|
||||
function normalizeModeName(value) {
|
||||
return String(value || "").trim().replace(/\s+/g, "").toLowerCase();
|
||||
}
|
||||
|
||||
function sanitizeModeDraftContext(value) {
|
||||
if (!value || typeof value !== "object") return null;
|
||||
const name = String(value.name || "").trim();
|
||||
const sourceRequestId = String(value.source_request_id || "").trim();
|
||||
const vehicleId = String(value.vehicle_id || "").trim();
|
||||
const draftConversationId = String(value.conversation_id || "").trim();
|
||||
if (
|
||||
!name
|
||||
|| name.length > 64
|
||||
|| !/^req-[a-z0-9-]{6,100}$/.test(sourceRequestId)
|
||||
) return null;
|
||||
if (!vehicleId || vehicleId.length > 80 || draftConversationId !== conversationId()) return null;
|
||||
return {
|
||||
name,
|
||||
source_request_id: sourceRequestId,
|
||||
vehicle_id: vehicleId,
|
||||
conversation_id: draftConversationId,
|
||||
};
|
||||
}
|
||||
|
||||
function loadActiveModeDraftContext() {
|
||||
try {
|
||||
return sanitizeModeDraftContext(
|
||||
JSON.parse(sessionStorage.getItem(ACTIVE_MODE_DRAFT_STORAGE_KEY) || "null"),
|
||||
);
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function rememberModeDraftContext(draft) {
|
||||
const candidate = sanitizeModeDraftContext({
|
||||
name: draft?.name,
|
||||
source_request_id: draft?.source_request_id,
|
||||
vehicle_id: draft?.vehicle_id || $("#vehicleSelect")?.value || null,
|
||||
conversation_id: conversationId(),
|
||||
});
|
||||
if (!candidate) return;
|
||||
activeModeDraftContext = candidate;
|
||||
sessionStorage.setItem(ACTIVE_MODE_DRAFT_STORAGE_KEY, JSON.stringify(candidate));
|
||||
}
|
||||
|
||||
function clearActiveModeDraftContext(sourceRequestId = null) {
|
||||
if (
|
||||
sourceRequestId
|
||||
&& activeModeDraftContext?.source_request_id !== String(sourceRequestId)
|
||||
) return;
|
||||
activeModeDraftContext = null;
|
||||
sessionStorage.removeItem(ACTIVE_MODE_DRAFT_STORAGE_KEY);
|
||||
}
|
||||
|
||||
function isModeDraftUpdateRequest(message, context) {
|
||||
const compact = String(message || "").trim().replace(/\s+/g, "");
|
||||
if (!compact || !context?.name) return false;
|
||||
const politePrefix = "(?:(?:请|麻烦)(?:帮我|帮忙|给我)?|帮我|帮忙|给我)?";
|
||||
const prefixUpdateVerb = "(?:修改一下|修改|调整|更改|更新|改一下|变更)";
|
||||
const changePredicate = "(?:修改|改成|改为|更改|更新|变更|变为|换成|调整|调节|调高|调低|调到|调成|调为|调至|设置|设为|设成|设到|设至|打开|开启|启动|关闭|关掉|关上|全关|停止|停用|切换|进入|退出|移动|前移|后退|前进|升高|降低|抬高|放低|降下|升起|调直|放倒|复位|半开|开到|关到|拉开|拉上|上锁|解锁)";
|
||||
const modeName = "([^,。!?!?、;;::]{1,64}?模式)";
|
||||
const modePossessiveBoundary = "(?:中的|里的|内的|之中的|的|中|里|内)";
|
||||
const modeMemberBoundary = `(?:${modePossessiveBoundary}|[,,::])`;
|
||||
const question = /为什么|什么|怎么|如何|介绍|解释|作用|区别|原理|哪些|多少|几(?:度|档|级|个|项)?|是否|能否|可不可以|合适吗|合理吗|会怎样|列出|告诉我|[??]|吗[。.!!]?$/;
|
||||
const candidates = [
|
||||
{
|
||||
pattern: new RegExp(`^${politePrefix}${prefixUpdateVerb}${modeName}${modeMemberBoundary}[,,::]?(.*)$`),
|
||||
predicateRequired: false,
|
||||
},
|
||||
{
|
||||
pattern: new RegExp(`^${politePrefix}(?:把|将)${modeName}${modeMemberBoundary}[,,::]?(.+)$`),
|
||||
predicateRequired: true,
|
||||
},
|
||||
{
|
||||
pattern: new RegExp(`^${politePrefix}${modeName}${modeMemberBoundary}[,,::]?(.+)$`),
|
||||
predicateRequired: true,
|
||||
},
|
||||
];
|
||||
return candidates.some(({ pattern, predicateRequired }) => {
|
||||
const matched = compact.match(pattern);
|
||||
const change = String(matched?.[2] || "");
|
||||
return Boolean(
|
||||
matched
|
||||
&& normalizeModeName(matched[1]) === normalizeModeName(context.name)
|
||||
&& !question.test(change)
|
||||
&& (!predicateRequired || new RegExp(changePredicate).test(change))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function requestModeDraftContext(message) {
|
||||
const context = activeModeDraftContext || loadActiveModeDraftContext();
|
||||
if (!context || !isModeDraftUpdateRequest(message, context)) return null;
|
||||
activeModeDraftContext = context;
|
||||
return {
|
||||
name: context.name,
|
||||
source_request_id: context.source_request_id,
|
||||
vehicle_id: context.vehicle_id,
|
||||
};
|
||||
}
|
||||
|
||||
function loadStoredMessages() {
|
||||
try {
|
||||
const stored = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || "[]");
|
||||
@@ -475,6 +579,7 @@
|
||||
status.className = "assistant-mode-draft-status success";
|
||||
status.textContent = existingModeId ? "模式已更新" : "模式已保存";
|
||||
card.querySelector(".assistant-mode-draft-actions")?.setAttribute("hidden", "");
|
||||
clearActiveModeDraftContext(draft.source_request_id);
|
||||
await window.CognitiveAgentConsole?.refreshModeProfiles?.(payload.mode_profile?.id);
|
||||
appendModeCreatedState(message, payload.mode_profile, { updated: Boolean(existingModeId) });
|
||||
persistMessages();
|
||||
@@ -506,13 +611,40 @@
|
||||
}
|
||||
}
|
||||
|
||||
function supersedeModeDraftCards(draft) {
|
||||
const nextName = normalizeModeName(draft?.name);
|
||||
const nextRequestId = String(draft?.source_request_id || "");
|
||||
if (!nextName || !nextRequestId) return;
|
||||
document.querySelectorAll("[data-assistant-mode-draft]").forEach((card) => {
|
||||
if (
|
||||
card.dataset.assistantModeName !== nextName
|
||||
|| card.dataset.assistantModeDraft === nextRequestId
|
||||
) return;
|
||||
card.classList.remove("is-busy");
|
||||
card.classList.add("is-superseded");
|
||||
card.querySelectorAll("button").forEach((button) => {
|
||||
button.disabled = true;
|
||||
});
|
||||
card.querySelector(".assistant-mode-draft-actions")?.setAttribute("hidden", "");
|
||||
const priorStatus = card.querySelector(".assistant-mode-draft-status");
|
||||
if (priorStatus) {
|
||||
priorStatus.hidden = false;
|
||||
priorStatus.className = "assistant-mode-draft-status superseded";
|
||||
priorStatus.textContent = "已由下方最新草稿替换";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function appendModeDraftState(message, draft, actions) {
|
||||
if (!message?.body || !draft?.name || !draft?.source_request_id) return;
|
||||
rememberModeDraftContext(draft);
|
||||
supersedeModeDraftCards(draft);
|
||||
const selector = `[data-assistant-mode-draft="${CSS.escape(String(draft.source_request_id))}"]`;
|
||||
if ($(selector, message.body)) return;
|
||||
const card = document.createElement("section");
|
||||
card.className = "assistant-mode-draft";
|
||||
card.dataset.assistantModeDraft = String(draft.source_request_id);
|
||||
card.dataset.assistantModeName = normalizeModeName(draft.name);
|
||||
message.article?.classList.add("assistant-message-mode-draft");
|
||||
|
||||
const heading = document.createElement("header");
|
||||
@@ -613,6 +745,7 @@
|
||||
const controlTransport = localStorage.getItem("cognitive-agent.control-transport") || "wired_can";
|
||||
const hardwareClientId = localStorage.getItem("cognitive-agent.hardware-client-id");
|
||||
const vehicleId = $("#vehicleSelect")?.value || null;
|
||||
const modeProfileDraftContext = requestModeDraftContext(message);
|
||||
return {
|
||||
message,
|
||||
conversation_id: conversationId(),
|
||||
@@ -620,6 +753,9 @@
|
||||
execution_mode: executionMode,
|
||||
control_transport: controlTransport,
|
||||
...(vehicleId ? { vehicle_id: vehicleId } : {}),
|
||||
...(modeProfileDraftContext
|
||||
? { mode_profile_draft_context: modeProfileDraftContext }
|
||||
: {}),
|
||||
...(executionMode === "hardware" && controlTransport !== "wireless_mqtt" && hardwareClientId
|
||||
? { hardware_client_id: hardwareClientId }
|
||||
: {}),
|
||||
@@ -681,6 +817,7 @@
|
||||
event.payload.mode_profile
|
||||
&& event.payload.orchestration_route === "mode_profile_create"
|
||||
) {
|
||||
clearActiveModeDraftContext();
|
||||
appendModeCreatedState(message, event.payload.mode_profile);
|
||||
}
|
||||
}
|
||||
@@ -859,6 +996,7 @@
|
||||
resizeComposerInput(input);
|
||||
}, 300);
|
||||
renderInitialMessages();
|
||||
activeModeDraftContext = loadActiveModeDraftContext();
|
||||
setComposerPending(false);
|
||||
}
|
||||
|
||||
@@ -873,6 +1011,7 @@
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (!target?.closest("#newConversation")) return;
|
||||
sessionStorage.removeItem(STORAGE_KEY);
|
||||
clearActiveModeDraftContext();
|
||||
sessionStorage.setItem(CONVERSATION_STORAGE_KEY, createConversationId());
|
||||
}, true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user