Browse Source

feat: 适配agent接口分为框架、业务两个端口

master
Senor-Liu 1 month ago
parent
commit
046f745411
  1. 3
      web/.vscode/launch.json
  2. 2
      web/client/src/sections/superAgent/actions/agentResources.js
  3. 13
      web/client/src/sections/superAgent/actions/api.js
  4. 6
      web/client/src/sections/superAgent/hooks/useSuperAgentStream.js
  5. 23
      web/client/src/sections/superAgent/utils/agentApi.js
  6. 5
      web/client/src/utils/superAgentApiTable.js
  7. 20
      web/config.cjs
  8. 16
      web/tests/agentApiAdapter.test.mjs
  9. 3
      web/tests/superAgentFlowContract.test.mjs

3
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",
}
},
{

2
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: "取消运行失败",
});

13
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,

6
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,

23
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,
});
}

5
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",

20
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",

16
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/);

3
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/);

Loading…
Cancel
Save