'use strict'; import { Router, type Request, type Response, type NextFunction } from 'express'; import logger from '../logger.js'; import * as sourceCorpusStore from '../stores/sourceCorpusStore.js'; import type { TextChunk, Source, SourceGroup } from '../stores/sourceCorpusStore.js'; import * as documentCacheStore from '../stores/documentCacheStore.js'; import type { CacheEntry } from '../stores/documentCacheStore.js'; import { generateStudyGuide, generateFaq, generateExecutiveBrief, type StudyGuide, type Faq, type ExecutiveBrief, } from '../services/documentService.js'; type DocumentType = 'study-guide' | 'faq' | 'executive-brief'; type GeneratorFn = (sourceGroups: SourceGroup[]) => Promise; const GENERATORS: Record = { 'study-guide': generateStudyGuide, 'faq': generateFaq, 'executive-brief': generateExecutiveBrief, }; const router = Router(); interface GenerateBody { sourceCorpusId?: string; type?: string; } router.post('/generate', async (req: Request, res: Response, next: NextFunction) => { try { const { sourceCorpusId, type } = req.body; if (!sourceCorpusId || !type) { res.status(400).json({ error: 'sourceCorpusId and type are required' }); return; } const generator = GENERATORS[type as DocumentType]; if (!generator) { res.status(400).json({ error: `Invalid type. Must be one of: ${Object.keys(GENERATORS).join(', ')}`, }); return; } const chunks: TextChunk[] = sourceCorpusStore.getChunksForSourceCorpus(sourceCorpusId); if (chunks.length === 0) { res.status(422).json({ error: 'No source material available in this source corpus' }); return; } const sources: Source[] = sourceCorpusStore.getSources(sourceCorpusId); const cached: CacheEntry | null = documentCacheStore.getCachedDocument(sourceCorpusId, type); if (cached && documentCacheStore.isFresh(sourceCorpusId, type, sources)) { logger.info({ sourceCorpusId, type }, 'serving cached document'); res.json({ type, document: cached.document }); return; } const sourceGroups: SourceGroup[] = sourceCorpusStore.buildSourceGroups(sourceCorpusId, chunks); logger.info( { sourceCorpusId, type, sourceCount: sourceGroups.length, chunkCount: chunks.length }, 'document generation started' ); const document: StudyGuide | Faq | ExecutiveBrief = await generator(sourceGroups); documentCacheStore.setCachedDocument(sourceCorpusId, type, document, sources); logger.info({ sourceCorpusId, type }, 'document generation complete'); res.json({ type, document }); } catch (err) { next(err); } }); export default router;