/** * Phase I — Session Blackboard + READ Layer 路由 * * 路由前缀: /sessions/:sessionId/blackboard * * GET / — 获取完整 SessionBlackboard * POST /data-overview — 触发 get_data_overview 工具 * POST /variable-detail — 触发 get_variable_detail 工具 * PATCH /variables/:name — 用户确认/修改变量类型 */ import { FastifyInstance, FastifyRequest } from 'fastify'; import { logger } from '../../../common/logging/index.js'; import { sessionBlackboardService } from '../services/SessionBlackboardService.js'; import { executeGetDataOverview } from '../services/tools/GetDataOverviewTool.js'; import { executeGetVariableDetail } from '../services/tools/GetVariableDetailTool.js'; import type { ColumnType, PicoRole, VariableDictPatch } from '../types/session-blackboard.types.js'; export default async function blackboardRoutes(app: FastifyInstance) { // GET /sessions/:sessionId/blackboard — 获取完整黑板 app.get('/', async (req, reply) => { const { sessionId } = req.params as { sessionId: string }; const blackboard = await sessionBlackboardService.get(sessionId); if (!blackboard) { return reply.status(404).send({ error: 'Blackboard not found for this session' }); } const report = blackboard.dataOverview ? sessionBlackboardService.generateFiveSectionReport( blackboard.dataOverview, blackboard.variableDictionary, ) : null; return reply.send({ blackboard, report }); }); // POST /sessions/:sessionId/blackboard/data-overview — 执行 get_data_overview app.post('/data-overview', async (req, reply) => { const { sessionId } = req.params as { sessionId: string }; logger.info('[SSA:Route] Triggering data overview', { sessionId }); const result = await executeGetDataOverview(sessionId); if (!result.success) { return reply.status(400).send({ error: result.error }); } return reply.send({ success: true, report: result.report, }); }); // POST /sessions/:sessionId/blackboard/variable-detail — 执行 get_variable_detail app.post('/variable-detail', async (req, reply) => { const { sessionId } = req.params as { sessionId: string }; const { variableName, confirmedType, label } = req.body as { variableName: string; confirmedType?: ColumnType; label?: string; }; if (!variableName) { return reply.status(400).send({ error: 'variableName is required' }); } logger.info('[SSA:Route] Triggering variable detail', { sessionId, variableName }); const result = await executeGetVariableDetail(sessionId, variableName, confirmedType, label); if (!result.success) { return reply.status(400).send({ error: result.error }); } return reply.send(result); }); // PATCH /sessions/:sessionId/blackboard/variables/:name — 更新变量字典条目 app.patch('/variables/:name', async (req, reply) => { const { sessionId, name } = req.params as { sessionId: string; name: string }; const body = req.body as { confirmedType?: ColumnType; label?: string; picoRole?: PicoRole | null; }; logger.info('[SSA:Route] Updating variable dictionary entry', { sessionId, name, body }); const dictPatch: VariableDictPatch = {}; if (body.confirmedType !== undefined) dictPatch.confirmedType = body.confirmedType; if (body.label !== undefined) dictPatch.label = body.label; if (body.picoRole !== undefined) dictPatch.picoRole = body.picoRole; await sessionBlackboardService.updateVariable(sessionId, name, dictPatch); return reply.send({ success: true }); }); }