diff --git a/web/.vscode/launch.json b/web/.vscode/launch.json index 78e5276..cedef78 100644 --- a/web/.vscode/launch.json +++ b/web/.vscode/launch.json @@ -63,7 +63,8 @@ "TIANCHI_REPORT_API": "http://127.0.0.1:8000", "FS_TIANCHI_REPORT_VISIBLE_PHONES": "13800000000,13900000000", "FS_FINANCE_DEPARTMENT_ID": "152", - "FS_SUPER_AGENT_API": "http://127.0.0.1:8000", + "FS_SUPER_AGENT_BUSINESS_API": "http://127.0.0.1:8000", + "FS_SUPER_AGENT_FRAMEWORK_API": "http://127.0.0.1:8005", } }, { diff --git a/web/client/src/sections/superAgent/actions/agentResources.js b/web/client/src/sections/superAgent/actions/agentResources.js index 169f4bf..e05aeeb 100644 --- a/web/client/src/sections/superAgent/actions/agentResources.js +++ b/web/client/src/sections/superAgent/actions/agentResources.js @@ -189,6 +189,7 @@ export const sendThreadCommand = (threadId, body) => path: superAgentServiceApiTable.threadCommands, params: { threadId }, body, + service: "framework", fallbackMessage: "发送线程命令失败", }); @@ -198,5 +199,6 @@ export const cancelThreadRun = (threadId, runId, query = { wait: false, action: path: superAgentServiceApiTable.threadCancel, params: { threadId, runId }, query, + service: "framework", fallbackMessage: "取消运行失败", }); diff --git a/web/client/src/sections/superAgent/actions/api.js b/web/client/src/sections/superAgent/actions/api.js index a8e94a3..3c6765b 100644 --- a/web/client/src/sections/superAgent/actions/api.js +++ b/web/client/src/sections/superAgent/actions/api.js @@ -25,6 +25,7 @@ import { buildSuperAgentHeaders, buildSuperAgentPath, buildSuperAgentServiceUrl, + getSuperAgentFrameworkApiUrl, requestAgentJson, } from "../utils/agentApi"; import request from "superagent"; @@ -130,6 +131,7 @@ export const respondSuperAgentCheckpoint = (options = {}) => async () => { params: { sessionId }, query: options.query, body, + service: "framework", fallbackMessage: "提交确认结果失败", }); return { payload: { data } }; @@ -203,6 +205,7 @@ export const cancelSuperAgentSession = async ({ sessionId, userId }) => query: { userId }, fallbackMessage: "取消任务失败", keepalive: true, + service: "framework", }); export const getSuperAgentSessionSectionsDirect = async ({ @@ -654,15 +657,21 @@ const requestSuperAgentStream = async ({ traceId, logScope, sessionId, + service = "framework", }) => { - const response = await fetch(buildSuperAgentServiceUrl(path), { + const response = await fetch( + service === "framework" + ? `${getSuperAgentFrameworkApiUrl()}${path}` + : buildSuperAgentServiceUrl(path), + { method: "POST", headers: await buildLegacyCompatibleHeaders({ "Content-Type": "application/json", }), body: JSON.stringify(body), signal, - }); + } + ); superAgentLog(logScope, "response.headers", { traceId, sessionId, diff --git a/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js b/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js index c22eb20..e7749be 100644 --- a/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js +++ b/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js @@ -22,7 +22,7 @@ import { ProtocolSseTransportAdapter } from "@langchain/langgraph-sdk"; import { ROOT_PUMP_CHANNELS } from "@langchain/langgraph-sdk/stream"; import { getCompatibleToken } from "../../../utils/authCompat"; import { - getSuperAgentApiUrl, + getSuperAgentFrameworkApiUrl, withAgentAuthorization, } from "../utils/agentApi"; @@ -497,7 +497,7 @@ const useSuperAgentStream = ({ const eventTransport = useMemo( () => { const transport = new SingleSubscriberSseTransportAdapter({ - apiUrl: getSuperAgentApiUrl(), + apiUrl: getSuperAgentFrameworkApiUrl(), defaultHeaders, fetch: fetchUseStreamTransport, maxReconnectAttempts: 5, @@ -520,7 +520,7 @@ const useSuperAgentStream = ({ ); const stream = useStream({ assistantId: "freesun_agent", - apiUrl: getSuperAgentApiUrl(), + apiUrl: getSuperAgentFrameworkApiUrl(), threadId: activeSessionId || null, defaultHeaders, callerOptions, diff --git a/web/client/src/sections/superAgent/utils/agentApi.js b/web/client/src/sections/superAgent/utils/agentApi.js index c796138..4018642 100644 --- a/web/client/src/sections/superAgent/utils/agentApi.js +++ b/web/client/src/sections/superAgent/utils/agentApi.js @@ -12,7 +12,12 @@ */ "use strict"; -import { superAgentServiceProxyMatch } from "../../../utils/superAgentApiTable"; +import { + superAgentBusinessService, + superAgentFrameworkProxyMatch, + superAgentFrameworkService, + superAgentServiceProxyMatch, +} from "../../../utils/superAgentApiTable"; import { getCompatibleToken } from "../../../utils/authCompat"; import { SuperAgentApiError, @@ -38,14 +43,19 @@ export { parseAgentPage, } from "./agentApiParse"; -export const buildSuperAgentServiceUrl = (path = "") => { +export const buildSuperAgentServiceUrl = (path = "", service = superAgentBusinessService) => { const rawPath = String(path || ""); const servicePath = rawPath && !rawPath.startsWith("/") ? `/${rawPath}` : rawPath; const origin = typeof window !== "undefined" ? window.location.origin : ""; - return `${origin}${superAgentServiceProxyMatch}${servicePath}`; + const proxy = service === superAgentFrameworkService + ? superAgentFrameworkProxyMatch + : superAgentServiceProxyMatch; + return `${origin}${proxy}${servicePath}`; }; export const getSuperAgentApiUrl = () => buildSuperAgentServiceUrl(""); +export const getSuperAgentFrameworkApiUrl = () => + buildSuperAgentServiceUrl("", superAgentFrameworkService); export const buildSuperAgentPath = (path, params = {}) => { let nextPath = String(path || ""); @@ -157,6 +167,7 @@ export const requestAgentJson = async ({ keepalive = false, idempotencyKey, retryOnUnauthorized = true, + service = superAgentBusinessService, } = {}) => { const resolvedPath = buildSuperAgentPath(path, params); const search = new URLSearchParams(); @@ -176,7 +187,10 @@ export const requestAgentJson = async ({ }); const response = await fetch( - buildSuperAgentServiceUrl(`${resolvedPath}${queryString ? `?${queryString}` : ""}`), + buildSuperAgentServiceUrl( + `${resolvedPath}${queryString ? `?${queryString}` : ""}`, + service + ), { method: methodName, headers, @@ -198,6 +212,7 @@ export const requestAgentJson = async ({ keepalive, idempotencyKey: key, retryOnUnauthorized: false, + service, }); } diff --git a/web/client/src/utils/superAgentApiTable.js b/web/client/src/utils/superAgentApiTable.js index 37d4a46..f6c7190 100644 --- a/web/client/src/utils/superAgentApiTable.js +++ b/web/client/src/utils/superAgentApiTable.js @@ -1,7 +1,12 @@ "use strict"; export const superAgentApiBaseEnvKey = "FS_SUPER_AGENT_API"; +export const superAgentBusinessApiEnvKey = "FS_SUPER_AGENT_BUSINESS_API"; +export const superAgentFrameworkApiEnvKey = "FS_SUPER_AGENT_FRAMEWORK_API"; export const superAgentServiceProxyMatch = "/_super-agent"; +export const superAgentFrameworkProxyMatch = "/_super-agent-framework"; +export const superAgentBusinessService = "business"; +export const superAgentFrameworkService = "framework"; export const superAgentServiceApiTable = { tasksets: "/tasksets", diff --git a/web/config.cjs b/web/config.cjs index 707937d..69f2452 100644 --- a/web/config.cjs +++ b/web/config.cjs @@ -5,9 +5,17 @@ if (!process.env.FS_QINIU_DOMAIN) process.env.FS_QINIU_DOMAIN = "http://test.res const hmrHost = process.env.HMR_HOST || "127.0.0.1"; const hmrPort = Number(process.env.HMR_PORT || 24678); const hmrClientPort = Number(process.env.HMR_CLIENT_PORT || hmrPort); -const superAgentTarget = (process.env.FS_SUPER_AGENT_API || "http://127.0.0.1:8000") +const normalizeSuperAgentTarget = (target, fallback) => String(target || fallback) .replace(/\/api\/v1\/?$/i, "") .replace(/\/+$/, ""); +const superAgentBusinessTarget = normalizeSuperAgentTarget( + process.env.FS_SUPER_AGENT_BUSINESS_API || process.env.FS_SUPER_AGENT_API, + "http://127.0.0.1:8000" +); +const superAgentFrameworkTarget = normalizeSuperAgentTarget( + process.env.FS_SUPER_AGENT_FRAMEWORK_API, + "http://127.0.0.1:8005" +); module.exports = { env: process.env.NODE_ENV || "development", // 运行环境 development | production @@ -23,11 +31,19 @@ module.exports = { { // SuperAgent 前端接口转发到 Agent 服务,避免浏览器跨域。 path: "/_super-agent", - target: superAgentTarget, + target: superAgentBusinessTarget, changeOrigin: true, rewrite: (path) => path.replace(/^\/_super-agent/, ""), logs: process.env.NODE_ENV == "development", }, + { + // SuperAgent 框架线程/运行接口转发到 8005。 + path: "/_super-agent-framework", + target: superAgentFrameworkTarget, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/_super-agent-framework/, ""), + logs: process.env.NODE_ENV == "development", + }, { // 保留旧版 SuperAgent API 代理供兼容调用。 path: "/_api/super-agent", diff --git a/web/tests/agentApiAdapter.test.mjs b/web/tests/agentApiAdapter.test.mjs index eeb2b55..c76a298 100644 --- a/web/tests/agentApiAdapter.test.mjs +++ b/web/tests/agentApiAdapter.test.mjs @@ -14,13 +14,19 @@ const { parseAgentPage, shouldSendIdempotencyKey, } = await import("../client/src/sections/superAgent/utils/agentApiParse.js"); -const { superAgentServiceApiTable } = await import("../client/src/utils/superAgentApiTable.js"); +const { + superAgentFrameworkProxyMatch, + superAgentServiceProxyMatch, + superAgentServiceApiTable, +} = await import("../client/src/utils/superAgentApiTable.js"); const { isUploadIntentExpired, normalizeAttachmentUploadIntent, } = await import("../client/src/sections/superAgent/utils/attachmentUpload.js"); test("new api table uses live agent paths", () => { + assert.equal(superAgentServiceProxyMatch, "/_super-agent"); + assert.equal(superAgentFrameworkProxyMatch, "/_super-agent-framework"); assert.equal(superAgentServiceApiTable.tasksets, "/tasksets"); assert.equal(superAgentServiceApiTable.conversations, "/conversations"); assert.equal( @@ -82,6 +88,7 @@ test("adapter source exchanges login token for agent jwt and omits identity head const protocol = read("..", "api", "app", "controllers", "superAgentProtocol.js"); const actions = read("client", "src", "sections", "superAgent", "actions", "api.js"); const resources = read("client", "src", "sections", "superAgent", "actions", "agentResources.js"); + const config = read("config.cjs"); const chatHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentChat.js"); const uploadUtils = read("client", "src", "sections", "superAgent", "utils", "attachmentUpload.js"); assert.doesNotMatch(table, /\/api\/v1\/tasks/); @@ -98,6 +105,13 @@ test("adapter source exchanges login token for agent jwt and omits identity head assert.match(protocol, /\/threads\/\$\{encodeURIComponent/); assert.match(actions, /requestAgentJson/); assert.match(resources, /createAttachmentUploadIntent/); + assert.match(resources, /service: "framework"/); + assert.match(config, /FS_SUPER_AGENT_BUSINESS_API/); + assert.match(config, /FS_SUPER_AGENT_FRAMEWORK_API/); + assert.match(config, /_super-agent-framework/); + assert.match(agentApi, /buildSuperAgentServiceUrl = \(path = "", service = superAgentBusinessService\)/); + assert.match(agentApi, /superAgentFrameworkProxyMatch/); + assert.match(agentApi, /getSuperAgentFrameworkApiUrl/); assert.match(resources, /completeAttachmentUpload/); assert.match(resources, /listAttachments/); assert.match(chatHook, /createAttachmentUploadIntent\(conversationId/); diff --git a/web/tests/superAgentFlowContract.test.mjs b/web/tests/superAgentFlowContract.test.mjs index 57a29dd..6e647db 100644 --- a/web/tests/superAgentFlowContract.test.mjs +++ b/web/tests/superAgentFlowContract.test.mjs @@ -18,7 +18,8 @@ const apiRoutes = read("..", "api", "app", "routes", "superAgent.js"); assert.equal(packageJson.dependencies["@langchain/react"], "1.0.29"); assert.match(streamHook, /useStream\(\{/); -assert.match(streamHook, /apiUrl:\s*getSuperAgentApiUrl\(\)/); +assert.match(streamHook, /apiUrl:\s*getSuperAgentFrameworkApiUrl\(\)/); +assert.match(streamHook, /getSuperAgentFrameworkApiUrl/); assert.doesNotMatch(streamHook, /\/api\/v1/); assert.match(streamHook, /withAgentAuthorization/); assert.doesNotMatch(streamHook, /X-User-Id/);