VIBN Frontend for Coolify deployment
This commit is contained in:
136
app/api/projects/[projectId]/knowledge/import-document/route.ts
Normal file
136
app/api/projects/[projectId]/knowledge/import-document/route.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getAdminDb } from '@/lib/firebase/admin';
|
||||
import { createKnowledgeItem } from '@/lib/server/knowledge';
|
||||
import type { KnowledgeSourceMeta } from '@/lib/types/knowledge';
|
||||
import { chunkDocument } from '@/lib/utils/document-chunker';
|
||||
|
||||
interface ImportDocumentRequest {
|
||||
filename?: string;
|
||||
content?: string;
|
||||
mimeType?: string;
|
||||
}
|
||||
|
||||
export const maxDuration = 30;
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ projectId: string }> },
|
||||
) {
|
||||
try {
|
||||
const { projectId } = await params;
|
||||
|
||||
if (!projectId) {
|
||||
return NextResponse.json({ error: 'Missing projectId' }, { status: 400 });
|
||||
}
|
||||
|
||||
const body = (await request.json()) as ImportDocumentRequest;
|
||||
const content = body.content?.trim();
|
||||
const filename = body.filename?.trim();
|
||||
|
||||
if (!content || !filename) {
|
||||
return NextResponse.json({ error: 'filename and content are 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(`[import-document] Processing ${filename}, length=${content.length}`);
|
||||
|
||||
// Chunk the document
|
||||
const chunks = chunkDocument(content, {
|
||||
maxChunkSize: 2000,
|
||||
chunkOverlap: 200,
|
||||
preserveParagraphs: true,
|
||||
preserveCodeBlocks: true,
|
||||
});
|
||||
|
||||
console.log(`[import-document] Created ${chunks.length} chunks for ${filename}`);
|
||||
|
||||
// Store each chunk as a separate knowledge_item
|
||||
const knowledgeItemIds: string[] = [];
|
||||
|
||||
for (const chunk of chunks) {
|
||||
const sourceMeta: KnowledgeSourceMeta = {
|
||||
origin: 'other',
|
||||
url: null,
|
||||
filename,
|
||||
createdAtOriginal: new Date().toISOString(),
|
||||
importance: 'primary',
|
||||
tags: ['document', 'chunked'],
|
||||
};
|
||||
|
||||
const chunkTitle = chunks.length > 1
|
||||
? `${filename} (chunk ${chunk.metadata.chunkIndex + 1}/${chunk.metadata.totalChunks})`
|
||||
: filename;
|
||||
|
||||
const knowledgeItem = await createKnowledgeItem({
|
||||
projectId,
|
||||
sourceType: 'imported_document',
|
||||
title: chunkTitle,
|
||||
content: chunk.content,
|
||||
sourceMeta: {
|
||||
...sourceMeta,
|
||||
chunkMetadata: {
|
||||
chunkIndex: chunk.metadata.chunkIndex,
|
||||
totalChunks: chunk.metadata.totalChunks,
|
||||
startChar: chunk.metadata.startChar,
|
||||
endChar: chunk.metadata.endChar,
|
||||
tokenCount: chunk.metadata.tokenCount,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
knowledgeItemIds.push(knowledgeItem.id);
|
||||
|
||||
// Chunk and embed in AlloyDB (fire-and-forget)
|
||||
(async () => {
|
||||
try {
|
||||
const { writeKnowledgeChunksForItem } = await import('@/lib/server/vector-memory');
|
||||
await writeKnowledgeChunksForItem({
|
||||
id: knowledgeItem.id,
|
||||
projectId: knowledgeItem.projectId,
|
||||
content: knowledgeItem.content,
|
||||
sourceMeta: knowledgeItem.sourceMeta,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`[import-document] Failed to chunk item ${knowledgeItem.id}:`, error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Also create a summary record in contextSources for UI display
|
||||
const contextSourcesRef = adminDb.collection('projects').doc(projectId).collection('contextSources');
|
||||
await contextSourcesRef.add({
|
||||
type: 'document',
|
||||
name: filename,
|
||||
summary: `Document with ${chunks.length} chunks (${content.length} characters)`,
|
||||
connectedAt: new Date(),
|
||||
metadata: {
|
||||
chunkCount: chunks.length,
|
||||
totalChars: content.length,
|
||||
mimeType: body.mimeType,
|
||||
knowledgeItemIds,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
filename,
|
||||
chunkCount: chunks.length,
|
||||
knowledgeItemIds,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[import-document] Failed to import document', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to import document',
|
||||
details: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user