84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
'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<StudyGuide | Faq | ExecutiveBrief>;
|
|
|
|
const GENERATORS: Record<DocumentType, GeneratorFn> = {
|
|
'study-guide': generateStudyGuide,
|
|
'faq': generateFaq,
|
|
'executive-brief': generateExecutiveBrief,
|
|
};
|
|
|
|
const router = Router();
|
|
|
|
interface GenerateBody {
|
|
sourceCorpusId?: string;
|
|
type?: string;
|
|
}
|
|
|
|
router.post('/generate', async (req: Request<unknown, unknown, GenerateBody>, 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;
|