|
|
@ -24,9 +24,12 @@ import { |
|
|
deleteSuperAgentSession, |
|
|
deleteSuperAgentSession, |
|
|
respondSuperAgentCheckpointStream, |
|
|
respondSuperAgentCheckpointStream, |
|
|
sendSuperAgentChat, |
|
|
sendSuperAgentChat, |
|
|
uploadSuperAgentFile, |
|
|
|
|
|
} from "../actions/api"; |
|
|
} from "../actions/api"; |
|
|
import { createResourceFileFromUpload } from "../actions/resourceLibrary"; |
|
|
import { |
|
|
|
|
|
completeAttachmentUpload, |
|
|
|
|
|
createAttachmentUploadIntent, |
|
|
|
|
|
listAttachments, |
|
|
|
|
|
} from "../actions/agentResources"; |
|
|
import useSuperAgentSidebar from "./useSuperAgentSidebar"; |
|
|
import useSuperAgentSidebar from "./useSuperAgentSidebar"; |
|
|
import { |
|
|
import { |
|
|
TASK_STAGE, |
|
|
TASK_STAGE, |
|
|
@ -62,6 +65,11 @@ import { |
|
|
} from "../utils/workspace"; |
|
|
} from "../utils/workspace"; |
|
|
import { normalizeCheckpoint } from "../utils/checkpoint"; |
|
|
import { normalizeCheckpoint } from "../utils/checkpoint"; |
|
|
import { isWorkflowBodyText } from "../utils/tenderWorkflow"; |
|
|
import { isWorkflowBodyText } from "../utils/tenderWorkflow"; |
|
|
|
|
|
import { |
|
|
|
|
|
calculateFileSha256, |
|
|
|
|
|
normalizeAttachmentUploadIntent, |
|
|
|
|
|
uploadFileToIntent, |
|
|
|
|
|
} from "../utils/attachmentUpload"; |
|
|
|
|
|
|
|
|
const getActionData = (res) => { |
|
|
const getActionData = (res) => { |
|
|
const payload = res?.payload ?? res; |
|
|
const payload = res?.payload ?? res; |
|
|
@ -76,41 +84,19 @@ const ensureActionSuccess = (res, fallbackMessage) => { |
|
|
return getActionData(res); |
|
|
return getActionData(res); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const toReadyUploadedFile = (attachment) => ({ |
|
|
|
|
|
id: String(attachment.id || attachment.attachment_id || attachment.attachmentId), |
|
|
|
|
|
attachmentId: String(attachment.id || attachment.attachment_id || attachment.attachmentId), |
|
|
|
|
|
name: attachment.file_name || attachment.filename || "未命名文件", |
|
|
|
|
|
status: attachment.status, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
const buildSessionParams = (sessionId) => ({ |
|
|
const buildSessionParams = (sessionId) => ({ |
|
|
conversationId: sessionId, |
|
|
conversationId: sessionId, |
|
|
sessionId, |
|
|
sessionId, |
|
|
":sessionId": sessionId, |
|
|
":sessionId": sessionId, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const hasFilePathPrefix = (filePath = "") => { |
|
|
|
|
|
return /^(https?:)?\/\//i.test(filePath) || /^(data|blob):/i.test(filePath); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const buildQiniuFilePath = (filePath = "") => { |
|
|
|
|
|
const cleanFilePath = String(filePath || "").trim(); |
|
|
|
|
|
if (!cleanFilePath || hasFilePathPrefix(cleanFilePath)) return cleanFilePath; |
|
|
|
|
|
|
|
|
|
|
|
const qiniuDomain = window?.env?.FS_QINIU_DOMAIN || ""; |
|
|
|
|
|
const cleanDomain = String(qiniuDomain || "").replace(/\/+$/, ""); |
|
|
|
|
|
if (!cleanDomain) return cleanFilePath; |
|
|
|
|
|
|
|
|
|
|
|
return `${cleanDomain}/${cleanFilePath.replace(/^\/+/, "")}`; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//[文件上下文] 上传文件只向智能体传递访问地址,不附加固定业务文案。
|
|
|
|
|
|
const buildUploadedFileReference = (fileList = []) => { |
|
|
|
|
|
return fileList |
|
|
|
|
|
.map((file) => { |
|
|
|
|
|
const fileName = file?.name || file?.originalFilename || "未命名文件"; |
|
|
|
|
|
const rawFilePath = file?.url || file?.path || file?.objectKey || file?.localPath || ""; |
|
|
|
|
|
const filePath = buildQiniuFilePath(rawFilePath); |
|
|
|
|
|
if (!filePath) return ""; |
|
|
|
|
|
return `(文件名:${fileName},文件路径:${filePath})`; |
|
|
|
|
|
}) |
|
|
|
|
|
.filter(Boolean) |
|
|
|
|
|
.join("\n"); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const useSuperAgentChat = ({ |
|
|
const useSuperAgentChat = ({ |
|
|
currentUserId, |
|
|
currentUserId, |
|
|
openAuthModal, |
|
|
openAuthModal, |
|
|
@ -156,6 +142,7 @@ const useSuperAgentChat = ({ |
|
|
const streamingMessageKeyRef = useRef(""); |
|
|
const streamingMessageKeyRef = useRef(""); |
|
|
const chatRunPromiseRef = useRef(Promise.resolve()); |
|
|
const chatRunPromiseRef = useRef(Promise.resolve()); |
|
|
const chatRunActiveRef = useRef(false); |
|
|
const chatRunActiveRef = useRef(false); |
|
|
|
|
|
const uploadAbortControllerRef = useRef(null); |
|
|
const creatingSessionRef = useRef(false); |
|
|
const creatingSessionRef = useRef(false); |
|
|
const sidebarRefreshTimerRef = useRef(null); |
|
|
const sidebarRefreshTimerRef = useRef(null); |
|
|
const artifactProgressSessionRef = useRef(""); |
|
|
const artifactProgressSessionRef = useRef(""); |
|
|
@ -168,6 +155,33 @@ const useSuperAgentChat = ({ |
|
|
activeSessionIdRef.current = activeSessionId; |
|
|
activeSessionIdRef.current = activeSessionId; |
|
|
}, [activeSessionId]); |
|
|
}, [activeSessionId]); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
const conversationId = String(activeConversationId || ""); |
|
|
|
|
|
if (!conversationId) { |
|
|
|
|
|
setUploadedFileList([]); |
|
|
|
|
|
return undefined; |
|
|
|
|
|
} |
|
|
|
|
|
let active = true; |
|
|
|
|
|
listAttachments(conversationId) |
|
|
|
|
|
.then((page) => { |
|
|
|
|
|
if (!active) return; |
|
|
|
|
|
setUploadedFileList( |
|
|
|
|
|
page.items |
|
|
|
|
|
.filter((attachment) => attachment.status === "ready" && !attachment.message_id) |
|
|
|
|
|
.map(toReadyUploadedFile) |
|
|
|
|
|
); |
|
|
|
|
|
}) |
|
|
|
|
|
.catch((error) => { |
|
|
|
|
|
superAgentWarn("chat.upload", "attachment_list_failed", { |
|
|
|
|
|
conversationId, |
|
|
|
|
|
error: error?.message, |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
return () => { |
|
|
|
|
|
active = false; |
|
|
|
|
|
}; |
|
|
|
|
|
}, [activeConversationId]); |
|
|
|
|
|
|
|
|
const activeTask = useMemo( |
|
|
const activeTask = useMemo( |
|
|
() => taskList.find((task) => task.id === activeTaskId) || null, |
|
|
() => taskList.find((task) => task.id === activeTaskId) || null, |
|
|
[activeTaskId, taskList] |
|
|
[activeTaskId, taskList] |
|
|
@ -357,6 +371,9 @@ const useSuperAgentChat = ({ |
|
|
if (chatAbortControllerRef.current) { |
|
|
if (chatAbortControllerRef.current) { |
|
|
chatAbortControllerRef.current.abort(); |
|
|
chatAbortControllerRef.current.abort(); |
|
|
} |
|
|
} |
|
|
|
|
|
if (uploadAbortControllerRef.current) { |
|
|
|
|
|
uploadAbortControllerRef.current.abort(); |
|
|
|
|
|
} |
|
|
if (sidebarRefreshTimerRef.current) { |
|
|
if (sidebarRefreshTimerRef.current) { |
|
|
window.clearTimeout(sidebarRefreshTimerRef.current); |
|
|
window.clearTimeout(sidebarRefreshTimerRef.current); |
|
|
} |
|
|
} |
|
|
@ -398,6 +415,7 @@ const useSuperAgentChat = ({ |
|
|
folderId = null, |
|
|
folderId = null, |
|
|
title = "", |
|
|
title = "", |
|
|
shouldResetMessages = false, |
|
|
shouldResetMessages = false, |
|
|
|
|
|
returnConversationId = false, |
|
|
} = {}) => { |
|
|
} = {}) => { |
|
|
if (!currentUserId) { |
|
|
if (!currentUserId) { |
|
|
openAuthModal("login"); |
|
|
openAuthModal("login"); |
|
|
@ -469,7 +487,7 @@ const useSuperAgentChat = ({ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
await loadSidebar({ silent: true }); |
|
|
await loadSidebar({ silent: true }); |
|
|
return nextSessionId; |
|
|
return returnConversationId ? nextSessionId : nextThreadId; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const getCurrentSessionType = () => { |
|
|
const getCurrentSessionType = () => { |
|
|
@ -1545,14 +1563,14 @@ const useSuperAgentChat = ({ |
|
|
|
|
|
|
|
|
//[执行上传文件的实际逻辑]
|
|
|
//[执行上传文件的实际逻辑]
|
|
|
const performUpload = (file, fileName, currentSessionType) => { |
|
|
const performUpload = (file, fileName, currentSessionType) => { |
|
|
const currentSessionId = activeSessionId || activeTaskId; |
|
|
const currentConversationId = activeConversationId || activeTaskId; |
|
|
const shouldCreateSession = !currentSessionId; |
|
|
const shouldCreateSession = !currentConversationId; |
|
|
const shouldUseTaskSession = |
|
|
const shouldUseTaskSession = |
|
|
currentSessionType === "task" || (shouldCreateSession && isTaskRoute); |
|
|
currentSessionType === "task" || (shouldCreateSession && isTaskRoute); |
|
|
const shouldCreateNewTask = shouldCreateSession && shouldUseTaskSession; |
|
|
const shouldCreateNewTask = shouldCreateSession && shouldUseTaskSession; |
|
|
superAgentLog("chat.upload", "perform.start", { |
|
|
superAgentLog("chat.upload", "perform.start", { |
|
|
fileName, |
|
|
fileName, |
|
|
currentSessionId, |
|
|
currentConversationId, |
|
|
currentSessionType, |
|
|
currentSessionType, |
|
|
shouldCreateSession, |
|
|
shouldCreateSession, |
|
|
shouldUseTaskSession, |
|
|
shouldUseTaskSession, |
|
|
@ -1583,23 +1601,32 @@ const useSuperAgentChat = ({ |
|
|
status: "preparing", |
|
|
status: "preparing", |
|
|
}); |
|
|
}); |
|
|
try { |
|
|
try { |
|
|
const sessionId = |
|
|
const conversationId = |
|
|
currentSessionId || |
|
|
currentConversationId || |
|
|
(await createAndActivateSession({ |
|
|
(await createAndActivateSession({ |
|
|
isTaskSession: shouldUseTaskSession, |
|
|
isTaskSession: shouldUseTaskSession, |
|
|
title: shouldUseTaskSession |
|
|
title: shouldUseTaskSession |
|
|
? fileName.replace(/\.[^.]+$/, "") || "AI 标书任务" |
|
|
? fileName.replace(/\.[^.]+$/, "") || "AI 标书任务" |
|
|
: "", |
|
|
: "", |
|
|
|
|
|
returnConversationId: true, |
|
|
})); |
|
|
})); |
|
|
superAgentLog("chat.upload", "session.ready", { |
|
|
superAgentLog("chat.upload", "session.ready", { |
|
|
sessionId, |
|
|
conversationId, |
|
|
fileName, |
|
|
fileName, |
|
|
shouldUseTaskSession, |
|
|
shouldUseTaskSession, |
|
|
}); |
|
|
}); |
|
|
const uploadResult = await uploadSuperAgentFile({ |
|
|
const sha256 = await calculateFileSha256(file); |
|
|
sessionId, |
|
|
const uploadIntent = normalizeAttachmentUploadIntent( |
|
|
userId: currentUserId, |
|
|
await createAttachmentUploadIntent(conversationId, { |
|
|
file, |
|
|
file_name: fileName, |
|
|
|
|
|
mime_type: file.type || "application/octet-stream", |
|
|
|
|
|
file_size: Number(file.size || 0), |
|
|
|
|
|
...(sha256 ? { sha256 } : {}), |
|
|
|
|
|
}) |
|
|
|
|
|
); |
|
|
|
|
|
uploadAbortControllerRef.current = new AbortController(); |
|
|
|
|
|
await uploadFileToIntent(uploadIntent, file, { |
|
|
|
|
|
signal: uploadAbortControllerRef.current.signal, |
|
|
onProgress: (progress) => { |
|
|
onProgress: (progress) => { |
|
|
setUploadProgress({ |
|
|
setUploadProgress({ |
|
|
fileName, |
|
|
fileName, |
|
|
@ -1613,57 +1640,36 @@ const useSuperAgentChat = ({ |
|
|
percent: 100, |
|
|
percent: 100, |
|
|
status: "processing", |
|
|
status: "processing", |
|
|
})); |
|
|
})); |
|
|
const uploadData = uploadResult?.data || uploadResult || {}; |
|
|
const uploadData = await completeAttachmentUpload(uploadIntent.attachmentId); |
|
|
try { |
|
|
const attachmentPage = await listAttachments(conversationId); |
|
|
await createResourceFileFromUpload(uploadData); |
|
|
const readyAttachment = attachmentPage.items.find((attachment) => |
|
|
} catch (resourceError) { |
|
|
String(attachment.id || attachment.attachment_id || attachment.attachmentId) === uploadIntent.attachmentId |
|
|
superAgentWarn("chat.upload", "resource_library_save_failed", { |
|
|
) || uploadData?.data || uploadData || {}; |
|
|
fileName, |
|
|
if (readyAttachment.status !== "ready") { |
|
|
error: resourceError?.message, |
|
|
throw new Error("附件尚未准备完成,请稍后重试"); |
|
|
}); |
|
|
|
|
|
message.warning("文件已上传,但保存到资源库失败"); |
|
|
|
|
|
} |
|
|
} |
|
|
const uploadedFileName = |
|
|
const uploadedFileName = |
|
|
uploadData.filename || |
|
|
readyAttachment.file_name || |
|
|
uploadData.original_filename || |
|
|
readyAttachment.filename || |
|
|
uploadData.originalFilename || |
|
|
readyAttachment.original_filename || |
|
|
|
|
|
readyAttachment.originalFilename || |
|
|
fileName; |
|
|
fileName; |
|
|
const rawUploadedFilePath = |
|
|
|
|
|
uploadData.path || |
|
|
|
|
|
uploadData.url || |
|
|
|
|
|
uploadData.object_key || |
|
|
|
|
|
uploadData.objectKey || |
|
|
|
|
|
""; |
|
|
|
|
|
const uploadedFilePath = buildQiniuFilePath(rawUploadedFilePath); |
|
|
|
|
|
const uploadedLocalPath = uploadData.local_path || uploadData.localPath || ""; |
|
|
|
|
|
const uploadedFileStorage = uploadData.storage || ""; |
|
|
|
|
|
const normalizedUploadedFileStorage = |
|
|
|
|
|
uploadedFileStorage || uploadData.storage_provider || uploadData.storageProvider || ""; |
|
|
|
|
|
superAgentLog("chat.upload", "upload.done", { |
|
|
superAgentLog("chat.upload", "upload.done", { |
|
|
sessionId, |
|
|
conversationId, |
|
|
uploadedFileName, |
|
|
uploadedFileName, |
|
|
uploadedFilePath, |
|
|
attachmentId: uploadIntent.attachmentId, |
|
|
uploadedLocalPath, |
|
|
}); |
|
|
uploadedFileStorage: normalizedUploadedFileStorage, |
|
|
const uploadedFile = toReadyUploadedFile({ |
|
|
uploadResult, |
|
|
...readyAttachment, |
|
|
|
|
|
id: uploadIntent.attachmentId, |
|
|
|
|
|
file_name: uploadedFileName, |
|
|
|
|
|
status: "ready", |
|
|
}); |
|
|
}); |
|
|
const uploadedFile = { |
|
|
|
|
|
id: `${sessionId}_${uploadedFileName}_${Date.now()}`, |
|
|
|
|
|
attachmentId: uploadData.id || "", |
|
|
|
|
|
name: uploadedFileName, |
|
|
|
|
|
url: uploadedFilePath, |
|
|
|
|
|
objectKey: uploadData.object_key || uploadData.objectKey || "", |
|
|
|
|
|
localPath: uploadedLocalPath, |
|
|
|
|
|
storage: normalizedUploadedFileStorage, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if (shouldCreateNewTask) { |
|
|
if (shouldCreateNewTask) { |
|
|
updateActiveTask({ |
|
|
updateActiveTask({ |
|
|
title: uploadedFileName.replace(/\.[^.]+$/, "") || "AI 标书任务", |
|
|
title: uploadedFileName.replace(/\.[^.]+$/, "") || "AI 标书任务", |
|
|
fileName: uploadedFileName, |
|
|
fileName: uploadedFileName, |
|
|
filePath: uploadedFilePath, |
|
|
|
|
|
localPath: uploadedLocalPath, |
|
|
|
|
|
fileStorage: normalizedUploadedFileStorage, |
|
|
|
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1677,6 +1683,7 @@ const useSuperAgentChat = ({ |
|
|
}); |
|
|
}); |
|
|
message.error(error?.message || "上传文件失败"); |
|
|
message.error(error?.message || "上传文件失败"); |
|
|
} finally { |
|
|
} finally { |
|
|
|
|
|
uploadAbortControllerRef.current = null; |
|
|
setIsUploadingFile(false); |
|
|
setIsUploadingFile(false); |
|
|
setUploadProgress(null); |
|
|
setUploadProgress(null); |
|
|
} |
|
|
} |
|
|
@ -1689,6 +1696,10 @@ const useSuperAgentChat = ({ |
|
|
setUploadedFileList((prev) => prev.filter((file) => file.id !== fileId)); |
|
|
setUploadedFileList((prev) => prev.filter((file) => file.id !== fileId)); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleCancelUpload = () => { |
|
|
|
|
|
uploadAbortControllerRef.current?.abort(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const confirmBasicInfo = () => { |
|
|
const confirmBasicInfo = () => { |
|
|
superAgentLog("chat.confirm", "basic_info", { activeSessionId, activeTaskId }); |
|
|
superAgentLog("chat.confirm", "basic_info", { activeSessionId, activeTaskId }); |
|
|
setTaskStage(TASK_STAGE.SCORE); |
|
|
setTaskStage(TASK_STAGE.SCORE); |
|
|
@ -1727,8 +1738,9 @@ const useSuperAgentChat = ({ |
|
|
const handleSend = () => { |
|
|
const handleSend = () => { |
|
|
const userInput = inputValue.trim(); |
|
|
const userInput = inputValue.trim(); |
|
|
const currentFileList = uploadedFileList; |
|
|
const currentFileList = uploadedFileList; |
|
|
const fileReferenceText = buildUploadedFileReference(currentFileList); |
|
|
const finalMessage = userInput || (currentFileList.length |
|
|
const finalMessage = [fileReferenceText, userInput].filter(Boolean).join("\n"); |
|
|
? `请处理已上传的附件:${currentFileList.map((file) => file.name).filter(Boolean).join("、")}` |
|
|
|
|
|
: ""); |
|
|
|
|
|
|
|
|
if (!finalMessage || isStreamingAnswer) return; |
|
|
if (!finalMessage || isStreamingAnswer) return; |
|
|
|
|
|
|
|
|
@ -1770,7 +1782,10 @@ const useSuperAgentChat = ({ |
|
|
//[文件引用写入历史展示字段,刷新或切换会话后才能还原文件标签]
|
|
|
//[文件引用写入历史展示字段,刷新或切换会话后才能还原文件标签]
|
|
|
displayContent: finalMessage, |
|
|
displayContent: finalMessage, |
|
|
attachmentIds: Array.from( |
|
|
attachmentIds: Array.from( |
|
|
new Set(currentFileList.map((file) => file.attachmentId).filter(Boolean)) |
|
|
new Set(currentFileList |
|
|
|
|
|
.filter((file) => file.status === "ready") |
|
|
|
|
|
.map((file) => file.attachmentId) |
|
|
|
|
|
.filter(Boolean)) |
|
|
), |
|
|
), |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
@ -1887,6 +1902,7 @@ const useSuperAgentChat = ({ |
|
|
handleTenderQuickStart, |
|
|
handleTenderQuickStart, |
|
|
handleUploadFile, |
|
|
handleUploadFile, |
|
|
handleRemoveUploadedFile, |
|
|
handleRemoveUploadedFile, |
|
|
|
|
|
handleCancelUpload, |
|
|
confirmBasicInfo, |
|
|
confirmBasicInfo, |
|
|
confirmScoreItems, |
|
|
confirmScoreItems, |
|
|
confirmCatalog, |
|
|
confirmCatalog, |
|
|
|