import { NextResponse } from 'next/server'; import { getAdminAuth, getAdminDb } from '@/lib/firebase/admin'; import { createKnowledgeItem } from '@/lib/server/knowledge'; import { writeKnowledgeChunksForItem } from '@/lib/server/vector-memory'; import type { KnowledgeSourceMeta } from '@/lib/types/knowledge'; export const maxDuration = 60; interface ChunkInsightRequest { content: string; title?: string; importance?: 'primary' | 'supporting' | 'irrelevant'; tags?: string[]; sourceKnowledgeItemId?: string; metadata?: Record; } export async function POST( request: Request, context: { params: Promise<{ projectId: string }> | { projectId: string } } ) { try { // Verify auth const authHeader = request.headers.get('Authorization'); if (!authHeader?.startsWith('Bearer ')) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const idToken = authHeader.split('Bearer ')[1]; const adminAuth = getAdminAuth(); let userId: string; try { const decodedToken = await adminAuth.verifyIdToken(idToken); userId = decodedToken.uid; } catch (error) { return NextResponse.json({ error: 'Invalid token' }, { status: 401 }); } // Handle async params in Next.js 16 const params = 'then' in context.params ? await context.params : context.params; const projectId = params.projectId; if (!projectId) { return NextResponse.json({ error: 'Missing projectId' }, { status: 400 }); } const body = await request.json() as ChunkInsightRequest; if (!body.content || body.content.trim().length === 0) { return NextResponse.json({ error: 'Content is required' }, { status: 400 }); } const adminDb = getAdminDb(); const projectSnap = await adminDb.collection('projects').doc(projectId).get(); if (!projectSnap.exists) { return NextResponse.json({ error: 'Project not found' }, { status: 404 }); } console.log(`[chunk-insight] Creating confirmed insight for project ${projectId}`); // Create source metadata const sourceMeta: KnowledgeSourceMeta = { origin: 'vibn', createdAtOriginal: new Date().toISOString(), importance: body.importance || 'primary', tags: [ 'extracted_insight', 'user_confirmed', 'extracted_by:' + userId, ...(body.sourceKnowledgeItemId ? [`source:${body.sourceKnowledgeItemId}`] : []), ...(body.tags || []) ], }; // Store the confirmed insight as a knowledge_item const knowledgeItem = await createKnowledgeItem({ projectId, sourceType: 'other', title: body.title || 'Extracted Insight', content: body.content, sourceMeta, }); console.log(`[chunk-insight] Created knowledge_item ${knowledgeItem.id}`); // Chunk and embed in AlloyDB (synchronous for this endpoint) try { await writeKnowledgeChunksForItem({ id: knowledgeItem.id, projectId: knowledgeItem.projectId, content: knowledgeItem.content, sourceMeta: knowledgeItem.sourceMeta, }); console.log(`[chunk-insight] Successfully chunked and embedded insight`); } catch (chunkError) { console.error(`[chunk-insight] Failed to chunk item ${knowledgeItem.id}:`, chunkError); // Don't fail the request, item is still saved in Firestore } return NextResponse.json({ success: true, knowledgeItemId: knowledgeItem.id, message: 'Insight chunked and stored successfully', }); } catch (error) { console.error('[chunk-insight] Error:', error); return NextResponse.json( { error: 'Failed to store insight', details: error instanceof Error ? error.message : String(error), }, { status: 500 }, ); } }