feat: localize ai monitor ui

This commit is contained in:
Youzini-afk
2026-04-08 22:58:51 +08:00
parent 323e1fd5b0
commit 3dc0312c8c
2 changed files with 150 additions and 39 deletions

View File

@@ -239,7 +239,7 @@
</div>
<div class="bme-config-card">
<div class="bme-section-header">AI Monitor</div>
<div class="bme-section-header">任务监视器</div>
<div class="bme-config-help">
默认跟随调试总开关;开启后展示最近的记忆任务流水。
</div>
@@ -514,7 +514,7 @@
</div>
<div class="bme-cog-space-tools" id="bme-cog-space-tools"></div>
<div class="bme-cog-section-title" style="margin-top:16px">
<i class="fa-solid fa-wave-square"></i> AI Monitor
<i class="fa-solid fa-wave-square"></i> 任务监视器
</div>
<div class="bme-cog-monitor-mini" id="bme-cog-monitor-mini"></div>
</div>
@@ -1201,7 +1201,7 @@
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">AI Monitor</div>
<div class="bme-config-card-title">任务监视器</div>
<div class="bme-config-card-subtitle">
记录最近的提取、召回、压缩等任务流水;默认关闭,建议配合调试开关一起使用。
</div>
@@ -1209,7 +1209,7 @@
</div>
<label class="bme-toggle-item" for="bme-setting-ai-monitor-enabled">
<span class="bme-toggle-copy">
<span class="bme-toggle-title">启用 AI Monitor</span>
<span class="bme-toggle-title">启用任务监视器</span>
<span class="bme-toggle-desc">
开启后面板会显示最近任务的模型路由、清洗摘要和最终消息预览。
</span>

View File

