Files
OnceLove-New/OnceLove/oncelove-graphrag/api/src/controllers/graphrag.controller.js
KOSHM-Pig 1f087599c5 feat(graphrag): 新增多轮检索流式问答与用户管理界面
- 新增多轮 GraphRAG 检索功能,支持流式进度输出(SSE)
- 新增用户管理界面,可查看所有用户图谱统计并快速导航
- 新增多 Agent 任务拆解与执行服务,支持复杂任务协作处理
- 改进 embedding 和 rerank 服务的容错机制,支持备用模型和端点
- 更新前端样式遵循 Acmetone 设计规范,优化视觉一致性
- 新增流式分析接口,支持并行处理和专家评审选项
2026-03-24 12:04:28 +08:00

124 lines
5.0 KiB
JavaScript

/**
* 统一服务层异常处理并输出标准响应结构。
*/
const sendServiceResult = async (reply, action) => {
try {
const data = await action();
return reply.send(data);
} catch (error) {
const statusCode = Number(error?.statusCode) || 500;
return reply.code(statusCode).send({
ok: false,
error: error?.message ?? "internal error"
});
}
};
/**
* GraphRAG 控制器:负责请求转发与响应封装。
*/
export const createGraphRagController = (service, multiAgentService) => ({
health: async (_request, reply) => reply.send({ ok: true }),
ready: async (_request, reply) => sendServiceResult(reply, () => service.ready()),
bootstrap: async (_request, reply) => sendServiceResult(reply, () => service.bootstrap()),
listUsers: async (request, reply) => sendServiceResult(reply, () => service.listUsers(request.query.limit || 200)),
getGraphStats: async (request, reply) => sendServiceResult(reply, () => service.getGraphStats(request.query.userId || 'default')),
ingest: async (request, reply) => sendServiceResult(reply, () => service.ingest(request.body)),
queryTimeline: async (request, reply) =>
sendServiceResult(reply, () => service.queryTimeline(request.body)),
queryGraphRag: async (request, reply) =>
sendServiceResult(reply, () => service.queryGraphRag(request.body)),
queryGraphRagMultiRound: async (request, reply) =>
sendServiceResult(reply, () => service.queryGraphRagMultiRound(request.body)),
queryGraphRagMultiRoundStream: async (request, reply) => {
reply.hijack();
const raw = reply.raw;
const requestOrigin = request.headers.origin;
raw.setHeader('Access-Control-Allow-Origin', requestOrigin || '*');
raw.setHeader('Vary', 'Origin');
raw.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
raw.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
raw.setHeader('Cache-Control', 'no-cache, no-transform');
raw.setHeader('Connection', 'keep-alive');
raw.setHeader('X-Accel-Buffering', 'no');
raw.flushHeaders?.();
const push = (event, data) => {
raw.write(`event: ${event}\n`);
raw.write(`data: ${JSON.stringify(data)}\n\n`);
};
try {
const result = await service.queryGraphRagMultiRound(request.body, {
onProgress: (event) => push('progress', event)
});
push('done', result);
} catch (error) {
push('error', {
ok: false,
statusCode: Number(error?.statusCode) || 500,
message: error?.message || 'internal error'
});
} finally {
raw.end();
}
},
analyzeAndIngest: async (request, reply) =>
sendServiceResult(reply, () => service.incrementalUpdate(request.body.text, request.body.userId || 'default')),
reindexUserVectors: async (request, reply) =>
sendServiceResult(reply, () => service.reindexUserVectors({
userId: request.body.userId || 'default',
limit: request.body.limit
})),
analyzeAndIngestStream: async (request, reply) => {
reply.hijack();
const raw = reply.raw;
const requestOrigin = request.headers.origin;
raw.setHeader('Access-Control-Allow-Origin', requestOrigin || '*');
raw.setHeader('Vary', 'Origin');
raw.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
raw.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
raw.setHeader('Cache-Control', 'no-cache, no-transform');
raw.setHeader('Connection', 'keep-alive');
raw.setHeader('X-Accel-Buffering', 'no');
raw.flushHeaders?.();
const push = (event, data) => {
raw.write(`event: ${event}\n`);
raw.write(`data: ${JSON.stringify(data)}\n\n`);
};
try {
push('progress', { stage: 'start' });
const result = await service.incrementalUpdate(
request.body.text,
request.body.userId || 'default',
{
parallelism: request.body.parallelism,
expertReview: request.body.expertReview,
onProgress: (event) => push('progress', event)
}
);
push('done', result);
} catch (error) {
push('error', {
ok: false,
statusCode: Number(error?.statusCode) || 500,
message: error?.message || 'internal error'
});
} finally {
raw.end();
}
},
queryHistory: async (request, reply) =>
sendServiceResult(reply, () => service.queryRelationshipHistory(
request.body.userId || 'default',
request.body.queryType || 'all',
request.body.limit || 20
)),
getAdvice: async (request, reply) =>
sendServiceResult(reply, () => service.getRelationshipAdvice(request.body.userId || 'default')),
decomposeMultiAgentTask: async (request, reply) =>
sendServiceResult(reply, () => multiAgentService.decomposeTask(request.body)),
executeMultiAgentTask: async (request, reply) =>
sendServiceResult(reply, () => multiAgentService.executeTaskWorkflow(request.body))
});