"use client"; import { useState, useRef } from "react"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; import { Card } from "@/components/ui/card"; import type LlamaStackClient from "llama-stack-client"; interface VectorDbManagerProps { client: LlamaStackClient; onVectorDbCreated: () => void; } interface UploadState { isUploading: boolean; uploadProgress: string; uploadError: string | null; } export function VectorDbManager({ client, onVectorDbCreated, }: VectorDbManagerProps) { const [showCreateForm, setShowCreateForm] = useState(false); const [isCreating, setIsCreating] = useState(false); const [createError, setCreateError] = useState(null); const [formData, setFormData] = useState({ vectorDbId: "", embeddingModel: "all-MiniLM-L6-v2", embeddingDimension: "384", }); const [uploadState, setUploadState] = useState({ isUploading: false, uploadProgress: "", uploadError: null, }); const [urlInput, setUrlInput] = useState(""); const fileInputRef = useRef(null); const handleCreateVectorDb = async () => { if (!formData.vectorDbId.trim()) { setCreateError("Vector DB ID is required"); return; } setIsCreating(true); setCreateError(null); try { // Get available providers to find a vector_io provider const providers = await client.providers.list(); const vectorIoProvider = providers.find(p => p.api === "vector_io"); if (!vectorIoProvider) { throw new Error("No vector_io provider found"); } await client.vectorDBs.register({ vector_db_id: formData.vectorDbId.trim(), embedding_model: formData.embeddingModel, embedding_dimension: parseInt(formData.embeddingDimension), provider_id: vectorIoProvider.provider_id, }); // Reset form and close setFormData({ vectorDbId: "", embeddingModel: "all-MiniLM-L6-v2", embeddingDimension: "384", }); setShowCreateForm(false); // Refresh the vector DB list onVectorDbCreated(); } catch (err) { console.error("Error creating vector DB:", err); setCreateError( err instanceof Error ? err.message : "Failed to create vector database" ); } finally { setIsCreating(false); } }; const handleCancel = () => { setShowCreateForm(false); setCreateError(null); setFormData({ vectorDbId: "", embeddingModel: "all-MiniLM-L6-v2", embeddingDimension: "384", }); setUploadState({ isUploading: false, uploadProgress: "", uploadError: null, }); setUrlInput(""); }; const chunkText = (text: string, chunkSize: number = 512): string[] => { const words = text.split(/\s+/); const chunks: string[] = []; for (let i = 0; i < words.length; i += chunkSize) { chunks.push(words.slice(i, i + chunkSize).join(" ")); } return chunks; }; const ingestDocument = async ( content: string, documentId: string, vectorDbId: string ) => { const chunks = chunkText(content); const vectorChunks = chunks.map((chunk, index) => ({ content: chunk, metadata: { document_id: documentId, chunk_index: index, source: documentId, }, })); await client.vectorIo.insert({ vector_db_id: vectorDbId, chunks: vectorChunks, }); }; const handleFileUpload = async (vectorDbId: string) => { if (!fileInputRef.current?.files?.length) return; const file = fileInputRef.current.files[0]; setUploadState({ isUploading: true, uploadProgress: `Reading ${file.name}...`, uploadError: null, }); try { const text = await file.text(); setUploadState({ isUploading: true, uploadProgress: `Ingesting ${file.name} into vector database...`, uploadError: null, }); await ingestDocument(text, file.name, vectorDbId); setUploadState({ isUploading: false, uploadProgress: `Successfully ingested ${file.name}`, uploadError: null, }); // Clear file input if (fileInputRef.current) { fileInputRef.current.value = ""; } } catch (err) { console.error("Error uploading file:", err); setUploadState({ isUploading: false, uploadProgress: "", uploadError: err instanceof Error ? err.message : "Failed to upload file", }); } }; const handleUrlUpload = async (vectorDbId: string) => { if (!urlInput.trim()) return; setUploadState({ isUploading: true, uploadProgress: `Fetching content from ${urlInput}...`, uploadError: null, }); try { const response = await fetch(`/api/fetch-url`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: urlInput }), }); if (!response.ok) { throw new Error(`Failed to fetch URL: ${response.statusText}`); } const { content } = await response.json(); setUploadState({ isUploading: true, uploadProgress: `Ingesting content from ${urlInput}...`, uploadError: null, }); await ingestDocument(content, urlInput, vectorDbId); setUploadState({ isUploading: false, uploadProgress: `Successfully ingested content from ${urlInput}`, uploadError: null, }); setUrlInput(""); } catch (err) { console.error("Error uploading URL:", err); setUploadState({ isUploading: false, uploadProgress: "", uploadError: err instanceof Error ? err.message : "Failed to fetch URL content", }); } }; return (
{!showCreateForm ? ( ) : (

Create Vector Database

{createError && (

{createError}

)} {uploadState.uploadError && (

{uploadState.uploadError}

)} {uploadState.uploadProgress && (

{uploadState.uploadProgress}

)}
setFormData({ ...formData, vectorDbId: e.target.value }) } placeholder="Enter unique vector DB identifier" disabled={isCreating || uploadState.isUploading} />
{/* Document Upload Section */}

Upload Documents (Optional)

setUrlInput(e.target.value)} placeholder="https://example.com/article" disabled={!formData.vectorDbId || uploadState.isUploading} />

Note: Create the Vector DB first, then upload documents to it.

)}
); }