@@ -1233,7 +1233,7 @@ function _renderCogMonitorMini() {
const settings = _getSettings?.() || {};
if (settings.enableAiMonitor !== true) {
el.innerHTML = `<div class="bme-cog-monitor-empty">AI Monitor 已关闭</div>`;
el.innerHTML = `<div class="bme-cog-monitor-empty">任务监视器已关闭</div>`;
return;
}
@@ -1246,8 +1246,6 @@ function _renderCogMonitorMini() {
return;
}
const taskTypeLabels = { extract: "提取", recall: "召回", compress: "压缩", sleep: "遗忘", evolve: "进化", embed: "向量" };
el.innerHTML = timeline
.slice(-8)
.reverse()
@@ -1256,15 +1254,18 @@ function _renderCogMonitorMini() {
const statusClass = status.includes("error") || status.includes("fail") ? "is-error"
: status.includes("run") ? "is-running" : "is-success";
const taskType = String(entry?.taskType || "unknown");
const route = String(entry?.route || entry?.llmConfigSourceLabel || entry?.model || "").trim();
const route =
_getMonitorRouteLabel(entry?.route) ||
_getMonitorRouteLabel(entry?.llmConfigSourceLabel) ||
String(entry?.model || "").trim();
const durationMs = Number(entry?.durationMs);
const durationText = Number.isFinite(durationMs) && durationMs > 0
? durationMs >= 1000 ? `${(durationMs / 1000).toFixed(1)}s` : `${Math.round(durationMs)}ms`
: "—";
return `
<div class="bme-cog-monitor-entry ${statusClass}">
<span class="bme-cog-monitor-badge">${_escHtml(taskTypeLabels[taskType] || taskType)}</span>
<span class="bme-cog-monitor-info">${_escHtml(route || "—")}</span>
<span class="bme-cog-monitor-badge">${_escHtml(_getMonitorTaskTypeLabel(taskType))}</span>
<span class="bme-cog-monitor-info">${_escHtml(route || _getMonitorStatusLabel(entry?.status) || "—")}</span>
<span class="bme-cog-monitor-duration">${_escHtml(durationText)}</span>
</div>`;
})
@@ -2007,7 +2008,7 @@ function _refreshAiMonitorDashboard() {
_renderMiniRecentList(
"bme-ai-monitor-list",
[],
"AI Monitor 已关闭",
"任务监视器已关闭",
);
return;
}
@@ -2022,15 +2023,18 @@ function _refreshAiMonitorDashboard() {
.slice(-6)
.reverse()
.map((entry) => {
const route = String(entry?.route || entry?.llmConfigSourceLabel || "").trim();
const route =
_getMonitorRouteLabel(entry?.route) ||
_getMonitorRouteLabel(entry?.llmConfigSourceLabel) ||
"";
const model = String(entry?.model || "").trim();
const durationText =
Number.isFinite(Number(entry?.durationMs)) && Number(entry.durationMs) > 0
? `${Math.round(Number(entry.durationMs))}ms`
: "";
return [
String(entry?.taskType || "unknown"),
String(entry?.status || ""),
_getMonitorTaskTypeLabel(entry?.taskType),
_getMonitorStatusLabel(entry?.status),
route || model ? `${route || model}` : "",
durationText,
]
@@ -5110,6 +5114,122 @@ function _formatDurationMs(durationMs) {
return `${(normalized / 1000).toFixed(normalized >= 10000 ? 0 : 1)}s`;
}
function _getMonitorTaskTypeLabel(taskType = "") {
const normalized = String(taskType || "").trim().toLowerCase();
const labels = {
extract: "提取",
recall: "召回",
consolidation: "整合",
compress: "压缩",
synopsis: "概要",
reflection: "反思",
sleep: "遗忘",
evolve: "进化",
embed: "向量",
rebuild: "重建",
};
return labels[normalized] || String(taskType || "未知任务");
}
function _getMonitorStatusLabel(status = "") {
const normalized = String(status || "").trim().toLowerCase();
if (!normalized) return "未知状态";
if (normalized.includes("error") || normalized.includes("fail")) return "失败";
if (normalized.includes("run")) return "运行中";
if (normalized.includes("queue")) return "排队中";
if (normalized.includes("pending")) return "等待中";
if (normalized.includes("skip")) return "已跳过";
if (normalized.includes("fallback")) return "已回退";
if (normalized.includes("disable")) return "已关闭";
if (
normalized.includes("success") ||
normalized.includes("complete") ||
normalized.includes("done") ||
normalized === "ok"
) {
return "成功";
}
return String(status || "未知状态");
}
function _getMonitorRoleLabel(role = "") {
const normalized = String(role || "").trim().toLowerCase();
const labels = {
system: "系统",
user: "用户",
assistant: "助手",
tool: "工具",
};
return labels[normalized] || String(role || "未知");
}
function _getMonitorRouteLabel(value = "") {
const normalized = String(value || "").trim();
if (!normalized) return "";
const labels = {
"dedicated-openai-compatible": "专用 OpenAI 兼容接口",
"sillytavern-current-model": "酒馆当前模型",
"dedicated-memory-llm": "专用记忆模型",
global: "跟随当前 API",
"task-preset": "任务专用模板",
"global-fallback-missing-task-preset": "任务模板缺失,已回退当前 API",
"global-fallback-invalid-task-preset": "任务模板不完整,已回退当前 API",
};
return labels[normalized] || normalized;
}
function _getMonitorStageLabel(stage = "") {
const normalized = String(stage || "").trim();
if (!normalized) return "—";
const labels = {
"input.userMessage": "输入阶段: 当前用户消息",
"input.recentMessages": "输入阶段: 最近消息",
"input.candidateText": "输入阶段: 候选文本",
"input.finalPrompt": "输入阶段: 最终提示词",
"output.rawResponse": "输出阶段: 原始响应",
"output.beforeParse": "输出阶段: 解析前",
"world-info-rendered": "世界书渲染后",
"final-injection-safe": "注入内容最终清洗",
"host:user_input": "宿主注入: 用户输入",
"host:ai_output": "宿主注入: AI 输出",
"host:world_info": "宿主注入: 世界书",
"host:reasoning": "宿主注入: 思维链/推理",
};
return labels[normalized] || normalized;
}
function _formatMonitorStageList(stages = []) {
if (!Array.isArray(stages) || !stages.length) return "—";
return stages
.map((entry) => _getMonitorStageLabel(entry?.stage || entry))
.filter(Boolean)
.join("、") || "—";
}
function _getMonitorEjsStatusLabel(status = "") {
const normalized = String(status || "").trim().toLowerCase();
if (!normalized) return "";
const labels = {
primary: "主运行时",
fallback: "回退运行时",
failed: "不可用",
};
return labels[normalized] || String(status || "");
}
function _formatMonitorRouteInfo(entry = {}) {
const parts = [
_getMonitorRouteLabel(entry?.route),
_getMonitorRouteLabel(entry?.llmConfigSourceLabel),
String(entry?.model || "").trim() ? `模型:${String(entry.model).trim()}` : "",
].filter(Boolean);
const uniqueParts = [];
for (const part of parts) {
if (!uniqueParts.includes(part)) uniqueParts.push(part);
}
return uniqueParts.join(" · ") || "未记录路由信息";
}
function _summarizeMonitorGovernance(entry = {}) {
const promptExecution = entry?.promptExecution || {};
const worldInfo = promptExecution?.worldInfo || null;
@@ -5122,11 +5242,11 @@ function _summarizeMonitorGovernance(entry = {}) {
if (worldInfo) {
lines.push(
`世界书: ${worldInfo.hit ? "命中" : "未命中"} · before ${Number(worldInfo.beforeCount || 0)} · after ${Number(worldInfo.afterCount || 0)} · atDepth ${Number(worldInfo.atDepthCount || 0)}`,
`世界书: ${worldInfo.hit ? "命中" : "未命中"} · 前置 ${Number(worldInfo.beforeCount || 0)} · 后置 ${Number(worldInfo.afterCount || 0)} · 深度 ${Number(worldInfo.atDepthCount || 0)}`,
);
}
if (promptExecution?.ejsRuntimeStatus) {
lines.push(`EJS: ${String(promptExecution.ejsRuntimeStatus || "")}`);
lines.push(`EJS: ${_getMonitorEjsStatusLabel(promptExecution.ejsRuntimeStatus)}`);
}
if (regexInput.length > 0) {
const appliedRuleCount = regexInput.reduce(
@@ -5137,12 +5257,12 @@ function _summarizeMonitorGovernance(entry = {}) {
}
if (requestCleaning) {
lines.push(
`发送前清洗: ${requestCleaning.changed ? "有改动" : "无改动"} · stage ${Array.isArray(requestCleaning.stages) ? requestCleaning.stages.join(", ") : "—"}`,
`发送前清洗: ${requestCleaning.changed ? "有改动" : "无改动"} · 阶段 ${_formatMonitorStageList(requestCleaning.stages)}`,
);
}
if (responseCleaning) {
lines.push(
`响应清洗: ${responseCleaning.changed ? "有改动" : "无改动"} · stage ${Array.isArray(responseCleaning.stages) ? responseCleaning.stages.join(", ") : "—"}`,
`响应清洗: ${responseCleaning.changed ? "有改动" : "无改动"} · 阶段 ${_formatMonitorStageList(responseCleaning.stages)}`,
);
}
if (entry?.jsonFailure?.failureReason) {
@@ -5162,28 +5282,22 @@ function _renderAiMonitorTraceCard(state) {
const timeline = Array.isArray(state.taskTimeline) ? state.taskTimeline : [];
if (state.settings?.enableAiMonitor !== true) {
return `
<div class="bme-config-card-title">AI Monitor 任务流水</div>
<div class="bme-config-card-title">任务监视器流水</div>
<div class="bme-config-help">
AI Monitor 当前关闭。打开后,这里会保留最近的提取 / 召回 / 维护任务快照,便于排查到底发了什么、用了哪套模型、做了哪些清洗。
任务监视器当前关闭。打开后,这里会保留最近的提取 / 召回 / 维护任务快照,便于排查到底发了什么、用了哪套模型、做了哪些清洗。
</div>
`;
}
if (!timeline.length) {
return `
<div class="bme-config-card-title">AI Monitor 任务流水</div>
<div class="bme-config-card-title">任务监视器流水</div>
<div class="bme-config-help">
还没有任务流水。等提取、召回或维护任务跑过一轮后,这里就会出现最近记录。
</div>
`;
}
const taskTypeLabels = {
extract: "提取", recall: "召回", compress: "压缩",
sleep: "遗忘", evolve: "进化", embed: "向量",
synopsis: "概要", rebuild: "重建",
};
const cards = timeline
.slice(-8)
.reverse()
@@ -5196,17 +5310,14 @@ function _renderAiMonitorTraceCard(state) {
String(entry?.model || "").trim() ||
"未知模型";
const taskType = String(entry?.taskType || "unknown");
const taskLabel = taskTypeLabels[taskType] || taskType;
const taskLabel = _getMonitorTaskTypeLabel(taskType);
const status = String(entry?.status || "").toLowerCase();
const dotClass = status.includes("error") || status.includes("fail")
? "dot-error"
: status.includes("run")
? "dot-running"
: "dot-success";
const routeInfo = [
String(entry?.route || "").trim(),
String(entry?.llmConfigSourceLabel || "").trim(),
].filter(Boolean).join(" · ") || "未记录路由信息";
const routeInfo = _formatMonitorRouteInfo(entry);
// Governance tags
const govTags = [];
@@ -5239,7 +5350,7 @@ function _renderAiMonitorTraceCard(state) {
<div class="bme-ai-monitor-entry__meta">
${_escHtml(
[
String(entry?.status || "unknown"),
_getMonitorStatusLabel(entry?.status),
_formatTaskProfileTime(entry?.updatedAt),
].filter(Boolean).join(" · "),
)}
@@ -5262,9 +5373,9 @@ function _renderAiMonitorTraceCard(state) {
: ""
}
${_renderMessageTraceTextBlock(
"最终发送 messages 预览",
"最终发送消息预览",
previewText,
"这条任务没有捕获到完整的 messages 预览。",
"这条任务没有捕获到完整的消息预览。",
)}
</div>
</div>
@@ -5276,7 +5387,7 @@ function _renderAiMonitorTraceCard(state) {
return `
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">AI Monitor 任务流水</div>
<div class="bme-config-card-title">任务监视器流水</div>
<div class="bme-config-card-subtitle">
最近 ${Math.min(timeline.length, 8)} 条任务快照 · 点击展开查看详情
</div>
@@ -5393,7 +5504,7 @@ function _stringifyTraceMessages(messages = []) {
return normalizedMessages
.map(
(message) => `${message.role}\n${message.content}`,
(message) => `${_getMonitorRoleLabel(message.role)}\n${message.content}`,
)
.join("\n\n---\n\n");
}
@@ -5404,10 +5515,10 @@ function _buildMainAiTraceText(triggeredUserMessage = "", injectionText = "") {
const normalizedInjectionText = String(injectionText || "").trim();
if (normalizedUserMessage) {
sections.push(`user\n${normalizedUserMessage}`);
sections.push(`用户\n${normalizedUserMessage}`);
}
if (normalizedInjectionText) {
sections.push(`memory injection\n${normalizedInjectionText}`);
sections.push(`记忆注入\n${normalizedInjectionText}`);
}
return sections.join("\n\n---\n\n").trim();