Files
memind/server/portal-agent-runtime-routes.mjs
T
2026-07-24 19:18:14 +08:00

201 lines
6.0 KiB
JavaScript

import {
createAgentRunEventsHandler,
createGetAgentRunHandler,
createPostAgentRunsHandler,
} from '../agent-run-routes.mjs';
import { executeDeepSearchLlmGateway } from '../deep-search-llm-gateway.mjs';
function assertRouter(api) {
if (
!api ||
typeof api.get !== 'function' ||
typeof api.post !== 'function'
) {
throw new Error(
'attachPortalAgentRuntimeRoutes requires an Express-compatible router',
);
}
}
function runHandlerChain(chain, req, res, next) {
let index = 0;
const run = (error) => {
if (error) return next(error);
const layer = chain[index++];
if (!layer) return;
layer(req, res, run);
};
run();
}
export function attachPortalAgentRuntimeRoutes(
api,
{
waitForUserAuthReady = async () => {},
getUserAuth = () => null,
getTkmindProxy = () => null,
getLlmProviderService = () => null,
getAgentRunGateway = () => null,
getSessionAccess = () => null,
getMindSpaceAssetAgent = () => null,
getCodeRunPolicyService = () => null,
getUserToken = () => null,
getDeepSearchInternalSecret = () => null,
bearerToken = () => null,
ownsAgentSession = async () => false,
executeDeepSearchLlmGatewayFn =
executeDeepSearchLlmGateway,
createPostAgentRunsHandlerFn = createPostAgentRunsHandler,
createGetAgentRunHandlerFn = createGetAgentRunHandler,
createAgentRunEventsHandlerFn = createAgentRunEventsHandler,
logger = console,
} = {},
) {
assertRouter(api);
api.post('/internal/deep-search/llm', async (req, res) => {
try {
const result = await executeDeepSearchLlmGatewayFn({
llmProviderService: getLlmProviderService(),
expectedSecret: getDeepSearchInternalSecret(),
providedSecret:
bearerToken(req) ?? req.get('x-secret-key'),
input: req.body ?? {},
});
return res.status(result.status).json(result.body);
} catch (error) {
logger.warn('[deep-search] LLM gateway failed:', error);
return res.status(502).json({
ok: false,
message:
error instanceof Error
? error.message
: 'Deep Search LLM gateway failed',
});
}
});
api.post('/llm/apply-local-fallback', async (req, res) => {
// Client calls after creditsExhausted or relay 500 (see useTKMindChat).
await waitForUserAuthReady();
const userAuth = getUserAuth();
const llmProviderService = getLlmProviderService();
const tkmindProxy = getTkmindProxy();
if (!userAuth || !llmProviderService || !tkmindProxy) {
return res.status(503).json({ message: '未启用 LLM 配置' });
}
const me = await userAuth.getMe(getUserToken(req));
if (!me) return res.status(401).json({ message: '未登录' });
const sessionId = String(req.body?.session_id ?? '').trim();
if (!sessionId) return res.status(400).json({ message: '缺少 session_id' });
const owns = await ownsAgentSession(me.id, sessionId);
if (!owns) return res.status(403).json({ message: '无权访问该会话' });
try {
const result = await tkmindProxy.applyLocalFallbackForSession(sessionId);
if (!result.ok) return res.status(503).json(result);
return res.json(result);
} catch (error) {
return res.status(500).json({
message: error instanceof Error ? error.message : '切换本地 LLM 失败',
});
}
});
api.post('/agent/start', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
if (!userAuth || !tkmindProxy) return next();
return runHandlerChain(
tkmindProxy.handlers['POST /agent/start'],
req,
res,
next,
);
});
api.post('/agent/runs', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
const agentRunGateway = getAgentRunGateway();
if (!userAuth || !tkmindProxy || !agentRunGateway) return next();
return runHandlerChain(
[
tkmindProxy.requireUser,
tkmindProxy.ensureChatAllowed,
createPostAgentRunsHandlerFn({
userAuth,
sessionAccess: getSessionAccess(),
agentRunGateway,
mindSpaceAssetAgent: getMindSpaceAssetAgent(),
codeRunPolicyService: getCodeRunPolicyService(),
}),
],
req,
res,
next,
);
});
api.get('/agent/runs/:runId', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
const agentRunGateway = getAgentRunGateway();
if (!userAuth || !tkmindProxy || !agentRunGateway) return next();
return runHandlerChain(
[
tkmindProxy.requireUser,
createGetAgentRunHandlerFn({ agentRunGateway }),
],
req,
res,
next,
);
});
api.get('/agent/runs/:runId/events', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
const agentRunGateway = getAgentRunGateway();
if (!userAuth || !tkmindProxy || !agentRunGateway) return next();
return runHandlerChain(
[
tkmindProxy.requireUser,
createAgentRunEventsHandlerFn({ agentRunGateway }),
],
req,
res,
next,
);
});
api.post('/agent/resume', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
if (!userAuth || !tkmindProxy) return next();
return runHandlerChain(
tkmindProxy.handlers['POST /agent/resume'],
req,
res,
next,
);
});
api.get('/sessions', async (req, res, next) => {
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
if (!userAuth || !tkmindProxy) return next();
return runHandlerChain(
tkmindProxy.handlers['GET /sessions'],
req,
res,
next,
);
});
}