You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
951 lines
26 KiB
951 lines
26 KiB
/**
|
|
* 文件作用:superAgent 超级智能体模块文件
|
|
* 职责范围:
|
|
* 1. 承载 superAgent 业务 session、workspace 和附件能力
|
|
* 2. 在不影响 newAiChat 的前提下复用现有组件和接口
|
|
* 3. 支撑标书任务工作区的前端编排
|
|
*
|
|
* 不负责:
|
|
* - 修改 newAiChat 原有页面行为
|
|
* - 实现后端标书解析、正文生成或导出接口
|
|
* - 封装 v2 useStream 协议;新版流由 useSuperAgentStream 直接交给 SDK 管理
|
|
*
|
|
* 维护说明:
|
|
* - 不负责消息流、thread 快照或 interrupt 恢复,这些能力统一由 useStream 管理
|
|
*/
|
|
"use strict";
|
|
|
|
import {
|
|
danaApi,
|
|
fastgptApiTable,
|
|
superAgentServiceApiTable,
|
|
basicAction,
|
|
} from "@u";
|
|
import {
|
|
buildSuperAgentHeaders,
|
|
buildSuperAgentPath,
|
|
buildSuperAgentServiceUrl,
|
|
requestAgentJson,
|
|
} from "../utils/agentApi";
|
|
import request from "superagent";
|
|
import {
|
|
createSuperAgentTraceId,
|
|
summarizeSuperAgentPayload,
|
|
superAgentError,
|
|
superAgentLog,
|
|
} from "../utils/debugLog";
|
|
|
|
const createDirectJsonAction = ({ method, path, fallbackMessage }) => (options = {}) => async () => {
|
|
const data = await requestAgentJson({
|
|
method,
|
|
path,
|
|
params: options.params,
|
|
query: options.query,
|
|
body: options.body,
|
|
fallbackMessage,
|
|
});
|
|
return { payload: { data } };
|
|
};
|
|
|
|
// SuperAgent 前端业务接口使用同源路径,由 web/config.cjs 转发到 Agent 服务。
|
|
export const listSuperAgentSessions = createDirectJsonAction({
|
|
method: "GET",
|
|
path: superAgentServiceApiTable.conversations,
|
|
fallbackMessage: "获取超级智能体会话失败",
|
|
});
|
|
|
|
export const createSuperAgentSession = (options = {}) => async () => {
|
|
const requestBody = options.body || {};
|
|
const sessionType =
|
|
requestBody.type === "task" || requestBody.type === "conversation"
|
|
? requestBody.type
|
|
: "conversation";
|
|
const body = {
|
|
type: sessionType,
|
|
name: String(requestBody.name || requestBody.title || (sessionType === "conversation" ? "新对话" : "新任务")),
|
|
};
|
|
const tasksetId = requestBody.taskset_id ?? requestBody.tasksetId ?? requestBody.folder_id ?? requestBody.folderId;
|
|
if (sessionType === "task") {
|
|
body.taskset_id = tasksetId;
|
|
}
|
|
|
|
const data = await requestSuperAgentJson({
|
|
method: "POST",
|
|
path: superAgentServiceApiTable.conversations,
|
|
query: options.query,
|
|
body,
|
|
fallbackMessage: "创建超级智能体会话失败",
|
|
});
|
|
return { payload: { data } };
|
|
};
|
|
|
|
export const getSuperAgentSessionSections = createDirectJsonAction({
|
|
method: "GET",
|
|
path: superAgentServiceApiTable.artifactChapters,
|
|
fallbackMessage: "获取超级智能体章节列表失败",
|
|
});
|
|
|
|
export const getSuperAgentSessionSection = createDirectJsonAction({
|
|
method: "GET",
|
|
path: superAgentServiceApiTable.artifactChapter,
|
|
fallbackMessage: "获取超级智能体章节内容失败",
|
|
});
|
|
|
|
export const updateSuperAgentSessionSection = createDirectJsonAction({
|
|
method: "PUT",
|
|
path: superAgentServiceApiTable.artifactChapter,
|
|
fallbackMessage: "保存超级智能体章节内容失败",
|
|
});
|
|
|
|
export const deleteSuperAgentSession = createDirectJsonAction({
|
|
method: "DELETE",
|
|
path: superAgentServiceApiTable.conversationDetail,
|
|
fallbackMessage: "删除超级智能体会话失败",
|
|
});
|
|
|
|
export const getSuperAgentSidebar = createDirectJsonAction({
|
|
method: "GET",
|
|
path: superAgentServiceApiTable.tasksets,
|
|
fallbackMessage: "获取超级智能体侧栏失败",
|
|
});
|
|
|
|
export const createSuperAgentFolder = createDirectJsonAction({
|
|
method: "POST",
|
|
path: superAgentServiceApiTable.tasksets,
|
|
fallbackMessage: "创建文件夹失败",
|
|
});
|
|
|
|
export const deleteSuperAgentFolder = createDirectJsonAction({
|
|
method: "DELETE",
|
|
path: superAgentServiceApiTable.tasksetDetail,
|
|
fallbackMessage: "删除文件夹失败",
|
|
});
|
|
|
|
export const respondSuperAgentCheckpoint = (options = {}) => async () => {
|
|
const body = options.body || {};
|
|
const sessionId = body.session_id || body.sessionId;
|
|
const data = await requestSuperAgentJson({
|
|
method: "POST",
|
|
path: superAgentServiceApiTable.threadCommands,
|
|
params: { sessionId },
|
|
query: options.query,
|
|
body,
|
|
fallbackMessage: "提交确认结果失败",
|
|
});
|
|
return { payload: { data } };
|
|
};
|
|
|
|
export const respondSuperAgentCheckpointStream = async ({
|
|
body,
|
|
userId,
|
|
onEvent,
|
|
signal,
|
|
sessionId,
|
|
}) => {
|
|
const requestBody = body || {};
|
|
const traceId = createSuperAgentTraceId("checkpoint_respond");
|
|
const requestSessionId = sessionId || requestBody.session_id || requestBody.sessionId || "";
|
|
superAgentLog("api.checkpoint", "request.start", {
|
|
traceId,
|
|
sessionId: requestSessionId,
|
|
checkpointId: requestBody.checkpoint_id,
|
|
action: requestBody.action,
|
|
});
|
|
await requestSuperAgentStream({
|
|
path: buildSuperAgentPath(superAgentServiceApiTable.threadCommands, {
|
|
sessionId: requestSessionId,
|
|
}),
|
|
userId,
|
|
body: requestBody,
|
|
onEvent,
|
|
signal,
|
|
fallbackMessage: "提交确认结果失败",
|
|
traceId,
|
|
logScope: "api.checkpoint",
|
|
sessionId: requestSessionId,
|
|
});
|
|
return {
|
|
success: true,
|
|
sessionId: requestSessionId,
|
|
};
|
|
};
|
|
|
|
const requestSuperAgentJson = async (options) => requestAgentJson(options);
|
|
|
|
export const updateSuperAgentFolder = async ({ folderId, userId, body }) =>
|
|
requestSuperAgentJson({
|
|
method: "PATCH",
|
|
path: superAgentServiceApiTable.tasksetDetail,
|
|
params: { folderId },
|
|
query: { userId },
|
|
body,
|
|
fallbackMessage: "更新文件夹失败",
|
|
});
|
|
|
|
export const patchSuperAgentSession = async ({ sessionId, userId, body }) =>
|
|
requestSuperAgentJson({
|
|
method: "PATCH",
|
|
path: superAgentServiceApiTable.conversationDetail,
|
|
params: { sessionId },
|
|
query: { userId },
|
|
body,
|
|
fallbackMessage: "更新会话失败",
|
|
});
|
|
|
|
export const cancelSuperAgentSession = async ({ sessionId, userId }) =>
|
|
requestSuperAgentJson({
|
|
method: "POST",
|
|
path: superAgentServiceApiTable.threadCancel,
|
|
params: { sessionId },
|
|
query: { userId },
|
|
fallbackMessage: "取消任务失败",
|
|
keepalive: true,
|
|
});
|
|
|
|
export const getSuperAgentSessionSectionsDirect = async ({
|
|
sessionId,
|
|
userId,
|
|
includeContent = 1,
|
|
}) => {
|
|
const path = buildSuperAgentPath(superAgentServiceApiTable.artifactChapters, {
|
|
sessionId,
|
|
});
|
|
const query = new URLSearchParams();
|
|
if (userId) query.set("userId", userId);
|
|
if (includeContent) query.set("include_content", String(includeContent));
|
|
query.set("_t", String(Date.now()));
|
|
const response = await fetch(
|
|
buildSuperAgentServiceUrl(`${path}${query.toString() ? `?${query.toString()}` : ""}`),
|
|
{
|
|
method: "GET",
|
|
headers: await buildLegacyCompatibleHeaders(),
|
|
cache: "no-store",
|
|
}
|
|
);
|
|
return parseJsonResponse(response, "获取正文章节失败");
|
|
};
|
|
|
|
export const getSuperAgentSessionSectionDirect = async ({
|
|
sessionId,
|
|
sectionId,
|
|
userId,
|
|
}) => {
|
|
const path = buildSuperAgentPath(superAgentServiceApiTable.artifactChapter, {
|
|
sessionId,
|
|
sectionId,
|
|
});
|
|
const query = new URLSearchParams();
|
|
if (userId) query.set("userId", userId);
|
|
query.set("_t", String(Date.now()));
|
|
const response = await fetch(
|
|
buildSuperAgentServiceUrl(`${path}${query.toString() ? `?${query.toString()}` : ""}`),
|
|
{
|
|
method: "GET",
|
|
headers: await buildLegacyCompatibleHeaders(),
|
|
cache: "no-store",
|
|
}
|
|
);
|
|
return parseJsonResponse(response, "获取章节内容失败");
|
|
};
|
|
|
|
const getExternalKnowledgeBaseList = basicAction.get("/aichat/knowledge", {
|
|
tip: false,
|
|
reducerName: "superAgent",
|
|
});
|
|
|
|
//[解析优化后的引用数据]
|
|
const parseOptimizedQuoteData = (content = "") => {
|
|
try {
|
|
//[去除Markdown代码块包裹]
|
|
const jsonText = content
|
|
.replace(/^```(?:json)?\s*/i, "")
|
|
.replace(/```\s*$/i, "")
|
|
.trim();
|
|
const data = JSON.parse(jsonText);
|
|
return data && typeof data === "object" ? data : null;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
//[优化引用内容]
|
|
const optimizeQuoteData = async (quoteData = {}) => {
|
|
const content = quoteData?.q || quoteData?.content || "";
|
|
if (!content) return quoteData;
|
|
|
|
try {
|
|
const res = await fetch("/_api/aichat/quote/optimize", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
quoteData,
|
|
}),
|
|
});
|
|
const data = await res.json();
|
|
const optimizedPayload =
|
|
data?.data && typeof data.data === "object" ? data.data : {};
|
|
const optimizedContent = (
|
|
optimizedPayload.q ||
|
|
optimizedPayload.content ||
|
|
data?.content ||
|
|
""
|
|
).trim();
|
|
if (!res.ok || !optimizedContent) {
|
|
return quoteData;
|
|
}
|
|
const optimizedData = parseOptimizedQuoteData(optimizedContent);
|
|
if (optimizedData) {
|
|
return {
|
|
...quoteData,
|
|
...optimizedPayload,
|
|
...optimizedData,
|
|
q: optimizedData.q || optimizedData.content || optimizedContent,
|
|
content: optimizedData.content || optimizedData.q || optimizedContent,
|
|
originalQ: quoteData.q,
|
|
originalContent: quoteData.content,
|
|
};
|
|
}
|
|
return {
|
|
...quoteData,
|
|
...optimizedPayload,
|
|
q: optimizedContent,
|
|
content: optimizedContent,
|
|
originalQ: quoteData.q,
|
|
originalContent: quoteData.content,
|
|
};
|
|
} catch (error) {
|
|
console.error("优化引用数据失败:", error);
|
|
return quoteData;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 记录用户查询
|
|
* @param {string} question 查询问题
|
|
* @param {string} userId 用户ID
|
|
*/
|
|
export const recordQuery = async ({
|
|
question,
|
|
userId,
|
|
clientId,
|
|
feature,
|
|
curIp,
|
|
}) => {
|
|
try {
|
|
await request
|
|
.post(danaApi.oaiQueryRecord)
|
|
.send({ question, userId, clientId, feature, curIp });
|
|
// DanaRequest.post(danaApi.oaiQueryRecord, {
|
|
// question,
|
|
// });
|
|
} catch (error) {
|
|
console.error("记录查询失败", error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 获取对话记录列表
|
|
* @param {string} chatKey 用户ID
|
|
* @param {string} appId 应用ID
|
|
* @returns {Promise<Array>} 对话记录列表
|
|
*/
|
|
export const getQueryItems = async (chatKey, appId) => {
|
|
try {
|
|
const res = await request.get(danaApi.oaiQueryItems).query({
|
|
chat_key: chatKey,
|
|
app_id: appId,
|
|
});
|
|
return res.body;
|
|
} catch (error) {
|
|
console.error("获取对话记录失败", error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 创建或更新对话记录
|
|
* @param {Object} params 参数对象
|
|
* @param {string} params.chat_id 对话ID
|
|
* @param {string} params.app_id 应用ID
|
|
* @param {string} params.title 对话标题
|
|
* @returns {Promise<Object>} 创建结果
|
|
*/
|
|
export const createOrUpdateChat = async (params) => {
|
|
try {
|
|
const res = await request.post(danaApi.oaiQueryItems).send(params);
|
|
return res.body;
|
|
} catch (error) {
|
|
console.error("创建/更新对话失败", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 删除对话
|
|
* @param {Object} params 参数对象
|
|
* @param {string} params.chatId 对话ID
|
|
*/
|
|
export const deleteChatItem = async (params) => {
|
|
return new Promise((resolve, reject) => {
|
|
request
|
|
.delete(danaApi.oaiDeleteItem)
|
|
.query(params)
|
|
.end((err, res) => {
|
|
if (err) {
|
|
console.error("删除对话失败", err);
|
|
reject(err);
|
|
} else {
|
|
resolve(res.body);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 初始化对话内容
|
|
* @param {Object} params 参数对象
|
|
* @param {string} params.appId 应用ID
|
|
* @param {string} params.chatId 对话ID
|
|
* @param {string} params.fastgptAppKey FastGPT应用密钥
|
|
* @returns {Promise<Array>} 对话内容
|
|
*/
|
|
export const initChatContent = async ({ appId, chatId, fastgptAppKey }) => {
|
|
try {
|
|
const url = `/_fgpt/api${fastgptApiTable.chatInit}`;
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
appId,
|
|
chatId,
|
|
offset: 0,
|
|
pageSize: 14,
|
|
loadCustomFeedbacks: true,
|
|
}),
|
|
mode: "cors",
|
|
});
|
|
|
|
const data = await res.json();
|
|
if (data.code == 200) {
|
|
return data?.data?.list || [];
|
|
} else {
|
|
console.error("获取对话内容失败:", data);
|
|
return [];
|
|
}
|
|
} catch (error) {
|
|
console.error("获取对话内容失败:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 初始化新对话
|
|
* @param {Object} params 参数对象
|
|
* @param {string} params.appId 应用ID
|
|
* @param {string} params.chatId 对话ID
|
|
* @param {string} params.fastgptAppKey FastGPT应用密钥
|
|
* @returns {Promise<boolean>} 是否成功
|
|
*/
|
|
export const initNewChat = async ({ appId, chatId, fastgptAppKey }) => {
|
|
try {
|
|
const url = `/_fgpt/api/core/chat/init?appId=${appId}&chatId=${chatId}`;
|
|
const res = await fetch(url, {
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
const data = await res.json();
|
|
return data?.code == 200;
|
|
} catch (error) {
|
|
console.error("初始化新对话失败:", error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 提交反馈
|
|
* @param {Object} params 反馈参数
|
|
* @param {string} appId 应用ID
|
|
* @param {string} chatId 对话ID
|
|
* @param {string} fastgptAppKey FastGPT应用密钥
|
|
* @returns {Promise<boolean>} 是否成功
|
|
*/
|
|
export const submitFeedback = async (params, appId, chatId, fastgptAppKey) => {
|
|
try {
|
|
const url = `/_fgpt${fastgptApiTable.feedback}`;
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
...params,
|
|
appId,
|
|
chatId,
|
|
}),
|
|
});
|
|
const data = await res.json();
|
|
return data?.code == 200;
|
|
} catch (error) {
|
|
console.error("提交反馈失败:", error);
|
|
return false;
|
|
}
|
|
};
|
|
/**
|
|
* 获取引用内容
|
|
*/
|
|
export const getQuote = async (params, fastgptAppKey) => {
|
|
try {
|
|
const url = `/_fgpt${fastgptApiTable.getQuote}`;
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(params),
|
|
});
|
|
const data = await res.json();
|
|
return data;
|
|
} catch (error) {
|
|
console.error("获取引用内容失败:", error);
|
|
return false;
|
|
}
|
|
};
|
|
/**
|
|
* 获取引用数据
|
|
*/
|
|
export const getQuoteData = async (params, fastgptAppKey, options = {}) => {
|
|
try {
|
|
const url = `/_fgpt${fastgptApiTable.getQuoteData}`;
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(params),
|
|
});
|
|
const data = await res.json();
|
|
|
|
if (data?.code === 200 && data?.data && options?.onOptimized) {
|
|
optimizeQuoteData(data.data).then((optimizedQuoteData) => {
|
|
options.onOptimized(optimizedQuoteData);
|
|
});
|
|
}
|
|
|
|
return data;
|
|
} catch (error) {
|
|
console.error("获取引用数据失败:", error);
|
|
return false;
|
|
}
|
|
};
|
|
/**
|
|
* 获取模型列表
|
|
* @param {string} fastgptToken FastGPT令牌
|
|
* @returns {Promise<Array>} 模型列表
|
|
*/
|
|
export const getModelList = async (fastgptToken) => {
|
|
try {
|
|
const res = await fetch(`/_fgpt/api/core/ai/model/list`, {
|
|
method: "GET",
|
|
headers: {
|
|
Token: fastgptToken,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
const data = await res.json();
|
|
return data.data || [];
|
|
} catch (error) {
|
|
console.error("获取模型列表失败:", error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 获取知识库列表
|
|
* @param {string} fastgptAppKey FastGPT应用密钥
|
|
* @param {string|null} parentId 父级ID
|
|
* @returns {Promise<Array>} 知识库列表
|
|
*/
|
|
export const getDatasetList = async (fastgptAppKey, parentId = null) => {
|
|
try {
|
|
const res = await fetch(`/_fgpt/api/core/dataset/list`, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${fastgptAppKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ parentId }),
|
|
});
|
|
const data = await res.json();
|
|
const result = [];
|
|
|
|
for (let d of data.data || []) {
|
|
if (d.name == "CK库表清单") {
|
|
continue;
|
|
}
|
|
if (d.type == "folder") {
|
|
d.children = await getDatasetList(fastgptAppKey, d._id);
|
|
}
|
|
result.push(d);
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
console.error("获取知识库列表失败:", error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
export { buildSuperAgentServiceUrl, buildSuperAgentPath } from "../utils/agentApi";
|
|
|
|
const parseJsonResponse = async (response, fallbackMessage) => {
|
|
const text = await response.text();
|
|
let data = {};
|
|
|
|
try {
|
|
data = text ? JSON.parse(text) : {};
|
|
} catch (error) {
|
|
superAgentError("api", "json_parse_failed", {
|
|
status: response.status,
|
|
fallbackMessage,
|
|
responseText: text,
|
|
error,
|
|
});
|
|
throw new Error(text || fallbackMessage);
|
|
}
|
|
|
|
if (!response.ok) {
|
|
superAgentError("api", "http_error", {
|
|
status: response.status,
|
|
fallbackMessage,
|
|
data,
|
|
});
|
|
throw new Error(data?.message || data?.error || fallbackMessage);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const buildLegacyCompatibleHeaders = async (extraHeaders = {}) => {
|
|
return buildSuperAgentHeaders(extraHeaders);
|
|
};
|
|
|
|
const requestSuperAgentStream = async ({
|
|
path,
|
|
userId,
|
|
body,
|
|
onEvent,
|
|
signal,
|
|
fallbackMessage,
|
|
traceId,
|
|
logScope,
|
|
sessionId,
|
|
}) => {
|
|
const response = await fetch(buildSuperAgentServiceUrl(path), {
|
|
method: "POST",
|
|
headers: await buildLegacyCompatibleHeaders({
|
|
"Content-Type": "application/json",
|
|
}),
|
|
body: JSON.stringify(body),
|
|
signal,
|
|
});
|
|
superAgentLog(logScope, "response.headers", {
|
|
traceId,
|
|
sessionId,
|
|
ok: response.ok,
|
|
status: response.status,
|
|
contentType: response.headers.get("content-type"),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
await parseJsonResponse(response, fallbackMessage);
|
|
return;
|
|
}
|
|
|
|
if (!response.body) {
|
|
throw new Error("当前浏览器不支持流式响应");
|
|
}
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder("utf-8");
|
|
let buffer = "";
|
|
let eventCount = 0;
|
|
let byteCount = 0;
|
|
|
|
const emitParsedEvent = (block) => {
|
|
const lines = block.split(/\r?\n/);
|
|
let eventName = "message";
|
|
const dataLines = [];
|
|
|
|
lines.forEach((line) => {
|
|
if (line.startsWith("event:")) {
|
|
eventName = line.slice(6).trim() || "message";
|
|
}
|
|
if (line.startsWith("data:")) {
|
|
dataLines.push(line.slice(5).trim());
|
|
}
|
|
});
|
|
|
|
const rawData = dataLines.join("\n");
|
|
let data = rawData;
|
|
try {
|
|
data = rawData ? JSON.parse(rawData) : {};
|
|
} catch (error) {
|
|
data = { content: rawData };
|
|
}
|
|
|
|
eventCount += 1;
|
|
superAgentLog(logScope, "sse.event", {
|
|
traceId,
|
|
sessionId,
|
|
eventCount,
|
|
event: eventName,
|
|
data: summarizeSuperAgentPayload(data),
|
|
});
|
|
onEvent({
|
|
event: eventName,
|
|
data,
|
|
});
|
|
};
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
byteCount += value?.byteLength || 0;
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const blocks = buffer.split(/\n\n/);
|
|
buffer = blocks.pop() || "";
|
|
|
|
blocks.forEach((block) => {
|
|
const trimmedBlock = block.trim();
|
|
if (!trimmedBlock) return;
|
|
emitParsedEvent(trimmedBlock);
|
|
});
|
|
}
|
|
|
|
const lastBlock = buffer.trim();
|
|
if (lastBlock) {
|
|
emitParsedEvent(lastBlock);
|
|
}
|
|
superAgentLog(logScope, "stream.done", {
|
|
traceId,
|
|
sessionId,
|
|
eventCount,
|
|
byteCount,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 功能:发送超级智能体对话并消费 SSE 事件
|
|
* 场景:普通文本、文件上传后的任务推进、checkpoint 后续指令都从这里进入
|
|
* 注意:后端事件名是业务事实来源,前端只做展示和交互承接
|
|
* 这是一个特殊的流式接口,不能使用basicAction,需要自定义实现
|
|
*/
|
|
export const sendSuperAgentChat = async ({
|
|
sessionId,
|
|
userId,
|
|
message,
|
|
displayContent,
|
|
attachmentIds = [],
|
|
onEvent,
|
|
signal,
|
|
}) => {
|
|
const traceId = createSuperAgentTraceId("chat");
|
|
superAgentLog("api.chat", "request.start", {
|
|
traceId,
|
|
sessionId,
|
|
userId,
|
|
message,
|
|
});
|
|
const chatPath = buildSuperAgentPath(superAgentServiceApiTable.threadEvents, {
|
|
sessionId,
|
|
});
|
|
await requestSuperAgentStream({
|
|
path: chatPath,
|
|
userId,
|
|
body: {
|
|
session_id: sessionId,
|
|
content: message,
|
|
display_content: displayContent || message,
|
|
...(attachmentIds.length ? { attachment_ids: attachmentIds } : {}),
|
|
},
|
|
onEvent,
|
|
signal,
|
|
fallbackMessage: "超级智能体对话失败",
|
|
traceId,
|
|
logScope: "api.chat",
|
|
sessionId,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 功能:上传标书相关文件
|
|
* 场景:用户在输入区或流程卡片上传招标文件、附件、补充材料
|
|
* 注意:上传完成后仍然由任务消息流接口继续驱动后续任务
|
|
* 这是一个特殊的文件上传接口,不能使用basicAction,需要自定义实现
|
|
*/
|
|
export const uploadSuperAgentFile = async ({ sessionId, userId, file, onProgress }) => {
|
|
const traceId = createSuperAgentTraceId("upload");
|
|
superAgentLog("api.upload", "request.start", {
|
|
traceId,
|
|
sessionId,
|
|
userId,
|
|
file,
|
|
});
|
|
const formData = new FormData();
|
|
formData.append("session_id", sessionId);
|
|
formData.append("file", file);
|
|
|
|
const uploadPath = buildSuperAgentPath(superAgentServiceApiTable.attachmentUploadIntents, {
|
|
conversationId: sessionId,
|
|
});
|
|
const agentHeaders = await buildLegacyCompatibleHeaders();
|
|
const result = await new Promise((resolve, reject) => {
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open("POST", buildSuperAgentServiceUrl(uploadPath));
|
|
|
|
Object.entries(agentHeaders).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null && value !== "") {
|
|
xhr.setRequestHeader(key, value);
|
|
}
|
|
});
|
|
|
|
xhr.upload.onprogress = (event) => {
|
|
const total = Number(event.total || file?.size || 0);
|
|
const loaded = Number(event.loaded || 0);
|
|
const percent = total > 0 ? Math.min(100, Math.round((loaded / total) * 100)) : 0;
|
|
if (typeof onProgress === "function") {
|
|
onProgress({ loaded, total, percent });
|
|
}
|
|
};
|
|
|
|
xhr.onerror = () => reject(new Error("上传文件失败,请检查网络连接"));
|
|
xhr.onabort = () => reject(new Error("文件上传已取消"));
|
|
xhr.onload = () => {
|
|
let data = {};
|
|
try {
|
|
data = xhr.responseText ? JSON.parse(xhr.responseText) : {};
|
|
} catch (error) {
|
|
superAgentError("api.upload", "json_parse_failed", {
|
|
traceId,
|
|
sessionId,
|
|
status: xhr.status,
|
|
responseText: xhr.responseText,
|
|
error,
|
|
});
|
|
reject(new Error(xhr.responseText || "上传文件失败"));
|
|
return;
|
|
}
|
|
|
|
if (xhr.status < 200 || xhr.status >= 300) {
|
|
superAgentError("api.upload", "http_error", {
|
|
traceId,
|
|
sessionId,
|
|
status: xhr.status,
|
|
data,
|
|
});
|
|
reject(new Error(data?.message || data?.error || "上传文件失败"));
|
|
return;
|
|
}
|
|
|
|
resolve(data);
|
|
};
|
|
|
|
xhr.send(formData);
|
|
});
|
|
superAgentLog("api.upload", "response.done", {
|
|
traceId,
|
|
sessionId,
|
|
result,
|
|
});
|
|
return result;
|
|
};
|
|
|
|
export const buildSuperAgentDownloadUrl = (filePath, sessionId = "") => {
|
|
const query = new URLSearchParams({ path: filePath || "" });
|
|
if (sessionId) {
|
|
query.set("session_id", sessionId);
|
|
}
|
|
return buildSuperAgentServiceUrl(`${superAgentServiceApiTable.attachments}?${query.toString()}`);
|
|
};
|
|
|
|
export const buildSuperAgentSessionDownloadUrl = (sessionId, userId = "", options = {}) => {
|
|
const path = buildSuperAgentPath(superAgentServiceApiTable.artifacts, {
|
|
sessionId,
|
|
});
|
|
const query = new URLSearchParams();
|
|
if (userId) {
|
|
query.set("userId", userId);
|
|
}
|
|
if (options.draft) {
|
|
query.set("draft", "1");
|
|
}
|
|
const queryString = query.toString();
|
|
return buildSuperAgentServiceUrl(`${path}${queryString ? `?${queryString}` : ""}`);
|
|
};
|
|
|
|
export const updateSuperAgentSessionSectionDirect = async ({
|
|
sessionId,
|
|
sectionId,
|
|
userId,
|
|
content,
|
|
status = "edited",
|
|
title = "",
|
|
order,
|
|
}) => {
|
|
const traceId = createSuperAgentTraceId("section_save");
|
|
const path = buildSuperAgentPath(superAgentServiceApiTable.artifactChapter, {
|
|
sessionId,
|
|
sectionId,
|
|
});
|
|
const query = new URLSearchParams();
|
|
if (userId) {
|
|
query.set("userId", userId);
|
|
}
|
|
superAgentLog("api.section", "update.start", {
|
|
traceId,
|
|
sessionId,
|
|
sectionId,
|
|
userId,
|
|
status,
|
|
title,
|
|
order,
|
|
content,
|
|
});
|
|
const response = await fetch(buildSuperAgentServiceUrl(`${path}${query.toString() ? `?${query.toString()}` : ""}`), {
|
|
method: "PUT",
|
|
headers: await buildLegacyCompatibleHeaders({
|
|
"Content-Type": "application/json",
|
|
}),
|
|
body: JSON.stringify({
|
|
content: content || "",
|
|
status,
|
|
title,
|
|
order,
|
|
}),
|
|
});
|
|
|
|
const result = await parseJsonResponse(response, "保存章节内容失败");
|
|
superAgentLog("api.section", "update.done", {
|
|
traceId,
|
|
sessionId,
|
|
sectionId,
|
|
result,
|
|
});
|
|
return result;
|
|
};
|
|
|
|
export default {
|
|
getExternalKnowledgeBaseList,
|
|
};
|
|
|