106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
'use strict';
|
|
|
|
import logger from '../logger.js';
|
|
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
|
|
import * as documentCacheStore from '../stores/documentCacheStore.js';
|
|
import {
|
|
generateStudyGuide,
|
|
generateFaq,
|
|
generateExecutiveBrief,
|
|
type SourceGroup,
|
|
type StudyGuide,
|
|
type Faq,
|
|
type ExecutiveBrief,
|
|
} from './documentService.js';
|
|
|
|
const MIN_SOURCES = 2;
|
|
|
|
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 DEBOUNCE_MS = 5000;
|
|
|
|
const pendingReGen = new Set<string>();
|
|
const debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
|
|
export function triggerPreGeneration(sourceCorpusId: string): void {
|
|
const sourceCorpus = sourceCorpusStore.getSourceCorpus(sourceCorpusId);
|
|
if (!sourceCorpus) return;
|
|
|
|
const sources = sourceCorpusStore.getSources(sourceCorpusId);
|
|
if (sources.length < MIN_SOURCES) return;
|
|
|
|
if (documentCacheStore.isGenerating(sourceCorpusId)) {
|
|
pendingReGen.add(sourceCorpusId);
|
|
documentCacheStore.invalidate(sourceCorpusId);
|
|
logger.debug({ sourceCorpusId }, 'pre-generation in progress, queued re-generation');
|
|
return;
|
|
}
|
|
|
|
const existingTimer = debounceTimers.get(sourceCorpusId);
|
|
if (existingTimer !== undefined) {
|
|
clearTimeout(existingTimer);
|
|
}
|
|
debounceTimers.set(
|
|
sourceCorpusId,
|
|
setTimeout(() => {
|
|
debounceTimers.delete(sourceCorpusId);
|
|
runPreGeneration(sourceCorpusId);
|
|
}, DEBOUNCE_MS)
|
|
);
|
|
logger.debug({ sourceCorpusId, debounceMs: DEBOUNCE_MS }, 'pre-generation debounced');
|
|
}
|
|
|
|
function runPreGeneration(sourceCorpusId: string): void {
|
|
try {
|
|
const sources = sourceCorpusStore.getSources(sourceCorpusId);
|
|
const chunks = sourceCorpusStore.getChunksForSourceCorpus(sourceCorpusId);
|
|
const sourceGroups = sourceCorpusStore.buildSourceGroups(sourceCorpusId, chunks) as SourceGroup[];
|
|
|
|
documentCacheStore.invalidate(sourceCorpusId);
|
|
documentCacheStore.markGenerating(sourceCorpusId);
|
|
|
|
logger.info(
|
|
{ sourceCorpusId, sourceCount: sources.length, chunkCount: chunks.length },
|
|
'background pre-generation started'
|
|
);
|
|
|
|
const jobs = (Object.entries(GENERATORS) as Array<[DocumentType, GeneratorFn]>).map(
|
|
async ([type, generator]) => {
|
|
try {
|
|
const document = await generator(sourceGroups);
|
|
documentCacheStore.setCachedDocument(sourceCorpusId, type, document, sources);
|
|
logger.info({ sourceCorpusId, type }, 'background pre-generation complete for type');
|
|
} catch (err) {
|
|
const error = err as Error;
|
|
logger.error({ sourceCorpusId, type, err: error.message }, 'background pre-generation failed for type');
|
|
}
|
|
}
|
|
);
|
|
|
|
Promise.all(jobs)
|
|
.then(() => {
|
|
logger.info({ sourceCorpusId }, 'all background pre-generation complete');
|
|
})
|
|
.finally(() => {
|
|
documentCacheStore.clearGenerating(sourceCorpusId);
|
|
|
|
if (pendingReGen.has(sourceCorpusId)) {
|
|
pendingReGen.delete(sourceCorpusId);
|
|
logger.info({ sourceCorpusId }, 're-triggering pre-generation for updated sources');
|
|
runPreGeneration(sourceCorpusId);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
const error = err as Error;
|
|
logger.error({ sourceCorpusId, err: error.message }, 'pre-generation setup failed');
|
|
documentCacheStore.clearGenerating(sourceCorpusId);
|
|
}
|
|
}
|