/** * 统一服务层异常处理并输出标准响应结构。 */ 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)) });