Syntax changes in front and back end dirs. These do not affect functionality and are purely intended to better capture and describe the app's functionality and purpose
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
|
||||
import { Router, type Request, type Response, type NextFunction } from 'express';
|
||||
import logger from '../logger.js';
|
||||
import * as notebookStore from '../stores/notebookStore.js';
|
||||
import type { TextChunk, Source, SourceGroup } from '../stores/notebookStore.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 {
|
||||
@@ -27,16 +27,16 @@ const GENERATORS: Record<DocumentType, GeneratorFn> = {
|
||||
const router = Router();
|
||||
|
||||
interface GenerateBody {
|
||||
notebookId?: string;
|
||||
sourceCorpusId?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
router.post('/generate', async (req: Request<unknown, unknown, GenerateBody>, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { notebookId, type } = req.body;
|
||||
const { sourceCorpusId, type } = req.body;
|
||||
|
||||
if (!notebookId || !type) {
|
||||
res.status(400).json({ error: 'notebookId and type are required' });
|
||||
if (!sourceCorpusId || !type) {
|
||||
res.status(400).json({ error: 'sourceCorpusId and type are required' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,31 +48,31 @@ router.post('/generate', async (req: Request<unknown, unknown, GenerateBody>, re
|
||||
return;
|
||||
}
|
||||
|
||||
const chunks: TextChunk[] = notebookStore.getChunksForNotebook(notebookId);
|
||||
const chunks: TextChunk[] = sourceCorpusStore.getChunksForSourceCorpus(sourceCorpusId);
|
||||
if (chunks.length === 0) {
|
||||
res.status(422).json({ error: 'No source material available in this notebook' });
|
||||
res.status(422).json({ error: 'No source material available in this source corpus' });
|
||||
return;
|
||||
}
|
||||
|
||||
const sources: Source[] = notebookStore.getSources(notebookId);
|
||||
const cached: CacheEntry | null = documentCacheStore.getCachedDocument(notebookId, type);
|
||||
if (cached && documentCacheStore.isFresh(notebookId, type, sources)) {
|
||||
logger.info({ notebookId, type }, 'serving cached document');
|
||||
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[] = notebookStore.buildSourceGroups(notebookId, chunks);
|
||||
const sourceGroups: SourceGroup[] = sourceCorpusStore.buildSourceGroups(sourceCorpusId, chunks);
|
||||
|
||||
logger.info(
|
||||
{ notebookId, type, sourceCount: sourceGroups.length, chunkCount: chunks.length },
|
||||
{ sourceCorpusId, type, sourceCount: sourceGroups.length, chunkCount: chunks.length },
|
||||
'document generation started'
|
||||
);
|
||||
|
||||
const document: StudyGuide | Faq | ExecutiveBrief = await generator(sourceGroups);
|
||||
|
||||
documentCacheStore.setCachedDocument(notebookId, type, document, sources);
|
||||
logger.info({ notebookId, type }, 'document generation complete');
|
||||
documentCacheStore.setCachedDocument(sourceCorpusId, type, document, sources);
|
||||
logger.info({ sourceCorpusId, type }, 'document generation complete');
|
||||
|
||||
res.json({ type, document });
|
||||
} catch (err) {
|
||||
|
||||
@@ -7,8 +7,8 @@ import type { ScoredChunk, RankedChunk, TextChunk } from '../services/retrievalS
|
||||
import { generate } from '../services/generationService.js';
|
||||
import type { GenerationResult } from '../services/generationService.js';
|
||||
import { computeGroundedness } from '../services/scoringService.js';
|
||||
import * as notebookStore from '../stores/notebookStore.js';
|
||||
import type { SourceGroup } from '../stores/notebookStore.js';
|
||||
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
|
||||
import type { SourceGroup } from '../stores/sourceCorpusStore.js';
|
||||
|
||||
const TOP_K_SEARCH = 20;
|
||||
const TOP_K_RERANK = 5;
|
||||
@@ -16,7 +16,7 @@ const TOP_K_RERANK = 5;
|
||||
const router = Router();
|
||||
|
||||
interface QueryBody {
|
||||
notebookId?: string;
|
||||
sourceCorpusId?: string;
|
||||
question?: string;
|
||||
}
|
||||
|
||||
@@ -36,20 +36,20 @@ interface QueryResponse {
|
||||
|
||||
router.post('/', async (req: Request<unknown, unknown, QueryBody>, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { notebookId, question } = req.body;
|
||||
if (!notebookId || !question) {
|
||||
res.status(400).json({ error: 'notebookId and question are required' });
|
||||
const { sourceCorpusId, question } = req.body;
|
||||
if (!sourceCorpusId || !question) {
|
||||
res.status(400).json({ error: 'sourceCorpusId and question are required' });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info({ notebookId, question }, 'query received');
|
||||
logger.info({ sourceCorpusId, question }, 'query received');
|
||||
|
||||
const [queryEmbedding]: number[][] = await embedTexts([question], 'query');
|
||||
|
||||
const searchResults: ScoredChunk[] = search(queryEmbedding, notebookId, TOP_K_SEARCH);
|
||||
const searchResults: ScoredChunk[] = search(queryEmbedding, sourceCorpusId, TOP_K_SEARCH);
|
||||
if (searchResults.length === 0) {
|
||||
res.json({
|
||||
answer: 'No sources found for this notebook. Upload some documents first.',
|
||||
answer: 'No sources found for this source corpus. Upload some documents first.',
|
||||
citations: [],
|
||||
groundednessScore: null,
|
||||
followUpQuestions: [],
|
||||
@@ -69,7 +69,7 @@ router.post('/', async (req: Request<unknown, unknown, QueryBody>, res: Response
|
||||
'retrieval complete'
|
||||
);
|
||||
|
||||
const sourceGroups: SourceGroup[] = notebookStore.buildSourceGroups(notebookId, topChunks);
|
||||
const sourceGroups: SourceGroup[] = sourceCorpusStore.buildSourceGroups(sourceCorpusId, topChunks);
|
||||
|
||||
const { answer, citedSourceIndices, followUpQuestions }: GenerationResult = await generate(question, sourceGroups);
|
||||
|
||||
|
||||
@@ -2,33 +2,33 @@
|
||||
|
||||
import { Router, type Request, type Response } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import * as notebookStore from '../stores/notebookStore.js';
|
||||
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
|
||||
import { deleteVectorsForChunks } from '../stores/vectorStore.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', (_req: Request, res: Response) => {
|
||||
res.json(notebookStore.getAllNotebooks());
|
||||
res.json(sourceCorpusStore.getAllSourceCorpora());
|
||||
});
|
||||
|
||||
interface CreateNotebookBody {
|
||||
interface CreateSourceCorpusBody {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
router.post('/', (req: Request<unknown, unknown, CreateNotebookBody>, res: Response) => {
|
||||
router.post('/', (req: Request<unknown, unknown, CreateSourceCorpusBody>, res: Response) => {
|
||||
const { name } = req.body;
|
||||
if (!name || !name.trim()) {
|
||||
res.status(400).json({ error: 'name is required' });
|
||||
return;
|
||||
}
|
||||
|
||||
const notebook = notebookStore.createNotebook({
|
||||
const sourceCorpus = sourceCorpusStore.createSourceCorpus({
|
||||
id: uuidv4(),
|
||||
name: name.trim(),
|
||||
createdAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
res.status(201).json(notebook);
|
||||
res.status(201).json(sourceCorpus);
|
||||
});
|
||||
|
||||
interface DeleteParams {
|
||||
@@ -36,10 +36,10 @@ interface DeleteParams {
|
||||
}
|
||||
|
||||
router.delete('/:id', (req: Request<DeleteParams>, res: Response) => {
|
||||
const chunks = notebookStore.getChunksForNotebook(req.params.id);
|
||||
const deleted = notebookStore.deleteNotebook(req.params.id);
|
||||
const chunks = sourceCorpusStore.getChunksForSourceCorpus(req.params.id);
|
||||
const deleted = sourceCorpusStore.deleteSourceCorpus(req.params.id);
|
||||
if (!deleted) {
|
||||
res.status(404).json({ error: 'notebook not found' });
|
||||
res.status(404).json({ error: 'source corpus not found' });
|
||||
return;
|
||||
}
|
||||
if (chunks.length) {
|
||||
@@ -4,18 +4,18 @@ import { Router, type Request, type Response, type NextFunction } from 'express'
|
||||
import multer from 'multer';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import logger from '../logger.js';
|
||||
import * as notebookStore from '../stores/notebookStore.js';
|
||||
import type { Source } from '../stores/notebookStore.js';
|
||||
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
|
||||
import type { Source } from '../stores/sourceCorpusStore.js';
|
||||
import { parseFile, chunkText, parseUrl, parseYoutubeUrl, isYoutubeUrl } from '../services/sourceService.js';
|
||||
import type { TextChunk } from '../services/sourceService.js';
|
||||
import { embedTexts, storeChunkEmbeddings } from '../services/retrievalService.js';
|
||||
import { triggerPreGeneration } from '../services/preGenerationService.js';
|
||||
import {
|
||||
requireNotebookId,
|
||||
requireNotebook,
|
||||
requireSourceCorpusId,
|
||||
requireSourceCorpus,
|
||||
requireFile,
|
||||
requireUrl,
|
||||
type NotebookRequest,
|
||||
type SourceCorpusRequest,
|
||||
} from '../middleware/validation.js';
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 50 * 1024 * 1024 } });
|
||||
@@ -26,8 +26,8 @@ function cleanFilename(name: string): string {
|
||||
return name.replace(TIMESTAMP_RE, '');
|
||||
}
|
||||
|
||||
router.get('/', requireNotebookId, (req: NotebookRequest, res: Response) => {
|
||||
res.json(notebookStore.getSources(req.notebookId!));
|
||||
router.get('/', requireSourceCorpusId, (req: SourceCorpusRequest, res: Response) => {
|
||||
res.json(sourceCorpusStore.getSources(req.sourceCorpusId!));
|
||||
});
|
||||
|
||||
function multerUpload(req: Request, res: Response, next: NextFunction): void {
|
||||
@@ -47,10 +47,10 @@ function multerUpload(req: Request, res: Response, next: NextFunction): void {
|
||||
router.post(
|
||||
'/',
|
||||
multerUpload,
|
||||
requireNotebookId,
|
||||
requireNotebook,
|
||||
requireSourceCorpusId,
|
||||
requireSourceCorpus,
|
||||
requireFile,
|
||||
async (req: NotebookRequest, res: Response, next: NextFunction) => {
|
||||
async (req: SourceCorpusRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const sourceId: string = uuidv4();
|
||||
const rawName: string = Buffer.from(req.file!.originalname, 'latin1').toString('utf-8');
|
||||
@@ -67,13 +67,13 @@ router.post(
|
||||
'parsed and chunked source'
|
||||
);
|
||||
|
||||
notebookStore.addChunksToNotebook(req.notebookId!, chunks);
|
||||
sourceCorpusStore.addChunksToSourceCorpus(req.sourceCorpusId!, chunks);
|
||||
|
||||
const texts: string[] = chunks.map((c) => c.text);
|
||||
const embeddings: number[][] = await embedTexts(texts, 'document');
|
||||
storeChunkEmbeddings(chunks, embeddings);
|
||||
|
||||
const source: Source | null = notebookStore.addSource(req.notebookId!, {
|
||||
const source: Source | null = sourceCorpusStore.addSource(req.sourceCorpusId!, {
|
||||
id: sourceId,
|
||||
name: displayName,
|
||||
mimetype: req.file!.mimetype,
|
||||
@@ -81,7 +81,7 @@ router.post(
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
triggerPreGeneration(req.notebookId!);
|
||||
triggerPreGeneration(req.sourceCorpusId!);
|
||||
|
||||
res.status(201).json(source);
|
||||
} catch (err) {
|
||||
@@ -96,10 +96,10 @@ interface UrlBody {
|
||||
|
||||
router.post(
|
||||
'/url',
|
||||
requireNotebookId,
|
||||
requireNotebook,
|
||||
requireSourceCorpusId,
|
||||
requireSourceCorpus,
|
||||
requireUrl,
|
||||
async (req: NotebookRequest & Request<unknown, unknown, UrlBody>, res: Response, next: NextFunction) => {
|
||||
async (req: SourceCorpusRequest & Request<unknown, unknown, UrlBody>, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { url } = req.body;
|
||||
const sourceId: string = uuidv4();
|
||||
@@ -116,13 +116,13 @@ router.post(
|
||||
return;
|
||||
}
|
||||
|
||||
notebookStore.addChunksToNotebook(req.notebookId!, chunks);
|
||||
sourceCorpusStore.addChunksToSourceCorpus(req.sourceCorpusId!, chunks);
|
||||
|
||||
const texts: string[] = chunks.map((c) => c.text);
|
||||
const embeddings: number[][] = await embedTexts(texts, 'document');
|
||||
storeChunkEmbeddings(chunks, embeddings);
|
||||
|
||||
const source: Source | null = notebookStore.addSource(req.notebookId!, {
|
||||
const source: Source | null = sourceCorpusStore.addSource(req.sourceCorpusId!, {
|
||||
id: sourceId,
|
||||
name: displayName,
|
||||
mimetype: isYT ? 'video/youtube' : 'text/html',
|
||||
@@ -130,7 +130,7 @@ router.post(
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
triggerPreGeneration(req.notebookId!);
|
||||
triggerPreGeneration(req.sourceCorpusId!);
|
||||
|
||||
res.status(201).json(source);
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user