From df531910cfebd6dd36bc734a143948f3c43b1e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Thu, 28 May 2026 14:34:14 +0800 Subject: [PATCH] Add files via upload --- web/static/i18n/en-US.json | 5 +++++ web/static/i18n/zh-CN.json | 5 +++++ web/static/js/chat.js | 14 +++++++++++++ web/static/js/monitor.js | 35 +++++++++++++++++++++++++++++++++ web/static/js/tasks.js | 40 +++++++++++++++++++++++++++++++++++++- web/templates/index.html | 7 +++++++ 6 files changed, 105 insertions(+), 1 deletion(-) diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 9bb53324..0511cb64 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -470,6 +470,8 @@ "einoAgentReplyTitle": "Sub-agent reply", "einoStreamErrorTitle": "⚠️ Eino stream interrupted ({{agent}})", "einoStreamErrorMessage": "Streaming read failed; the system will retry or terminate according to policy.", + "einoRunRetryTitle": "🔁 Transient error retry", + "einoRunRetryErrorDetail": "Error detail", "iterationLimitReachedTitle": "⛔ Iteration limit reached", "iterationLimitReachedMessage": "Maximum iteration count reached; automatic iteration has stopped.", "einoPendingOrphanedTitle": "🧹 Tool call reconciliation", @@ -2271,6 +2273,9 @@ "role": "Role", "defaultRole": "Default", "roleHint": "Select a role; all tasks will be executed using that role's configuration (prompt and tools).", + "project": "Project", + "projectNone": "(Unbound)", + "projectHint": "Optionally bind this queue to a project; leave empty to keep it unbound.", "agentMode": "Agent mode", "agentModeSingle": "Single-agent (ReAct)", "agentModeMulti": "Multi-agent (Eino)", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 8545bbd1..8a1981f7 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -459,6 +459,8 @@ "einoAgentReplyTitle": "子代理回复", "einoStreamErrorTitle": "⚠️ Eino 流式中断({{agent}})", "einoStreamErrorMessage": "流式读取异常,系统将按策略重试或结束。", + "einoRunRetryTitle": "🔁 临时错误重试", + "einoRunRetryErrorDetail": "具体报错", "iterationLimitReachedTitle": "⛔ 达到迭代上限", "iterationLimitReachedMessage": "已达到最大迭代次数,任务已停止继续自动迭代。", "einoPendingOrphanedTitle": "🧹 工具调用收尾补偿", @@ -2260,6 +2262,9 @@ "role": "角色", "defaultRole": "默认", "roleHint": "选择一个角色,所有任务将使用该角色的配置(提示词和工具)执行。", + "project": "所属项目", + "projectNone": "(未绑定)", + "projectHint": "可为队列绑定项目;留空则不绑定项目上下文。", "agentMode": "代理模式", "agentModeSingle": "单代理(ReAct)", "agentModeMulti": "多代理(Eino)", diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 51d81990..18c2d39c 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -2479,6 +2479,20 @@ function renderProcessDetails(messageId, processDetails) { itemTitle = agPx + execLine; } else if (eventType === 'eino_agent_reply') { itemTitle = agPx + '💬 ' + (typeof window.t === 'function' ? window.t('chat.einoAgentReplyTitle') : '子代理回复'); + } else if (eventType === 'eino_run_retry') { + itemTitle = typeof window.t === 'function' + ? window.t('chat.einoRunRetryTitle') + : '🔁 临时错误重试'; + const errRaw = data && data.error != null ? String(data.error).trim() : ''; + if (errRaw) { + const detailLabel = typeof window.t === 'function' + ? window.t('chat.einoRunRetryErrorDetail') + : '错误详情'; + if (!title || String(title).indexOf(errRaw) === -1) { + const merged = title ? (String(title) + '\n' + detailLabel + ':' + errRaw) : (detailLabel + ':' + errRaw); + detail.message = merged; + } + } } else if (eventType === 'knowledge_retrieval') { itemTitle = '📚 ' + (typeof window.t === 'function' ? window.t('chat.knowledgeRetrieval') : '知识检索'); } else if (eventType === 'error') { diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index 5f40ab84..8eb60c85 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -1271,6 +1271,22 @@ function mergeMcpExecutionIDLists(prev, next) { return out; } +function formatEinoRunRetryMessage(message, data) { + const d = data && typeof data === 'object' ? data : {}; + const base = String(message || '').trim(); + const errRaw = d.error != null ? String(d.error).trim() : ''; + if (!errRaw) { + return base; + } + const detailLabel = typeof window.t === 'function' + ? window.t('chat.einoRunRetryErrorDetail') + : '错误详情'; + if (base && base.indexOf(errRaw) !== -1) { + return base; + } + return base ? (base + '\n' + detailLabel + ':' + errRaw) : (detailLabel + ':' + errRaw); +} + // 处理流式事件 function handleStreamEvent(event, progressElement, progressId, getAssistantId, setAssistantId, getMcpIds, setMcpIds) { @@ -1582,6 +1598,20 @@ function handleStreamEvent(event, progressElement, progressId, break; } + case 'eino_run_retry': { + const d = event.data || {}; + const title = typeof window.t === 'function' + ? window.t('chat.einoRunRetryTitle') + : '🔁 临时错误重试'; + const msg = formatEinoRunRetryMessage(event.message, d); + addTimelineItem(timeline, 'warning', { + title: title, + message: msg, + data: d + }); + break; + } + case 'iteration_limit_reached': { addTimelineItem(timeline, 'warning', { title: typeof window.t === 'function' ? window.t('chat.iterationLimitReachedTitle') : '⛔ 达到迭代上限', @@ -2966,6 +2996,11 @@ function addTimelineItem(timeline, type, options) { ${escapeHtml(options.message || taskCancelledLabel)} `; + } else if (type === 'warning' && options.message) { + const streamBody = typeof formatTimelineStreamBody === 'function' + ? formatTimelineStreamBody(options.message, options.data) + : options.message; + content += `
${formatMarkdown(streamBody)}
`; } else if (type === 'progress' && options.message) { content += `
${escapeHtml(options.message)}
`; } else if (type === 'user_interrupt_continue' && options.message) { diff --git a/web/static/js/tasks.js b/web/static/js/tasks.js index 69458742..644474cb 100644 --- a/web/static/js/tasks.js +++ b/web/static/js/tasks.js @@ -812,12 +812,44 @@ const batchQueuesState = { totalPages: 1 }; +async function refreshBatchProjectSelectOptions() { + const projectSelect = document.getElementById('batch-queue-project-id'); + if (!projectSelect) return; + + const noneLabel = _t('batchImportModal.projectNone'); + projectSelect.innerHTML = ``; + + try { + const response = await apiFetch('/api/projects?status=active&limit=200'); + if (!response.ok) { + throw new Error(_t('projects.loadProjectsFailed')); + } + const projects = await response.json(); + const list = Array.isArray(projects) ? projects : []; + const activeProjectId = typeof getActiveProjectId === 'function' ? getActiveProjectId() || '' : ''; + + list.forEach((project) => { + if (!project || !project.id) return; + const option = document.createElement('option'); + option.value = project.id; + option.textContent = project.name || project.id; + if (activeProjectId && project.id === activeProjectId) { + option.selected = true; + } + projectSelect.appendChild(option); + }); + } catch (error) { + console.warn('加载项目列表失败:', error); + } +} + // 显示新建任务模态框 async function showBatchImportModal() { const modal = document.getElementById('batch-import-modal'); const input = document.getElementById('batch-tasks-input'); const titleInput = document.getElementById('batch-queue-title'); const roleSelect = document.getElementById('batch-queue-role'); + const projectSelect = document.getElementById('batch-queue-project-id'); const agentModeSelect = document.getElementById('batch-queue-agent-mode'); const scheduleModeSelect = document.getElementById('batch-queue-schedule-mode'); const cronExprInput = document.getElementById('batch-queue-cron-expr'); @@ -831,6 +863,9 @@ async function showBatchImportModal() { if (roleSelect) { roleSelect.value = ''; } + if (projectSelect) { + projectSelect.value = ''; + } if (agentModeSelect) { agentModeSelect.value = 'single'; } @@ -872,6 +907,7 @@ async function showBatchImportModal() { console.error('加载角色列表失败:', error); } } + await refreshBatchProjectSelectOptions(); modal.style.display = 'block'; input.focus(); @@ -935,6 +971,7 @@ async function createBatchQueue() { const input = document.getElementById('batch-tasks-input'); const titleInput = document.getElementById('batch-queue-title'); const roleSelect = document.getElementById('batch-queue-role'); + const projectSelect = document.getElementById('batch-queue-project-id'); const agentModeSelect = document.getElementById('batch-queue-agent-mode'); const scheduleModeSelect = document.getElementById('batch-queue-schedule-mode'); const cronExprInput = document.getElementById('batch-queue-cron-expr'); @@ -959,6 +996,7 @@ async function createBatchQueue() { // 获取角色(可选,空字符串表示默认角色) const role = roleSelect ? roleSelect.value || '' : ''; + const projectId = projectSelect ? (projectSelect.value || '').trim() : ''; const rawMode = agentModeSelect ? agentModeSelect.value : 'single'; const agentMode = isBatchQueueAgentMode(rawMode) ? rawMode : 'single'; const scheduleMode = scheduleModeSelect ? (scheduleModeSelect.value === 'cron' ? 'cron' : 'manual') : 'manual'; @@ -987,7 +1025,7 @@ async function createBatchQueue() { scheduleMode, cronExpr, executeNow, - projectId: typeof getActiveProjectId === 'function' ? getActiveProjectId() || '' : '', + projectId, }), }); diff --git a/web/templates/index.html b/web/templates/index.html index eae276f4..ebd9cb4b 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -3678,6 +3678,13 @@
选择一个角色,所有任务将使用该角色的配置(提示词和工具)执行。
+
+ + +
可为队列绑定项目;留空则不绑定项目上下文。
+