Add files via upload

This commit is contained in:
公明
2026-05-10 21:34:34 +08:00
committed by GitHub
parent c62ff3bde9
commit 82d840966e
5 changed files with 88 additions and 14 deletions
+15
View File
@@ -26,6 +26,11 @@ const DRAFT_SAVE_DELAY = 500; // 500ms防抖延迟
// 对话文件上传相关(后端会拼接路径与内容发给大模型,前端不再重复发文件列表)
const MAX_CHAT_FILES = 10;
const CHAT_FILE_DEFAULT_PROMPT = '请根据上传的文件内容进行分析。';
/** 与 handler.formatInterruptContinueUserMessage 首段一致;主对话不展示,仅迭代详情(user_interrupt_continue */
const CHAT_INTERRUPT_CONTINUE_USER_PREFIX = '【用户补充 / 中断后继续】';
function isInterruptContinueInjectChatMessage(content) {
return typeof content === 'string' && content.trimStart().startsWith(CHAT_INTERRUPT_CONTINUE_USER_PREFIX);
}
/**
* 对话附件选文件后异步 POST /api/chat-uploads发送时只传 serverPath绝对路径请求体不再内联大文件内容
* @type {{ id: number, fileName: string, mimeType: string, serverPath: string|null, uploading: boolean, uploadPercent: number, uploadPromise: Promise<void>|null, uploadError: string|null }[]}
@@ -2259,6 +2264,10 @@ function renderProcessDetails(messageId, processDetails) {
itemTitle = agPx + '🧑‍⚖️ HITL · ' + hitlMsg;
} else if (eventType === 'progress') {
itemTitle = typeof window.translateProgressMessage === 'function' ? window.translateProgressMessage(detail.message || '') : (detail.message || '');
} else if (eventType === 'user_interrupt_continue') {
itemTitle = typeof window.t === 'function'
? window.t('chat.userInterruptContinueTitle')
: '⏸️ 用户中断并继续';
}
addTimelineItem(timeline, eventType, {
@@ -2975,6 +2984,9 @@ async function loadConversation(conversationId) {
// 渲染单条消息的辅助函数
const renderOneMessage = (msg) => {
if (msg.role === 'user' && isInterruptContinueInjectChatMessage(msg.content)) {
return;
}
let displayContent = msg.content;
if (msg.role === 'assistant' && msg.content === '处理中...' && msg.processDetails && msg.processDetails.length > 0) {
for (let i = msg.processDetails.length - 1; i >= 0; i--) {
@@ -6639,6 +6651,9 @@ function formatConversationAsMarkdown(conversation, options = {}) {
}
messages.forEach((msg, index) => {
if (msg && msg.role === 'user' && isInterruptContinueInjectChatMessage(msg.content)) {
return;
}
const role = getConversationRoleLabel(msg && msg.role);
const timestamp = formatConversationDateForMarkdown(msg && msg.createdAt);
const content = msg && typeof msg.content === 'string' ? msg.content : '';
+64 -12
View File
@@ -784,19 +784,33 @@ function integrateProgressToMCPSection(progressId, assistantMessageId, mcpExecut
mcpSection.appendChild(buttonsContainer);
}
const hasExecBtns = buttonsContainer.querySelector('.mcp-detail-btn:not(.process-detail-btn)');
if (mcpIds.length > 0 && !hasExecBtns) {
mcpIds.forEach((execId, index) => {
let maxExecIndex = 0;
const existingExecBtns = buttonsContainer.querySelectorAll('.mcp-detail-btn:not(.process-detail-btn)');
existingExecBtns.forEach(function (btn) {
const n = parseInt(btn.dataset.execIndex, 10);
if (!isNaN(n) && n > maxExecIndex) maxExecIndex = n;
});
const seenExec = new Set();
existingExecBtns.forEach(function (btn) {
if (btn.dataset.execId) seenExec.add(String(btn.dataset.execId).trim());
});
let appendedAny = false;
if (mcpIds.length > 0) {
mcpIds.forEach(function (execId) {
const id = execId != null ? String(execId).trim() : '';
if (!id || seenExec.has(id)) return;
seenExec.add(id);
maxExecIndex += 1;
appendedAny = true;
const detailBtn = document.createElement('button');
detailBtn.className = 'mcp-detail-btn';
detailBtn.dataset.execId = execId;
detailBtn.dataset.execIndex = String(index + 1);
detailBtn.innerHTML = '<span>' + (typeof window.t === 'function' ? window.t('chat.callNumber', { n: index + 1 }) : '调用 #' + (index + 1)) + '</span>';
detailBtn.onclick = () => showMCPDetail(execId);
detailBtn.dataset.execId = id;
detailBtn.dataset.execIndex = String(maxExecIndex);
detailBtn.innerHTML = '<span>' + (typeof window.t === 'function' ? window.t('chat.callNumber', { n: maxExecIndex }) : '调用 #' + maxExecIndex) + '</span>';
detailBtn.onclick = function () { showMCPDetail(id); };
buttonsContainer.appendChild(detailBtn);
});
// 使用批量 API 一次性获取所有工具名称(消除 N 次单独请求)
if (typeof batchUpdateButtonToolNames === 'function') {
if (appendedAny && typeof batchUpdateButtonToolNames === 'function') {
batchUpdateButtonToolNames(buttonsContainer, mcpIds);
}
}
@@ -1079,6 +1093,24 @@ function resolveStreamTimeline(progressId) {
return timeline;
}
/** 去重合并 MCP execution id(顺序:先 prev 后 next),用于多段 Run / 多次 SSE 同一任务。 */
function mergeMcpExecutionIDLists(prev, next) {
const seen = new Set();
const out = [];
const add = function (arr) {
if (!Array.isArray(arr)) return;
for (let i = 0; i < arr.length; i++) {
const s = arr[i] != null ? String(arr[i]).trim() : '';
if (!s || seen.has(s)) continue;
seen.add(s);
out.push(s);
}
};
add(prev);
add(next);
return out;
}
// 处理流式事件
function handleStreamEvent(event, progressElement, progressId,
getAssistantId, setAssistantId, getMcpIds, setMcpIds) {
@@ -1320,6 +1352,19 @@ function handleStreamEvent(event, progressElement, progressId,
});
break;
case 'user_interrupt_continue': {
const d = event.data || {};
const titleBase = typeof window.t === 'function'
? window.t('chat.userInterruptContinueTitle')
: '⏸️ 用户中断并继续';
addTimelineItem(timeline, 'user_interrupt_continue', {
title: titleBase,
message: event.message || '',
data: d
});
break;
}
case 'eino_stream_error': {
const d = event.data || {};
const agent = d.einoAgent ? String(d.einoAgent) : '';
@@ -1672,7 +1717,7 @@ function handleStreamEvent(event, progressElement, progressId,
const responseData = event.data || {};
const mcpIds = responseData.mcpExecutionIds || [];
setMcpIds(mcpIds);
setMcpIds(mergeMcpExecutionIDLists(typeof getMcpIds === 'function' ? (getMcpIds() || []) : [], mcpIds));
if (responseData.conversationId) {
// 如果用户已经开始了新对话(currentConversationId 为 null),且这个事件来自旧对话,则忽略
@@ -1748,7 +1793,7 @@ function handleStreamEvent(event, progressElement, progressId,
// 先更新 mcp ids
const responseData = event.data || {};
const mcpIds = responseData.mcpExecutionIds || [];
const mcpIds = mergeMcpExecutionIDLists(typeof getMcpIds === 'function' ? (getMcpIds() || []) : [], responseData.mcpExecutionIds || []);
setMcpIds(mcpIds);
// 更新对话ID
@@ -2272,7 +2317,7 @@ async function attachRunningTaskEventStream(conversationId) {
if (line.indexOf('data: ') === 0) {
try {
const eventData = JSON.parse(line.slice(6));
handleStreamEvent(eventData, null, progressId, getAssistantIdFn, setAssistantIdFn, function () { return mcpIds; }, function (ids) { mcpIds = ids; });
handleStreamEvent(eventData, null, progressId, getAssistantIdFn, setAssistantIdFn, function () { return mcpIds; }, function (ids) { mcpIds = mergeMcpExecutionIDLists(mcpIds, ids || []); });
} catch (e) {
console.error('task-events parse', e);
}
@@ -2485,6 +2530,11 @@ function addTimelineItem(timeline, type, options) {
${escapeHtml(options.message || taskCancelledLabel)}
</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)}</div>`;
}
item.innerHTML = content;
@@ -3386,6 +3436,8 @@ function refreshProgressAndTimelineI18n() {
titleSpan.textContent = ap + '\uD83D\uDCAC ' + _t('chat.einoAgentReplyTitle');
} else if (type === 'cancelled') {
titleSpan.textContent = '\u26D4 ' + _t('chat.taskCancelled');
} else if (type === 'user_interrupt_continue') {
titleSpan.textContent = _t('chat.userInterruptContinueTitle');
} else if (type === 'progress' && item.dataset.progressMessage !== undefined) {
titleSpan.textContent = typeof window.translateProgressMessage === 'function' ? window.translateProgressMessage(item.dataset.progressMessage) : item.dataset.progressMessage;
}