mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-10 14:28:43 +02:00
Add files via upload
This commit is contained in:
+12
-3
@@ -213,12 +213,12 @@ html[data-theme="dark"] .c2-session-main-empty__icon {
|
||||
min-width: 0;
|
||||
min-height: 40px;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--c2-border);
|
||||
border: 1.5px solid var(--c2-border);
|
||||
border-radius: var(--c2-radius-sm);
|
||||
background: var(--c2-surface);
|
||||
color: var(--c2-text);
|
||||
font-size: 14px;
|
||||
line-height: 1.25;
|
||||
line-height: 1.5;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
@@ -230,7 +230,16 @@ html[data-theme="dark"] .c2-session-main-empty__icon {
|
||||
|
||||
.c2-form-select-ui.open .c2-form-select-trigger {
|
||||
border-color: var(--c2-accent);
|
||||
box-shadow: 0 0 0 2px var(--c2-accent-dim);
|
||||
box-shadow: 0 0 0 3px var(--c2-accent-dim);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-modal .c2-form-select-trigger {
|
||||
background: var(--c2-surface);
|
||||
border-color: var(--c2-border);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .c2-modal .c2-form-select-ui.open .c2-form-select-trigger {
|
||||
border-color: var(--c2-accent);
|
||||
}
|
||||
|
||||
.c2-form-select-ui.open {
|
||||
|
||||
+280
-8
@@ -4190,6 +4190,41 @@ html[data-theme="dark"] .openapi-doc-btn:hover {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.mcp-detail-btn[data-exec-id] {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.mcp-detail-btn[data-status="completed"] {
|
||||
color: var(--text-primary);
|
||||
border-color: rgba(40, 167, 69, 0.34);
|
||||
background: rgba(40, 167, 69, 0.055);
|
||||
}
|
||||
|
||||
.mcp-detail-btn[data-status="failed"] {
|
||||
color: var(--text-primary);
|
||||
border-color: rgba(220, 53, 69, 0.36);
|
||||
background: rgba(220, 53, 69, 0.06);
|
||||
}
|
||||
|
||||
.mcp-detail-btn[data-status="running"],
|
||||
.mcp-detail-btn[data-status="pending"] {
|
||||
color: var(--text-primary);
|
||||
border-color: rgba(0, 102, 255, 0.32);
|
||||
background: rgba(0, 102, 255, 0.055);
|
||||
}
|
||||
|
||||
.mcp-detail-btn[data-status="cancelled"] {
|
||||
color: var(--text-muted);
|
||||
border-color: rgba(148, 163, 184, 0.32);
|
||||
background: rgba(148, 163, 184, 0.065);
|
||||
}
|
||||
|
||||
.mcp-detail-btn .mcp-tool-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 登录遮罩 */
|
||||
.login-overlay {
|
||||
position: fixed;
|
||||
@@ -5163,7 +5198,14 @@ html[data-theme="dark"] .openapi-doc-btn:hover {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 过程详情 Markdown:避免长 URL/代码/表格撑破紫/蓝时间线条目 */
|
||||
/* 过程详情纯文本:避免长 URL/代码/表格撑破时间线条目 */
|
||||
.timeline-item-content .timeline-plain-text {
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.timeline-item-content p,
|
||||
.timeline-item-content li,
|
||||
.timeline-item-content td,
|
||||
@@ -5200,6 +5242,16 @@ html[data-theme="dark"] .openapi-doc-btn:hover {
|
||||
contain-intrinsic-size: auto 72px;
|
||||
}
|
||||
|
||||
.timeline-live-pruned-marker {
|
||||
margin: 8px 0;
|
||||
padding: 8px 12px;
|
||||
border: 1px dashed rgba(148, 163, 184, 0.35);
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
background: rgba(148, 163, 184, 0.08);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.tool-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -5481,20 +5533,73 @@ html[data-theme="dark"] .openapi-doc-btn:hover {
|
||||
border: 1px solid rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
|
||||
/* 工具调用项状态:全程保持「信息蓝」系,完成态不用绿色(避免与工具成功结果混淆) */
|
||||
/* 工具调用项状态:摘要态直接表达成功/失败,避免必须展开才能判断状态 */
|
||||
.timeline-item-tool_call.tool-call-running {
|
||||
border-left-color: #42a5f5;
|
||||
background: rgba(66, 165, 245, 0.1);
|
||||
border: 1px solid rgba(66, 165, 245, 0.32);
|
||||
border-left: 3px solid #42a5f5;
|
||||
background: rgba(66, 165, 245, 0.075);
|
||||
}
|
||||
|
||||
.timeline-item-tool_call.tool-call-completed {
|
||||
border-left-color: #0d47a1;
|
||||
background: rgba(13, 71, 161, 0.08);
|
||||
border: 1px solid rgba(40, 167, 69, 0.34);
|
||||
border-left: 3px solid var(--success-color);
|
||||
background: rgba(40, 167, 69, 0.075);
|
||||
}
|
||||
|
||||
.timeline-item-tool_call.tool-call-failed {
|
||||
border-left-color: var(--error-color);
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
border: 1px solid rgba(220, 53, 69, 0.36);
|
||||
border-left: 3px solid var(--error-color);
|
||||
background: rgba(220, 53, 69, 0.085);
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding-right: 104px;
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header:hover .timeline-item-title {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header::after {
|
||||
content: attr(data-tool-detail-label);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(96, 165, 250, 0.35);
|
||||
color: #93c5fd;
|
||||
background: rgba(37, 99, 235, 0.12);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header:hover::after {
|
||||
color: #dbeafe;
|
||||
border-color: rgba(147, 197, 253, 0.65);
|
||||
background: rgba(37, 99, 235, 0.22);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.timeline-item.tool-detail-collapsible .timeline-item-header::after {
|
||||
position: static;
|
||||
transform: none;
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 参数块与卡片类型色弱对齐,扫读时一眼归到「调用」 */
|
||||
@@ -29722,6 +29827,173 @@ html[data-theme="dark"] #skill-view-body {
|
||||
border-color: #263244;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-content .timeline-plain-text {
|
||||
background: transparent !important;
|
||||
border-color: transparent !important;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-content.timeline-stream-plain {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .progress-container {
|
||||
background: #0f172a;
|
||||
border-color: #243044;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message.system .message-bubble.progress-container {
|
||||
background: #0b1120 !important;
|
||||
color: var(--text-primary);
|
||||
border-color: #203047 !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .progress-container.completed {
|
||||
background: #111827;
|
||||
border-color: #263244;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .chat-scroll-to-bottom {
|
||||
background: rgba(15, 23, 42, 0.94);
|
||||
color: #dbeafe;
|
||||
border-color: rgba(96, 165, 250, 0.36);
|
||||
box-shadow:
|
||||
0 10px 24px rgba(0, 0, 0, 0.34),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.03) inset;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .chat-scroll-to-bottom:hover {
|
||||
background: #172033;
|
||||
color: #ffffff;
|
||||
border-color: rgba(147, 197, 253, 0.62);
|
||||
box-shadow:
|
||||
0 12px 28px rgba(0, 0, 0, 0.42),
|
||||
0 0 0 1px rgba(147, 197, 253, 0.08) inset;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .chat-scroll-to-bottom:focus-visible {
|
||||
outline-color: #93c5fd;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .process-details-container {
|
||||
border-top-color: #263244;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .progress-header,
|
||||
html[data-theme="dark"] .progress-footer {
|
||||
border-color: #243044;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .progress-toggle {
|
||||
background: #111827;
|
||||
border-color: #2b374b;
|
||||
color: #a7b0c0;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .progress-toggle:hover {
|
||||
background: #172033;
|
||||
border-color: rgba(96, 165, 250, 0.38);
|
||||
color: #dbeafe;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item {
|
||||
background: #0f172a;
|
||||
border-left-color: #334155;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item:hover {
|
||||
background: #132036;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-content {
|
||||
border-top-color: rgba(148, 163, 184, 0.16);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-iteration,
|
||||
html[data-theme="dark"] .timeline-item.timeline-item-iteration.timeline-eino-scope-main,
|
||||
html[data-theme="dark"] .timeline-item.timeline-item-iteration.timeline-eino-scope-sub {
|
||||
background: #101a2b !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-thinking {
|
||||
background: rgba(139, 92, 246, 0.09);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-thinking[data-response-stream-placeholder="1"] {
|
||||
background: rgba(96, 165, 250, 0.075);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-reasoning_chain {
|
||||
background: rgba(124, 58, 237, 0.11);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-planning {
|
||||
background: rgba(45, 212, 191, 0.075);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call,
|
||||
html[data-theme="dark"] .timeline-item-tool_calls_detected {
|
||||
background: rgba(96, 165, 250, 0.075);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_result,
|
||||
html[data-theme="dark"] .timeline-item-progress {
|
||||
background: rgba(148, 163, 184, 0.075);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-eino_agent_reply {
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call:has(.tool-result-section.success),
|
||||
html[data-theme="dark"] .timeline-item-tool_result:has(.tool-result-section.success) {
|
||||
background: rgba(52, 211, 153, 0.08);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call:has(.tool-result-section.error),
|
||||
html[data-theme="dark"] .timeline-item-tool_result:has(.tool-result-section.error),
|
||||
html[data-theme="dark"] .timeline-item-tool_result.error,
|
||||
html[data-theme="dark"] .timeline-item-error {
|
||||
background: rgba(248, 113, 113, 0.1);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-running {
|
||||
border: 1px solid rgba(96, 165, 250, 0.32);
|
||||
border-left: 3px solid #60a5fa;
|
||||
background: rgba(37, 99, 235, 0.105);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-completed {
|
||||
border: 1px solid rgba(52, 211, 153, 0.34);
|
||||
border-left: 3px solid #34d399;
|
||||
background: rgba(16, 185, 129, 0.105);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-failed {
|
||||
border: 1px solid rgba(248, 113, 113, 0.38);
|
||||
border-left: 3px solid #f87171;
|
||||
background: rgba(239, 68, 68, 0.115);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-running:hover {
|
||||
background: rgba(37, 99, 235, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-completed:hover {
|
||||
background: rgba(16, 185, 129, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-tool_call.tool-call-failed:hover {
|
||||
background: rgba(239, 68, 68, 0.16);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .timeline-item-warning,
|
||||
html[data-theme="dark"] .timeline-item-user_interrupt_continue {
|
||||
background: rgba(251, 191, 36, 0.08);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .message.assistant .message-bubble {
|
||||
background: #111827;
|
||||
color: var(--text-primary);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"view": "View",
|
||||
"actions": "Actions",
|
||||
"loadFailed": "Load failed",
|
||||
"loadMore": "Load more",
|
||||
"untitled": "Untitled",
|
||||
"justNow": "Just now",
|
||||
"minutesAgo": "{{n}} min ago",
|
||||
@@ -555,6 +556,10 @@
|
||||
"noMatchTools": "No matching tools",
|
||||
"penetrationTestDetail": "Penetration test details",
|
||||
"expandDetail": "Expand details",
|
||||
"expandDetailLazyHint": "Expand details (loads iteration details on click)",
|
||||
"viewToolDetail": "View details",
|
||||
"collapseToolDetail": "Collapse",
|
||||
"liveTimelinePruned": "Collapsed the first {{count}} live process details. View the full record page by page after the task completes.",
|
||||
"toolExecutionsCount": "{{n}} tool runs",
|
||||
"collapseToolExecutions": "Collapse tool runs",
|
||||
"noProcessDetail": "No process details (execution may be too fast or no detailed events)",
|
||||
@@ -804,7 +809,9 @@
|
||||
"noResult": "No result",
|
||||
"running": "Running...",
|
||||
"completed": "Completed",
|
||||
"execFailed": "Execution failed"
|
||||
"execFailed": "Execution failed",
|
||||
"toolSucceeded": "Succeeded",
|
||||
"toolFailed": "Failed"
|
||||
},
|
||||
"tasks": {
|
||||
"title": "Task Management",
|
||||
@@ -1813,6 +1820,7 @@
|
||||
"mcpMonitor": {
|
||||
"deselectAll": "Deselect all",
|
||||
"statusPending": "Pending",
|
||||
"statusSuccess": "Succeeded",
|
||||
"statusCompleted": "Completed",
|
||||
"statusRunning": "Running",
|
||||
"statusFailed": "Failed",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"view": "查看",
|
||||
"actions": "操作",
|
||||
"loadFailed": "加载失败",
|
||||
"loadMore": "加载更多",
|
||||
"untitled": "未命名",
|
||||
"justNow": "刚刚",
|
||||
"minutesAgo": "{{n}} 分钟前",
|
||||
@@ -543,6 +544,10 @@
|
||||
"noMatchTools": "没有匹配的工具",
|
||||
"penetrationTestDetail": "渗透测试详情",
|
||||
"expandDetail": "展开详情",
|
||||
"expandDetailLazyHint": "展开详情(点击后加载迭代详情)",
|
||||
"viewToolDetail": "查看详情",
|
||||
"collapseToolDetail": "收起",
|
||||
"liveTimelinePruned": "已收起前 {{count}} 条实时过程详情,任务完成后可按页查看完整记录",
|
||||
"toolExecutionsCount": "{{n}}次工具执行",
|
||||
"collapseToolExecutions": "收起工具执行",
|
||||
"noProcessDetail": "暂无过程详情(可能执行过快或未触发详细事件)",
|
||||
@@ -792,7 +797,9 @@
|
||||
"noResult": "无结果",
|
||||
"running": "执行中...",
|
||||
"completed": "已完成",
|
||||
"execFailed": "执行失败"
|
||||
"execFailed": "执行失败",
|
||||
"toolSucceeded": "成功",
|
||||
"toolFailed": "失败"
|
||||
},
|
||||
"tasks": {
|
||||
"title": "任务管理",
|
||||
@@ -1801,6 +1808,7 @@
|
||||
"mcpMonitor": {
|
||||
"deselectAll": "取消全选",
|
||||
"statusPending": "等待中",
|
||||
"statusSuccess": "成功",
|
||||
"statusCompleted": "已完成",
|
||||
"statusRunning": "执行中",
|
||||
"statusFailed": "失败",
|
||||
|
||||
+65
-6
@@ -2397,7 +2397,10 @@ function isEinoAgentHeartbeatProgress(detail) {
|
||||
|
||||
function filterNoiseProcessDetails(details) {
|
||||
if (!Array.isArray(details)) return details;
|
||||
return details.filter(function (d) { return !isEinoAgentHeartbeatProgress(d); });
|
||||
return details.filter(function (d) {
|
||||
if (isEinoAgentHeartbeatProgress(d)) return false;
|
||||
return !(d && d.eventType === 'tool_calls_detected');
|
||||
});
|
||||
}
|
||||
|
||||
function dedupeConsecutiveProcessDetailRows(details) {
|
||||
@@ -2704,6 +2707,7 @@ function renderProcessDetails(messageId, processDetails, options) {
|
||||
title: itemTitle,
|
||||
message: detail.message || '',
|
||||
data: data,
|
||||
processDetailId: detail.id || '',
|
||||
createdAt: detail.createdAt
|
||||
};
|
||||
if (eventType === 'tool_call' && data._mergedResult) {
|
||||
@@ -3052,7 +3056,62 @@ function ensureMcpCallButtons(messageElement) {
|
||||
window.ensureMcpCallButtons = ensureMcpCallButtons;
|
||||
window.appendMcpCallButtons = appendMcpCallButtons;
|
||||
|
||||
// 批量获取工具名称并更新按钮(消除 N 次单独 API 请求,合并为 1 次)
|
||||
function normalizeToolExecutionSummary(raw) {
|
||||
if (typeof raw === 'string') {
|
||||
return { toolName: raw, status: '' };
|
||||
}
|
||||
if (raw && typeof raw === 'object') {
|
||||
return {
|
||||
toolName: raw.toolName || raw.name || '',
|
||||
status: raw.status || ''
|
||||
};
|
||||
}
|
||||
return { toolName: '', status: '' };
|
||||
}
|
||||
|
||||
function getToolExecutionStatusLabel(status) {
|
||||
const normalized = String(status || '').toLowerCase();
|
||||
if (typeof window.t === 'function') {
|
||||
const keyMap = {
|
||||
completed: 'mcpMonitor.statusSuccess',
|
||||
failed: 'mcpMonitor.statusFailed',
|
||||
running: 'mcpMonitor.statusRunning',
|
||||
cancelled: 'mcpMonitor.statusCancelled',
|
||||
pending: 'mcpMonitor.statusPending'
|
||||
};
|
||||
const key = keyMap[normalized];
|
||||
if (key) {
|
||||
const translated = window.t(key);
|
||||
if (translated && translated !== key) return translated;
|
||||
}
|
||||
}
|
||||
const fallback = {
|
||||
completed: '成功',
|
||||
failed: '失败',
|
||||
running: '运行中',
|
||||
cancelled: '已取消',
|
||||
pending: '等待中'
|
||||
};
|
||||
return fallback[normalized] || '';
|
||||
}
|
||||
|
||||
function renderToolExecutionButtonContent(btn, displayToolName, index, status) {
|
||||
const safeToolName = escapeHtml(displayToolName || (typeof window.t === 'function' ? window.t('chat.unknownTool') : '未知工具'));
|
||||
const safeIndex = escapeHtml(index || '');
|
||||
const statusText = getToolExecutionStatusLabel(status);
|
||||
const normalizedStatus = String(status || '').toLowerCase();
|
||||
const label = safeIndex ? `${safeToolName} #${safeIndex}` : safeToolName;
|
||||
btn.innerHTML = '<span class="mcp-tool-name">' + label + '</span>';
|
||||
if (!statusText) {
|
||||
btn.removeAttribute('data-status');
|
||||
btn.removeAttribute('title');
|
||||
return;
|
||||
}
|
||||
btn.dataset.status = normalizedStatus;
|
||||
btn.title = statusText;
|
||||
}
|
||||
|
||||
// 批量获取工具摘要并更新按钮(消除 N 次单独 API 请求,合并为 1 次)
|
||||
async function batchUpdateButtonToolNames(buttonsContainer, executionIds) {
|
||||
if (!executionIds || executionIds.length === 0) return;
|
||||
try {
|
||||
@@ -3062,17 +3121,17 @@ async function batchUpdateButtonToolNames(buttonsContainer, executionIds) {
|
||||
body: JSON.stringify({ ids: executionIds }),
|
||||
});
|
||||
if (!response.ok) return;
|
||||
const nameMap = await response.json(); // { execId: toolName }
|
||||
const nameMap = await response.json(); // { execId: toolName } 或 { execId: { toolName, status } }
|
||||
// 更新对应按钮的文本
|
||||
const buttons = buttonsContainer.querySelectorAll('.mcp-detail-btn[data-exec-id]');
|
||||
buttons.forEach(btn => {
|
||||
const execId = btn.dataset.execId;
|
||||
const index = btn.dataset.execIndex;
|
||||
const toolName = nameMap[execId];
|
||||
const summary = normalizeToolExecutionSummary(nameMap[execId]);
|
||||
const toolName = summary.toolName;
|
||||
if (toolName) {
|
||||
const displayToolName = toolName.includes('::') ? toolName.split('::')[1] : toolName;
|
||||
const span = btn.querySelector('span');
|
||||
if (span) span.textContent = `${displayToolName} #${index}`;
|
||||
renderToolExecutionButtonContent(btn, displayToolName, index, summary.status);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
+355
-171
@@ -236,11 +236,7 @@ function finalizeMainResponseStreamItem(streamState, finalMessage, responseData)
|
||||
const body = typeof formatTimelineStreamBody === 'function'
|
||||
? formatTimelineStreamBody(fullText, meta)
|
||||
: fullText;
|
||||
if (typeof formatMarkdown === 'function') {
|
||||
setTimelineItemContentStreamRich(contentEl, formatMarkdown(body, timelineMarkdownOpts));
|
||||
} else {
|
||||
setTimelineItemContentStreamPlain(contentEl, body);
|
||||
}
|
||||
setTimelineItemContentStreamPlain(contentEl, body);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -439,6 +435,9 @@ function toolResultStreamKey(progressId, toolCallId) {
|
||||
return String(progressId) + '::' + String(toolCallId);
|
||||
}
|
||||
|
||||
const LIVE_TIMELINE_MAX_ITEMS = 150;
|
||||
const LIVE_TIMELINE_PRUNE_CHUNK = 50;
|
||||
|
||||
/** Eino 多代理:时间线标题前加 [agentId],标明哪一代理产生该工具调用/结果/回复 */
|
||||
function timelineAgentBracketPrefix(data) {
|
||||
if (!data || data.einoAgent == null) return '';
|
||||
@@ -461,9 +460,6 @@ function applyEinoTimelineRole(item, data) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 过程详情时间线:更严消毒(无 img;整页 HTML 见 sanitize-markdown.js) */
|
||||
const timelineMarkdownOpts = { profile: 'timeline' };
|
||||
|
||||
function escapeHtmlLocal(text) {
|
||||
if (!text) return '';
|
||||
const div = document.createElement('div');
|
||||
@@ -471,6 +467,10 @@ function escapeHtmlLocal(text) {
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatTimelinePlainTextHtml(text) {
|
||||
return '<pre class="timeline-plain-text">' + escapeHtml(text == null ? '' : String(text)) + '</pre>';
|
||||
}
|
||||
|
||||
/** fenced 块占位(BMP 私用区,正文几乎不会出现) */
|
||||
const _MD_FENCE_PRE = '\n\uE000CSAI_FENCE_';
|
||||
const _MD_FENCE_SUF = '_\uE000\n';
|
||||
@@ -1280,23 +1280,14 @@ function integrateProgressToMCPSection(progressId, assistantMessageId, mcpExecut
|
||||
const progressElement = document.getElementById(progressId);
|
||||
if (!progressElement) return;
|
||||
|
||||
// 快照 innerHTML 前刷掉尚未执行的 rAF 流式更新,避免过程详情少最后几帧
|
||||
// 只 flush 终态标题/正文;完成后的详情回看统一走后端分页,不再复制实时 DOM 快照。
|
||||
flushAllPendingStreamPlainUpdates();
|
||||
|
||||
// Ensure any "running" tool_call badges are closed before we snapshot timeline HTML.
|
||||
// Otherwise, once the progress element is removed, later 'done' events may not be able
|
||||
// to update the original timeline DOM and the copied HTML would stay "执行中".
|
||||
// 进度 DOM 即将移除;先关闭仍处于 running 的工具摘要状态。
|
||||
finalizeOutstandingToolCallsForProgress(progressId, 'failed');
|
||||
|
||||
const mcpIds = Array.isArray(mcpExecutionIds) ? mcpExecutionIds : [];
|
||||
|
||||
// 获取时间线内容
|
||||
const timeline = document.getElementById(progressId + '-timeline');
|
||||
let timelineHTML = '';
|
||||
if (timeline) {
|
||||
timelineHTML = timeline.innerHTML;
|
||||
}
|
||||
|
||||
// 获取助手消息元素
|
||||
const assistantElement = document.getElementById(assistantMessageId);
|
||||
if (!assistantElement) {
|
||||
@@ -1320,8 +1311,6 @@ function integrateProgressToMCPSection(progressId, assistantMessageId, mcpExecut
|
||||
return;
|
||||
}
|
||||
|
||||
const hasContent = timelineHTML.trim().length > 0;
|
||||
|
||||
if (mcpIds.length > 0 && typeof window.appendMcpCallButtons === 'function') {
|
||||
window.appendMcpCallButtons(assistantElement, mcpIds);
|
||||
const toolList = mcpSection.querySelector('.mcp-tool-list');
|
||||
@@ -1354,40 +1343,87 @@ function integrateProgressToMCPSection(progressId, assistantMessageId, mcpExecut
|
||||
mcpSection.appendChild(detailsContainer);
|
||||
}
|
||||
}
|
||||
|
||||
detailsContainer.innerHTML = `
|
||||
<div class="process-details-content">
|
||||
${hasContent ? `<div class="progress-timeline" id="${detailsId}-timeline">${timelineHTML}</div>` : '<div class="progress-timeline-empty">' + (typeof window.t === 'function' ? window.t('chat.noProcessDetail') : '暂无过程详情(可能执行过快或未触发详细事件)') + '</div>'}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (hasContent) {
|
||||
const timeline = document.getElementById(detailsId + '-timeline');
|
||||
if (timeline) {
|
||||
timeline.classList.remove('expanded');
|
||||
}
|
||||
|
||||
|
||||
if (typeof renderProcessDetails === 'function') {
|
||||
renderProcessDetails(assistantMessageId, null);
|
||||
} else {
|
||||
const expandLabel = typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情';
|
||||
document.querySelectorAll(`#${assistantMessageId} .process-detail-btn`).forEach((btn) => {
|
||||
btn.innerHTML = '<span>' + expandLabel + '</span>';
|
||||
});
|
||||
detailsContainer.dataset.lazyNotLoaded = '1';
|
||||
detailsContainer.dataset.loaded = '0';
|
||||
detailsContainer.innerHTML = `
|
||||
<div class="process-details-content">
|
||||
<div class="progress-timeline" id="${detailsId}-timeline">
|
||||
<div class="progress-timeline-empty">${typeof window.t === 'function' ? window.t('chat.expandDetailLazyHint') : (expandLabel + '(点击后加载迭代详情)')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const expandLabel = typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情';
|
||||
document.querySelectorAll(`#${assistantMessageId} .process-detail-btn`).forEach((btn) => {
|
||||
btn.innerHTML = '<span>' + expandLabel + '</span>';
|
||||
});
|
||||
|
||||
removeMessage(progressId);
|
||||
}
|
||||
|
||||
const PROCESS_DETAILS_PAGE_SIZE = 100;
|
||||
const PROCESS_DETAILS_PAGE_SIZE = 50;
|
||||
|
||||
function getProcessDetailsLoadMoreLabel(hasMore) {
|
||||
if (!hasMore) return '';
|
||||
return (typeof window.t === 'function' ? window.t('common.loadMore') : '加载更多') + ' · ' +
|
||||
(typeof window.t === 'function' ? window.t('chat.penetrationTestDetail') : '过程详情');
|
||||
}
|
||||
|
||||
function updateProcessDetailsLoadMoreButton(assistantMessageId, backendMessageId, hasMore) {
|
||||
const detailsContainer = document.getElementById('process-details-' + assistantMessageId);
|
||||
if (!detailsContainer) return;
|
||||
|
||||
let btn = detailsContainer.querySelector('.process-details-load-more-btn');
|
||||
if (!hasMore) {
|
||||
if (btn) btn.remove();
|
||||
return;
|
||||
}
|
||||
if (!btn) {
|
||||
btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'mcp-detail-btn process-details-load-more-btn';
|
||||
detailsContainer.appendChild(btn);
|
||||
}
|
||||
btn.textContent = getProcessDetailsLoadMoreLabel(true);
|
||||
btn.disabled = false;
|
||||
btn.onclick = async () => {
|
||||
if (detailsContainer.dataset.loadingMore === '1') return;
|
||||
detailsContainer.dataset.loadingMore = '1';
|
||||
btn.disabled = true;
|
||||
btn.textContent = typeof window.t === 'function' ? window.t('common.loading') : '加载中…';
|
||||
try {
|
||||
await loadProcessDetailsPaginated(assistantMessageId, backendMessageId, {
|
||||
append: true,
|
||||
autoLoadAll: false
|
||||
});
|
||||
} finally {
|
||||
detailsContainer.dataset.loadingMore = '0';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页加载过程详情并增量渲染,避免数百轮迭代一次性阻塞主线程。
|
||||
* 分页加载过程详情并增量渲染。默认全量加载供恢复流程使用;
|
||||
* 用户手动展开时传 autoLoadAll=false,只加载一页并展示“加载更多”。
|
||||
*/
|
||||
async function loadProcessDetailsPaginated(assistantMessageId, backendMessageId) {
|
||||
async function loadProcessDetailsPaginated(assistantMessageId, backendMessageId, options) {
|
||||
if (!assistantMessageId || !backendMessageId || typeof apiFetch !== 'function' || typeof renderProcessDetails !== 'function') {
|
||||
return;
|
||||
}
|
||||
const opts = options || {};
|
||||
const autoLoadAll = opts.autoLoadAll !== false;
|
||||
const detailsContainer = document.getElementById('process-details-' + assistantMessageId);
|
||||
const PAGE = PROCESS_DETAILS_PAGE_SIZE;
|
||||
let offset = 0;
|
||||
let isFirst = true;
|
||||
let offset = opts.append && detailsContainer && detailsContainer.dataset.nextOffset
|
||||
? parseInt(detailsContainer.dataset.nextOffset, 10) || 0
|
||||
: 0;
|
||||
let isFirst = !opts.append;
|
||||
while (true) {
|
||||
const res = await apiFetch(
|
||||
'/api/messages/' + encodeURIComponent(String(backendMessageId)) +
|
||||
@@ -1401,12 +1437,18 @@ async function loadProcessDetailsPaginated(assistantMessageId, backendMessageId)
|
||||
const hasMore = !!(j && j.hasMore);
|
||||
renderProcessDetails(assistantMessageId, details, {
|
||||
append: !isFirst,
|
||||
markLoaded: !hasMore
|
||||
markLoaded: autoLoadAll ? !hasMore : true
|
||||
});
|
||||
if (!hasMore || details.length === 0) {
|
||||
offset += details.length;
|
||||
if (detailsContainer) {
|
||||
detailsContainer.dataset.lazyNotLoaded = '0';
|
||||
detailsContainer.dataset.loaded = hasMore ? 'partial' : '1';
|
||||
detailsContainer.dataset.nextOffset = String(offset);
|
||||
}
|
||||
updateProcessDetailsLoadMoreButton(assistantMessageId, backendMessageId, hasMore && !autoLoadAll);
|
||||
if (!hasMore || details.length === 0 || !autoLoadAll) {
|
||||
break;
|
||||
}
|
||||
offset += details.length;
|
||||
isFirst = false;
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||
}
|
||||
@@ -1437,7 +1479,7 @@ function toggleProcessDetails(progressId, assistantMessageId) {
|
||||
if (timeline) {
|
||||
timeline.innerHTML = '<div class="progress-timeline-empty">' + ((typeof window.t === 'function') ? window.t('common.loading') : '加载中…') + '</div>';
|
||||
}
|
||||
loadProcessDetailsPaginated(assistantMessageId, backendMessageId)
|
||||
loadProcessDetailsPaginated(assistantMessageId, backendMessageId, { autoLoadAll: false })
|
||||
.catch((e) => {
|
||||
console.error('加载过程详情失败:', e);
|
||||
const tl = detailsContainer.querySelector('.progress-timeline');
|
||||
@@ -1527,82 +1569,6 @@ async function cancelProgressTask(progressId) {
|
||||
openUserInterruptModal(progressId, state.conversationId);
|
||||
}
|
||||
|
||||
// 将进度消息转换为可折叠的详情组件
|
||||
function convertProgressToDetails(progressId, assistantMessageId) {
|
||||
const progressElement = document.getElementById(progressId);
|
||||
if (!progressElement) return;
|
||||
|
||||
// 获取时间线内容
|
||||
const timeline = document.getElementById(progressId + '-timeline');
|
||||
// 即使时间线不存在,也创建详情组件(显示空状态)
|
||||
let timelineHTML = '';
|
||||
if (timeline) {
|
||||
timelineHTML = timeline.innerHTML;
|
||||
}
|
||||
|
||||
// 获取助手消息元素
|
||||
const assistantElement = document.getElementById(assistantMessageId);
|
||||
if (!assistantElement) {
|
||||
removeMessage(progressId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建详情组件
|
||||
const detailsId = 'details-' + Date.now() + '-' + messageCounter++;
|
||||
const detailsDiv = document.createElement('div');
|
||||
detailsDiv.id = detailsId;
|
||||
detailsDiv.className = 'message system progress-details';
|
||||
|
||||
const contentWrapper = document.createElement('div');
|
||||
contentWrapper.className = 'message-content';
|
||||
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = 'message-bubble progress-container completed';
|
||||
|
||||
// 获取时间线HTML内容
|
||||
const hasContent = timelineHTML.trim().length > 0;
|
||||
|
||||
// 检查时间线中是否有错误项
|
||||
const hasError = timeline && timeline.querySelector('.timeline-item-error');
|
||||
|
||||
// 如果有错误,默认折叠;否则默认展开
|
||||
const shouldExpand = !hasError;
|
||||
const expandedClass = shouldExpand ? 'expanded' : '';
|
||||
const collapseDetailText = typeof window.t === 'function' ? window.t('tasks.collapseDetail') : '收起详情';
|
||||
const expandDetailText = typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情';
|
||||
const toggleText = shouldExpand ? collapseDetailText : expandDetailText;
|
||||
const penetrationDetailText = typeof window.t === 'function' ? window.t('chat.penetrationTestDetail') : '渗透测试详情';
|
||||
const noProcessDetailText = typeof window.t === 'function' ? window.t('chat.noProcessDetail') : '暂无过程详情(可能执行过快或未触发详细事件)';
|
||||
bubble.innerHTML = `
|
||||
<div class="progress-header">
|
||||
<span class="progress-title">📋 ${penetrationDetailText}</span>
|
||||
${hasContent ? `<button class="progress-toggle" onclick="toggleProgressDetails('${detailsId}')">${toggleText}</button>` : ''}
|
||||
</div>
|
||||
${hasContent ? `<div class="progress-timeline ${expandedClass}" id="${detailsId}-timeline">${timelineHTML}</div><div class="progress-footer"><button type="button" class="progress-toggle progress-toggle-bottom" onclick="toggleProgressDetails('${detailsId}')">${toggleText}</button></div>` : '<div class="progress-timeline-empty">' + noProcessDetailText + '</div>'}
|
||||
`;
|
||||
|
||||
contentWrapper.appendChild(bubble);
|
||||
detailsDiv.appendChild(contentWrapper);
|
||||
|
||||
// 将详情组件插入到助手消息之后
|
||||
const messagesDiv = document.getElementById('chat-messages');
|
||||
const insertWasPinned = typeof window.captureScrollPinState === 'function'
|
||||
? window.captureScrollPinState()
|
||||
: (typeof window.isChatMessagesPinnedToBottom === 'function' ? window.isChatMessagesPinnedToBottom() : true);
|
||||
// assistantElement 是消息div,需要插入到它的下一个兄弟节点之前
|
||||
if (assistantElement.nextSibling) {
|
||||
messagesDiv.insertBefore(detailsDiv, assistantElement.nextSibling);
|
||||
} else {
|
||||
// 如果没有下一个兄弟节点,直接追加
|
||||
messagesDiv.appendChild(detailsDiv);
|
||||
}
|
||||
|
||||
// 移除原来的进度消息
|
||||
removeMessage(progressId);
|
||||
|
||||
scrollChatMessagesToBottomIfPinned(insertWasPinned);
|
||||
}
|
||||
|
||||
/** 将后端消息 UUID 绑定到助手气泡,供删除本轮 / 过程详情懒加载(domId 为前端 msg-*) */
|
||||
function applyBackendMessageIdToAssistantDom(domAssistantId, backendMessageId) {
|
||||
if (!domAssistantId || !backendMessageId) return;
|
||||
@@ -2065,11 +2031,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
const contentEl = item.querySelector('.timeline-item-content');
|
||||
if (contentEl) {
|
||||
flushStreamPlainTextUpdate(contentEl);
|
||||
if (typeof formatMarkdown === 'function') {
|
||||
setTimelineItemContentStreamRich(contentEl, formatMarkdown(s.buffer, timelineMarkdownOpts));
|
||||
} else {
|
||||
setTimelineItemContentStreamPlain(contentEl, s.buffer);
|
||||
}
|
||||
setTimelineItemContentStreamPlain(contentEl, s.buffer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2091,11 +2053,6 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
case 'tool_calls_detected':
|
||||
// 助手正文段结束、进入工具调用:下一段 response_start 应新建时间线条目
|
||||
responseStreamStateByProgressId.delete(progressId);
|
||||
addTimelineItem(timeline, 'tool_calls_detected', {
|
||||
title: timelineAgentBracketPrefix(event.data) + '🔧 ' + (typeof window.t === 'function' ? window.t('chat.toolCallsDetected', { count: event.data?.count || 0 }) : '检测到 ' + (event.data?.count || 0) + ' 个工具调用'),
|
||||
message: event.message,
|
||||
data: event.data
|
||||
});
|
||||
break;
|
||||
|
||||
case 'warning':
|
||||
@@ -2240,6 +2197,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
title: timelineAgentBracketPrefix(toolInfo) + '🔧 ' + toolCallTitle,
|
||||
message: event.message,
|
||||
data: toolInfo,
|
||||
processDetailId: toolInfo.processDetailId || '',
|
||||
expanded: false
|
||||
});
|
||||
|
||||
@@ -2304,6 +2262,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
title: timelineAgentBracketPrefix(resultInfo) + statusIcon + ' ' + resultExecText,
|
||||
message: event.message,
|
||||
data: resultInfo,
|
||||
processDetailId: resultInfo.processDetailId || '',
|
||||
expanded: false
|
||||
});
|
||||
break;
|
||||
@@ -2391,11 +2350,7 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
item.appendChild(contentEl);
|
||||
}
|
||||
flushStreamPlainTextUpdate(contentEl);
|
||||
if (typeof formatMarkdown === 'function') {
|
||||
setTimelineItemContentStreamRich(contentEl, formatMarkdown(full, timelineMarkdownOpts));
|
||||
} else {
|
||||
setTimelineItemContentStreamPlain(contentEl, full);
|
||||
}
|
||||
setTimelineItemContentStreamPlain(contentEl, full);
|
||||
if (d.einoAgent != null && String(d.einoAgent).trim() !== '') {
|
||||
item.dataset.einoAgent = String(d.einoAgent).trim();
|
||||
}
|
||||
@@ -2640,17 +2595,20 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
// so the copied timeline HTML reflects the final status.
|
||||
finalizeOutstandingToolCallsForProgress(progressId, 'failed');
|
||||
|
||||
const respMid = responseData.messageId;
|
||||
if (respMid) {
|
||||
applyBackendMessageIdToAssistantDom(assistantIdFinal, respMid);
|
||||
}
|
||||
|
||||
const replayCtx = window.csTaskReplay;
|
||||
const directReplay = replayCtx && replayCtx.progressId === progressId;
|
||||
if (!directReplay) {
|
||||
// 将进度详情集成到工具调用区域(放在最终 response 之后,保证时间线已完整)
|
||||
// 将详情入口集成到工具调用区域;完整过程从后端分页加载,不依赖实时 DOM 快照。
|
||||
integrateProgressToMCPSection(progressId, assistantIdFinal, mcpIds);
|
||||
}
|
||||
responseStreamStateByProgressId.delete(progressId);
|
||||
|
||||
const respMid = responseData.messageId;
|
||||
if (respMid) {
|
||||
applyBackendMessageIdToAssistantDom(assistantIdFinal, respMid);
|
||||
if (typeof window.syncAssistantReasoningContentFromServer === 'function') {
|
||||
setTimeout(function () {
|
||||
window.syncAssistantReasoningContentFromServer(respMid, assistantIdFinal);
|
||||
@@ -3466,8 +3424,154 @@ function buildToolResultSectionHtml(data, opts) {
|
||||
);
|
||||
}
|
||||
|
||||
const toolCallDetailStateByItemId = new Map();
|
||||
|
||||
function setToolCallDetailState(item, state) {
|
||||
if (!item || !item.id) return;
|
||||
toolCallDetailStateByItemId.set(item.id, Object.assign({}, state || {}));
|
||||
item.classList.add('tool-detail-collapsible');
|
||||
item.classList.add('tool-call-collapsible');
|
||||
updateToolDetailToggleLabel(item);
|
||||
}
|
||||
|
||||
function toolDetailToggleText(expanded) {
|
||||
if (typeof window.t === 'function') {
|
||||
return expanded
|
||||
? window.t('chat.collapseToolDetail')
|
||||
: window.t('chat.viewToolDetail');
|
||||
}
|
||||
return expanded ? '收起' : '查看详情';
|
||||
}
|
||||
|
||||
function updateToolDetailToggleLabel(item) {
|
||||
if (!item) return;
|
||||
const header = item.querySelector('.timeline-item-header');
|
||||
if (!header) return;
|
||||
const expanded = item.classList.contains('tool-call-detail-expanded');
|
||||
header.setAttribute('data-tool-detail-label', toolDetailToggleText(expanded) + (expanded ? ' ▴' : ' ▾'));
|
||||
}
|
||||
|
||||
async function fetchFullProcessDetailData(detailId) {
|
||||
const id = detailId != null ? String(detailId).trim() : '';
|
||||
if (!id || typeof apiFetch !== 'function') return null;
|
||||
const res = await apiFetch('/api/process-details/' + encodeURIComponent(id));
|
||||
const j = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
throw new Error((j && j.error) ? j.error : String(res.status));
|
||||
}
|
||||
const detail = j && j.processDetail ? j.processDetail : null;
|
||||
return detail && detail.data ? detail.data : null;
|
||||
}
|
||||
|
||||
function collapseOtherToolCallDetails(item) {
|
||||
const timeline = item && item.closest ? item.closest('.progress-timeline') : null;
|
||||
if (!timeline) return;
|
||||
timeline.querySelectorAll('.timeline-item.tool-detail-collapsible.tool-call-detail-expanded').forEach(function (other) {
|
||||
if (other === item) return;
|
||||
other.classList.remove('tool-call-detail-expanded');
|
||||
const content = other.querySelector('.timeline-item-content.tool-call-detail-content');
|
||||
if (content) content.remove();
|
||||
updateToolDetailToggleLabel(other);
|
||||
});
|
||||
}
|
||||
|
||||
async function renderToolCallDetailContent(item) {
|
||||
if (!item || !item.id) return;
|
||||
const state = toolCallDetailStateByItemId.get(item.id) || {};
|
||||
collapseOtherToolCallDetails(item);
|
||||
|
||||
let content = item.querySelector('.timeline-item-content.tool-call-detail-content');
|
||||
if (content) {
|
||||
content.remove();
|
||||
item.classList.remove('tool-call-detail-expanded');
|
||||
updateToolDetailToggleLabel(item);
|
||||
return;
|
||||
}
|
||||
|
||||
content = document.createElement('div');
|
||||
content.className = 'timeline-item-content tool-call-detail-content';
|
||||
if (state.payloadDeferred && !state.payloadLoaded && (state.processDetailId || state.resultDetailId)) {
|
||||
content.innerHTML = '<div class="progress-timeline-empty">' +
|
||||
escapeHtml(typeof window.t === 'function' ? window.t('common.loading') : '加载中…') +
|
||||
'</div>';
|
||||
item.appendChild(content);
|
||||
item.classList.add('tool-call-detail-expanded');
|
||||
updateToolDetailToggleLabel(item);
|
||||
try {
|
||||
if (state.processDetailId && !state.hideArgs) {
|
||||
const fullCall = await fetchFullProcessDetailData(state.processDetailId);
|
||||
if (fullCall) {
|
||||
state.args = parseToolCallArgsFromData(fullCall);
|
||||
state.payloadDeferred = false;
|
||||
}
|
||||
}
|
||||
if (state.resultDetailId) {
|
||||
const fullResult = await fetchFullProcessDetailData(state.resultDetailId);
|
||||
if (fullResult) {
|
||||
state.resultData = fullResult;
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = fullResult.result != null ? fullResult.result : (fullResult.error != null ? fullResult.error : noResultText);
|
||||
state.rawText = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
}
|
||||
}
|
||||
state.payloadLoaded = true;
|
||||
setToolCallDetailState(item, state);
|
||||
content.remove();
|
||||
item.classList.remove('tool-call-detail-expanded');
|
||||
renderToolCallDetailContent(item);
|
||||
} catch (e) {
|
||||
content.innerHTML = '<div class="progress-timeline-empty">' + escapeHtml(e && e.message ? e.message : '加载失败') + '</div>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const args = state.args != null ? state.args : {};
|
||||
let resultBlock = '';
|
||||
if (state.resultData) {
|
||||
resultBlock = '<div class="tool-details tool-result-slot">' +
|
||||
buildToolResultSectionHtml(state.resultData, { rawText: state.rawText }) +
|
||||
'</div>';
|
||||
} else if (state.pending !== false) {
|
||||
resultBlock = '<div class="tool-details tool-result-slot">' +
|
||||
buildToolResultSectionHtml({}, { pending: true }) +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
const paramsLabel = typeof window.t === 'function' ? window.t('timeline.params') : '参数:';
|
||||
const argsBlock = state.hideArgs ? '' :
|
||||
'<div class="tool-arg-section">' +
|
||||
'<strong data-i18n="timeline.params">' + escapeHtml(paramsLabel) + '</strong>' +
|
||||
'<pre class="tool-args">' + escapeHtml(JSON.stringify(args, null, 2)) + '</pre>' +
|
||||
'</div>';
|
||||
content.innerHTML = '<div class="tool-details">' + argsBlock + resultBlock + '</div>';
|
||||
item.appendChild(content);
|
||||
item.classList.add('tool-call-detail-expanded');
|
||||
updateToolDetailToggleLabel(item);
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined' && !document.__cyberStrikeToolCallDetailToggleBound) {
|
||||
document.__cyberStrikeToolCallDetailToggleBound = true;
|
||||
document.addEventListener('click', function (event) {
|
||||
const target = event.target;
|
||||
if (target && target.closest && target.closest('button, a, input, textarea, select, pre, code, .timeline-item-content')) {
|
||||
return;
|
||||
}
|
||||
const item = target && target.closest
|
||||
? target.closest('.timeline-item.tool-detail-collapsible')
|
||||
: null;
|
||||
if (!item) return;
|
||||
renderToolCallDetailContent(item);
|
||||
});
|
||||
}
|
||||
|
||||
function ensureToolCallResultSlot(item) {
|
||||
if (!item) return null;
|
||||
if (item.classList.contains('tool-call-collapsible')) {
|
||||
const state = toolCallDetailStateByItemId.get(item.id) || {};
|
||||
state.pending = true;
|
||||
setToolCallDetailState(item, state);
|
||||
if (!item.classList.contains('tool-call-detail-expanded')) return null;
|
||||
}
|
||||
let section = item.querySelector('.tool-result-section');
|
||||
if (section) return section;
|
||||
const content = item.querySelector('.timeline-item-content');
|
||||
@@ -3488,6 +3592,27 @@ function mergeToolResultIntoCallItem(item, data, options) {
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const text = options.rawText != null ? String(options.rawText) : resultStr;
|
||||
|
||||
if (item.classList.contains('tool-call-collapsible')) {
|
||||
const state = toolCallDetailStateByItemId.get(item.id) || {};
|
||||
state.resultData = data;
|
||||
state.rawText = text;
|
||||
state.resultDetailId = data.processDetailId || state.resultDetailId || '';
|
||||
state.pending = false;
|
||||
setToolCallDetailState(item, state);
|
||||
const expanded = item.classList.contains('tool-call-detail-expanded');
|
||||
const content = item.querySelector('.timeline-item-content.tool-call-detail-content');
|
||||
if (content) content.remove();
|
||||
if (expanded) {
|
||||
item.classList.remove('tool-call-detail-expanded');
|
||||
renderToolCallDetailContent(item);
|
||||
}
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = data.success !== false ? '1' : '0';
|
||||
item.classList.remove('tool-call-running');
|
||||
item.classList.add(data.success !== false ? 'tool-call-completed' : 'tool-call-failed');
|
||||
return true;
|
||||
}
|
||||
|
||||
let section = item.querySelector('.tool-result-section');
|
||||
if (!section) {
|
||||
ensureToolCallResultSlot(item);
|
||||
@@ -3568,6 +3693,9 @@ function coalesceProcessDetailsToolPairs(details) {
|
||||
const rd = resultDetail.data || {};
|
||||
targetDetail.data = targetDetail.data || {};
|
||||
targetDetail.data._mergedResult = Object.assign({}, rd);
|
||||
if (resultDetail.id) {
|
||||
targetDetail.data._mergedResultDetailId = resultDetail.id;
|
||||
}
|
||||
if (resultDetail.createdAt) {
|
||||
targetDetail.data._mergedResultAt = resultDetail.createdAt;
|
||||
}
|
||||
@@ -3581,6 +3709,7 @@ function coalesceProcessDetailsToolPairs(details) {
|
||||
|
||||
if (et === 'tool_call') {
|
||||
const copy = {
|
||||
id: detail.id,
|
||||
eventType: detail.eventType,
|
||||
message: detail.message,
|
||||
createdAt: detail.createdAt,
|
||||
@@ -3692,6 +3821,44 @@ function buildWorkflowBranchDetailHtml(data) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function isLiveProgressTimeline(timeline) {
|
||||
return !!(timeline && timeline.id && /^progress-\d+-\d+-timeline$/.test(timeline.id));
|
||||
}
|
||||
|
||||
function pruneLiveTimelineIfNeeded(timeline) {
|
||||
if (!isLiveProgressTimeline(timeline)) return;
|
||||
const items = Array.from(timeline.children).filter(function (el) {
|
||||
return el && el.classList && el.classList.contains('timeline-item');
|
||||
});
|
||||
if (items.length <= LIVE_TIMELINE_MAX_ITEMS) return;
|
||||
|
||||
let marker = timeline.querySelector('.timeline-live-pruned-marker');
|
||||
let pruned = marker ? (parseInt(marker.dataset.prunedCount || '0', 10) || 0) : 0;
|
||||
let removable = items.filter(function (el) {
|
||||
return !el.dataset.hitlInterruptId &&
|
||||
!el.dataset.workflowRunId &&
|
||||
!el.classList.contains('timeline-item-workflow_hitl_waiting') &&
|
||||
!el.classList.contains('timeline-item-hitl_interrupt');
|
||||
});
|
||||
const overflow = items.length - LIVE_TIMELINE_MAX_ITEMS;
|
||||
const removeCount = Math.min(removable.length, Math.max(overflow, LIVE_TIMELINE_PRUNE_CHUNK));
|
||||
if (removeCount <= 0) return;
|
||||
|
||||
if (!marker) {
|
||||
marker = document.createElement('div');
|
||||
marker.className = 'timeline-live-pruned-marker';
|
||||
timeline.insertBefore(marker, timeline.firstChild);
|
||||
}
|
||||
for (let i = 0; i < removeCount; i++) {
|
||||
removable[i].remove();
|
||||
}
|
||||
pruned += removeCount;
|
||||
marker.dataset.prunedCount = String(pruned);
|
||||
marker.textContent = typeof window.t === 'function'
|
||||
? window.t('chat.liveTimelinePruned', { count: pruned })
|
||||
: ('已收起前 ' + pruned + ' 条实时过程详情,任务完成后可按页查看完整记录');
|
||||
}
|
||||
|
||||
function addTimelineItem(timeline, type, options) {
|
||||
const item = document.createElement('div');
|
||||
// 生成唯一ID
|
||||
@@ -3715,6 +3882,9 @@ function addTimelineItem(timeline, type, options) {
|
||||
}
|
||||
if (type === 'tool_call' && options.data) {
|
||||
const d = options.data;
|
||||
if (options.processDetailId) {
|
||||
item.dataset.processDetailId = String(options.processDetailId);
|
||||
}
|
||||
item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : '';
|
||||
item.dataset.toolIndex = (d.index != null) ? String(d.index) : '0';
|
||||
item.dataset.toolTotal = (d.total != null) ? String(d.total) : '0';
|
||||
@@ -3725,6 +3895,10 @@ function addTimelineItem(timeline, type, options) {
|
||||
if (merged) {
|
||||
item.dataset.toolResultMerged = '1';
|
||||
item.dataset.toolSuccess = merged.success !== false ? '1' : '0';
|
||||
item.classList.add(merged.success !== false ? 'tool-call-completed' : 'tool-call-failed');
|
||||
if (d._mergedResultDetailId) {
|
||||
item.dataset.toolResultDetailId = String(d._mergedResultDetailId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type === 'hitl_interrupt' && options.data && options.data.interruptId != null && String(options.data.interruptId).trim() !== '') {
|
||||
@@ -3738,6 +3912,9 @@ function addTimelineItem(timeline, type, options) {
|
||||
}
|
||||
if (type === 'tool_result' && options.data) {
|
||||
const d = options.data;
|
||||
if (options.processDetailId) {
|
||||
item.dataset.processDetailId = String(options.processDetailId);
|
||||
}
|
||||
item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : '';
|
||||
item.dataset.toolSuccess = d.success !== false ? '1' : '0';
|
||||
}
|
||||
@@ -3790,34 +3967,29 @@ function addTimelineItem(timeline, type, options) {
|
||||
const streamBody = typeof formatTimelineStreamBody === 'function'
|
||||
? formatTimelineStreamBody(options.message, options.data)
|
||||
: options.message;
|
||||
content += `<div class="timeline-item-content">${formatMarkdown(streamBody, timelineMarkdownOpts)}</div>`;
|
||||
content += `<div class="timeline-item-content timeline-stream-plain">${formatTimelinePlainTextHtml(streamBody)}</div>`;
|
||||
} else if (type === 'tool_call' && options.data) {
|
||||
const data = options.data;
|
||||
const args = parseToolCallArgsFromData(data);
|
||||
const merged = options.mergedResult || data._mergedResult;
|
||||
const paramsLabel = typeof window.t === 'function' ? window.t('timeline.params') : '参数:';
|
||||
let resultBlock = '';
|
||||
if (merged) {
|
||||
resultBlock = '<div class="tool-details tool-result-slot">' + buildToolResultSectionHtml(merged) + '</div>';
|
||||
if (merged.success !== false) {
|
||||
item.classList.add('tool-call-completed');
|
||||
} else {
|
||||
item.classList.add('tool-call-failed');
|
||||
}
|
||||
} else if (!options.skipPendingResult) {
|
||||
resultBlock = '<div class="tool-details tool-result-slot">' + buildToolResultSectionHtml({}, { pending: true }) + '</div>';
|
||||
item.classList.add('tool-call-running');
|
||||
}
|
||||
content += `
|
||||
<div class="timeline-item-content">
|
||||
<div class="tool-details">
|
||||
<div class="tool-arg-section">
|
||||
<strong data-i18n="timeline.params">${escapeHtml(paramsLabel)}</strong>
|
||||
<pre class="tool-args">${escapeHtml(JSON.stringify(args, null, 2))}</pre>
|
||||
</div>
|
||||
${resultBlock}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
setToolCallDetailState(item, {
|
||||
args: args,
|
||||
resultData: merged || null,
|
||||
pending: !merged && !options.skipPendingResult,
|
||||
processDetailId: options.processDetailId || '',
|
||||
resultDetailId: data._mergedResultDetailId || '',
|
||||
payloadDeferred: data._payloadDeferred === true || (merged && merged._payloadDeferred === true),
|
||||
payloadLoaded: !(data._payloadDeferred === true || (merged && merged._payloadDeferred === true))
|
||||
});
|
||||
} else if ((type === 'eino_agent_reply' || type === 'workflow_agent_output') && options.message) {
|
||||
let prefix = '';
|
||||
if (type === 'workflow_agent_output' && options.data) {
|
||||
@@ -3839,9 +4011,9 @@ function addTimelineItem(timeline, type, options) {
|
||||
const body = type === 'workflow_agent_output'
|
||||
? `<div class="workflow-agent-output">
|
||||
<div class="workflow-agent-io-label">输出</div>
|
||||
<div class="workflow-agent-output-body">${formatMarkdown(options.message, timelineMarkdownOpts)}</div>
|
||||
<div class="workflow-agent-output-body">${formatTimelinePlainTextHtml(options.message)}</div>
|
||||
</div>`
|
||||
: formatMarkdown(options.message, timelineMarkdownOpts);
|
||||
: formatTimelinePlainTextHtml(options.message);
|
||||
content += `<div class="timeline-item-content workflow-agent-io">${prefix}${body}</div>`;
|
||||
} else if (type === 'workflow_node_result' && options.data && String(options.data.nodeType || '').toLowerCase() === 'condition') {
|
||||
content += buildWorkflowConditionResultHtml(options.data);
|
||||
@@ -3853,17 +4025,17 @@ function addTimelineItem(timeline, type, options) {
|
||||
const noResultText = typeof window.t === 'function' ? window.t('timeline.noResult') : '无结果';
|
||||
const result = data.result || data.error || noResultText;
|
||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
||||
const execResultLabel = typeof window.t === 'function' ? window.t('timeline.executionResult') : '执行结果:';
|
||||
const execIdLabel = typeof window.t === 'function' ? window.t('timeline.executionId') : '执行ID:';
|
||||
content += `
|
||||
<div class="timeline-item-content">
|
||||
<div class="tool-result-section ${isError ? 'error' : 'success'}">
|
||||
<strong data-i18n="timeline.executionResult">${escapeHtml(execResultLabel)}</strong>
|
||||
<pre class="tool-result">${escapeHtml(resultStr)}</pre>
|
||||
${data.executionId ? `<div class="tool-execution-id"><span data-i18n="timeline.executionId">${escapeHtml(execIdLabel)}</span> <code>${escapeHtml(data.executionId)}</code></div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
setToolCallDetailState(item, {
|
||||
resultData: data,
|
||||
rawText: resultStr,
|
||||
pending: false,
|
||||
hideArgs: true,
|
||||
processDetailId: options.processDetailId || '',
|
||||
resultDetailId: options.processDetailId || '',
|
||||
payloadDeferred: data._payloadDeferred === true,
|
||||
payloadLoaded: data._payloadDeferred !== true
|
||||
});
|
||||
item.classList.add(isError ? 'tool-call-failed' : 'tool-call-completed');
|
||||
} else if (type === 'cancelled') {
|
||||
const taskCancelledLabel = typeof window.t === 'function' ? window.t('chat.taskCancelled') : '任务已取消';
|
||||
content += `
|
||||
@@ -3875,21 +4047,25 @@ function addTimelineItem(timeline, type, options) {
|
||||
const streamBody = typeof formatTimelineStreamBody === 'function'
|
||||
? formatTimelineStreamBody(options.message, options.data)
|
||||
: options.message;
|
||||
content += `<div class="timeline-item-content">${formatMarkdown(streamBody, timelineMarkdownOpts)}</div>`;
|
||||
content += `<div class="timeline-item-content timeline-stream-plain">${formatTimelinePlainTextHtml(streamBody)}</div>`;
|
||||
} else if (type === 'progress' && options.message) {
|
||||
content += `<div class="timeline-item-content timeline-eino-trace"><pre class="tool-result">${escapeHtml(options.message)}</pre></div>`;
|
||||
} else if (type === 'user_interrupt_continue' && options.message) {
|
||||
const streamBody = typeof formatTimelineStreamBody === 'function'
|
||||
? formatTimelineStreamBody(options.message, options.data)
|
||||
: options.message;
|
||||
content += `<div class="timeline-item-content">${formatMarkdown(streamBody, timelineMarkdownOpts)}</div>`;
|
||||
content += `<div class="timeline-item-content timeline-stream-plain">${formatTimelinePlainTextHtml(streamBody)}</div>`;
|
||||
}
|
||||
|
||||
item.innerHTML = content;
|
||||
if (item.classList.contains('tool-detail-collapsible')) {
|
||||
updateToolDetailToggleLabel(item);
|
||||
}
|
||||
if (options.data) {
|
||||
applyEinoTimelineRole(item, options.data);
|
||||
}
|
||||
timeline.appendChild(item);
|
||||
pruneLiveTimelineIfNeeded(timeline);
|
||||
|
||||
// 自动展开详情
|
||||
const expanded = timeline.classList.contains('expanded');
|
||||
@@ -6245,6 +6421,14 @@ function refreshProgressAndTimelineI18n() {
|
||||
timeSpan.textContent = d.toLocaleTimeString(timeLocale, timeOpts);
|
||||
}
|
||||
}
|
||||
if (item.classList.contains('tool-detail-collapsible') && typeof updateToolDetailToggleLabel === 'function') {
|
||||
updateToolDetailToggleLabel(item);
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.timeline-live-pruned-marker').forEach(function (marker) {
|
||||
const count = parseInt(marker.dataset.prunedCount || '0', 10) || 0;
|
||||
marker.textContent = _t('chat.liveTimelinePruned', { count: count });
|
||||
});
|
||||
|
||||
// 详情区「展开/收起」按钮
|
||||
|
||||
@@ -2311,9 +2311,15 @@ function initModelListControls() {
|
||||
hitlAuditProv.dataset.modelListBound = '1';
|
||||
hitlAuditProv.addEventListener('change', syncModelListFetchButtons);
|
||||
}
|
||||
const knowledgeEmbeddingProv = document.getElementById('knowledge-embedding-provider');
|
||||
if (knowledgeEmbeddingProv && !knowledgeEmbeddingProv.dataset.modelListBound) {
|
||||
knowledgeEmbeddingProv.dataset.modelListBound = '1';
|
||||
knowledgeEmbeddingProv.addEventListener('change', syncModelListFetchButtons);
|
||||
}
|
||||
bindModelSelect('openai');
|
||||
bindModelSelect('vision');
|
||||
bindModelSelect('hitlAudit');
|
||||
bindModelSelect('knowledgeEmbedding');
|
||||
syncModelListFetchButtons();
|
||||
}
|
||||
|
||||
@@ -2324,6 +2330,9 @@ function modelSelectIds(scope) {
|
||||
if (scope === 'hitlAudit') {
|
||||
return { selectId: 'hitl-audit-model-select', inputId: 'hitl-audit-model-name' };
|
||||
}
|
||||
if (scope === 'knowledgeEmbedding') {
|
||||
return { selectId: 'knowledge-embedding-model-select', inputId: 'knowledge-embedding-model' };
|
||||
}
|
||||
return { selectId: 'openai-model-select', inputId: 'openai-model' };
|
||||
}
|
||||
|
||||
@@ -2359,6 +2368,15 @@ function resolveModelListCredentials(scope) {
|
||||
|| (document.getElementById('openai-api-key')?.value || '').trim();
|
||||
return { provider, base_url: baseUrl, api_key: apiKey };
|
||||
}
|
||||
if (scope === 'knowledgeEmbedding') {
|
||||
const kp = (document.getElementById('knowledge-embedding-provider')?.value || '').trim();
|
||||
const provider = kp || document.getElementById('openai-provider')?.value || 'openai';
|
||||
const baseUrl = (document.getElementById('knowledge-embedding-base-url')?.value || '').trim()
|
||||
|| (document.getElementById('openai-base-url')?.value || '').trim();
|
||||
const apiKey = (document.getElementById('knowledge-embedding-api-key')?.value || '').trim()
|
||||
|| (document.getElementById('openai-api-key')?.value || '').trim();
|
||||
return { provider, base_url: baseUrl, api_key: apiKey };
|
||||
}
|
||||
return {
|
||||
provider: document.getElementById('openai-provider')?.value || 'openai',
|
||||
base_url: (document.getElementById('openai-base-url')?.value || '').trim(),
|
||||
@@ -2444,6 +2462,32 @@ function syncModelListFetchButtons() {
|
||||
hitlAuditHint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
const kp = (document.getElementById('knowledge-embedding-provider')?.value || '').trim();
|
||||
const knowledgeEmbeddingEffectiveProv = kp || openaiProv;
|
||||
const knowledgeEmbeddingBtn = document.getElementById('fetch-knowledge-embedding-models-btn');
|
||||
const knowledgeEmbeddingHint = document.getElementById('fetch-knowledge-embedding-models-hint');
|
||||
const knowledgeEmbeddingSelect = document.getElementById('knowledge-embedding-model-select');
|
||||
const isClaudeKnowledgeEmbedding = knowledgeEmbeddingEffectiveProv === 'claude';
|
||||
if (knowledgeEmbeddingBtn) {
|
||||
knowledgeEmbeddingBtn.style.display = isClaudeKnowledgeEmbedding ? 'none' : '';
|
||||
}
|
||||
if (knowledgeEmbeddingSelect && isClaudeKnowledgeEmbedding) {
|
||||
knowledgeEmbeddingSelect.style.display = 'none';
|
||||
const knowledgeEmbeddingWrap = modelPickSelectMap['knowledge-embedding-model-select'];
|
||||
if (knowledgeEmbeddingWrap) knowledgeEmbeddingWrap.wrapper.style.display = 'none';
|
||||
} else if (knowledgeEmbeddingSelect && !isClaudeKnowledgeEmbedding) {
|
||||
syncModelPickDropdown('knowledge-embedding-model-select');
|
||||
}
|
||||
if (knowledgeEmbeddingHint) {
|
||||
if (isClaudeKnowledgeEmbedding) {
|
||||
knowledgeEmbeddingHint.textContent = tFn('settingsBasic.modelsListClaudeHint');
|
||||
knowledgeEmbeddingHint.style.display = '';
|
||||
} else {
|
||||
knowledgeEmbeddingHint.textContent = '';
|
||||
knowledgeEmbeddingHint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function populateModelSelect(scope, models, currentValue) {
|
||||
@@ -2483,12 +2527,27 @@ function populateModelSelect(scope, models, currentValue) {
|
||||
async function fetchModelList(scope) {
|
||||
const tFn = typeof window.t === 'function' ? window.t : (k) => k;
|
||||
const creds = resolveModelListCredentials(scope);
|
||||
const btnId = scope === 'vision'
|
||||
? 'fetch-vision-models-btn'
|
||||
: (scope === 'hitlAudit' ? 'fetch-hitl-audit-models-btn' : 'fetch-openai-models-btn');
|
||||
const resultId = scope === 'vision'
|
||||
? 'fetch-vision-models-result'
|
||||
: (scope === 'hitlAudit' ? 'fetch-hitl-audit-models-result' : 'fetch-openai-models-result');
|
||||
const modelListUiIds = {
|
||||
openai: {
|
||||
btnId: 'fetch-openai-models-btn',
|
||||
resultId: 'fetch-openai-models-result'
|
||||
},
|
||||
vision: {
|
||||
btnId: 'fetch-vision-models-btn',
|
||||
resultId: 'fetch-vision-models-result'
|
||||
},
|
||||
hitlAudit: {
|
||||
btnId: 'fetch-hitl-audit-models-btn',
|
||||
resultId: 'fetch-hitl-audit-models-result'
|
||||
},
|
||||
knowledgeEmbedding: {
|
||||
btnId: 'fetch-knowledge-embedding-models-btn',
|
||||
resultId: 'fetch-knowledge-embedding-models-result'
|
||||
}
|
||||
};
|
||||
const uiIds = modelListUiIds[scope] || modelListUiIds.openai;
|
||||
const btnId = uiIds.btnId;
|
||||
const resultId = uiIds.resultId;
|
||||
const inputId = modelSelectIds(scope).inputId;
|
||||
const btn = document.getElementById(btnId);
|
||||
const resultEl = document.getElementById(resultId);
|
||||
|
||||
Reference in New Issue
Block a user