'use client'; import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Plus, Edit2, Trash2, Clock, MoreVertical, AlertCircle, FileText, Eye, EyeOff, Globe, Search, Loader2, } from 'lucide-react'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { useKnowledgeBaseEntries, useCreateKnowledgeBaseEntry, useUpdateKnowledgeBaseEntry, useDeleteKnowledgeBaseEntry, } from '@/hooks/react-query/knowledge-base/use-knowledge-base-queries'; import { cn } from '@/lib/utils'; import { CreateKnowledgeBaseEntryRequest, KnowledgeBaseEntry, UpdateKnowledgeBaseEntryRequest } from '@/hooks/react-query/knowledge-base/types'; interface KnowledgeBaseManagerProps { threadId: string; } interface EditDialogData { entry?: KnowledgeBaseEntry; isOpen: boolean; } const USAGE_CONTEXT_OPTIONS = [ { value: 'always', label: 'Always Active', icon: Globe, color: 'bg-green-50 text-green-700 border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800' }, // { // value: 'contextual', // label: 'Smart Context', // description: 'Included when contextually relevant', // icon: Target, // color: 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800' // }, // { // value: 'on_request', // label: 'On Demand', // description: 'Only when explicitly requested', // icon: Zap, // color: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/20 dark:text-amber-400 dark:border-amber-800' // }, ] as const; const KnowledgeBaseSkeleton = () => (
{[1, 2, 3].map((i) => (
))}
); export const KnowledgeBaseManager = ({ threadId }: KnowledgeBaseManagerProps) => { const [editDialog, setEditDialog] = useState({ isOpen: false }); const [deleteEntryId, setDeleteEntryId] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [formData, setFormData] = useState({ name: '', description: '', content: '', usage_context: 'always', }); const { data: knowledgeBase, isLoading, error } = useKnowledgeBaseEntries(threadId); const createMutation = useCreateKnowledgeBaseEntry(); const updateMutation = useUpdateKnowledgeBaseEntry(); const deleteMutation = useDeleteKnowledgeBaseEntry(); const handleOpenCreateDialog = () => { setFormData({ name: '', description: '', content: '', usage_context: 'always', }); setEditDialog({ isOpen: true }); }; const handleOpenEditDialog = (entry: KnowledgeBaseEntry) => { setFormData({ name: entry.name, description: entry.description || '', content: entry.content, usage_context: entry.usage_context, }); setEditDialog({ entry, isOpen: true }); }; const handleCloseDialog = () => { setEditDialog({ isOpen: false }); setFormData({ name: '', description: '', content: '', usage_context: 'always', }); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formData.name.trim() || !formData.content.trim()) { return; } try { if (editDialog.entry) { const updateData: UpdateKnowledgeBaseEntryRequest = { name: formData.name !== editDialog.entry.name ? formData.name : undefined, description: formData.description !== editDialog.entry.description ? formData.description : undefined, content: formData.content !== editDialog.entry.content ? formData.content : undefined, usage_context: formData.usage_context !== editDialog.entry.usage_context ? formData.usage_context : undefined, }; const hasChanges = Object.values(updateData).some(value => value !== undefined); if (hasChanges) { await updateMutation.mutateAsync({ entryId: editDialog.entry.entry_id, data: updateData }); } } else { await createMutation.mutateAsync({ threadId, data: formData }); } handleCloseDialog(); } catch (error) { console.error('Error saving knowledge base entry:', error); } }; const handleDelete = async (entryId: string) => { try { await deleteMutation.mutateAsync(entryId); setDeleteEntryId(null); } catch (error) { console.error('Error deleting knowledge base entry:', error); } }; const handleToggleActive = async (entry: KnowledgeBaseEntry) => { try { await updateMutation.mutateAsync({ entryId: entry.entry_id, data: { is_active: !entry.is_active } }); } catch (error) { console.error('Error toggling entry status:', error); } }; const getUsageContextConfig = (context: string) => { return USAGE_CONTEXT_OPTIONS.find(option => option.value === context) || USAGE_CONTEXT_OPTIONS[0]; }; if (isLoading) { return ; } if (error) { return (

Failed to load knowledge base

); } const entries = knowledgeBase?.entries || []; const filteredEntries = entries.filter(entry => entry.name.toLowerCase().includes(searchQuery.toLowerCase()) || entry.content.toLowerCase().includes(searchQuery.toLowerCase()) || (entry.description && entry.description.toLowerCase().includes(searchQuery.toLowerCase())) ); return (
{entries.length > 0 && ( <>
setSearchQuery(e.target.value)} className="pl-9" />
{filteredEntries.length === 0 ? (

No entries match your search

) : ( filteredEntries.map((entry) => { const contextConfig = getUsageContextConfig(entry.usage_context); const ContextIcon = contextConfig.icon; return (

{entry.name}

{!entry.is_active && ( Disabled )}
{entry.description && (

{entry.description}

)}

{entry.content}

{contextConfig.label} {new Date(entry.created_at).toLocaleDateString()}
{entry.content_tokens && ( ~{entry.content_tokens.toLocaleString()} tokens )}
handleOpenEditDialog(entry)}> Edit handleToggleActive(entry)}> {entry.is_active ? ( <> Disable ) : ( <> Enable )} setDeleteEntryId(entry.entry_id)} className="text-destructive focus:bg-destructive/10 focus:text-destructive" > Delete
); }) )}
)} {entries.length === 0 && (

No Knowledge Entries

Add knowledge entries to provide your agent with context, guidelines, and information it should always remember.

)} {editDialog.entry ? 'Edit Knowledge Entry' : 'Add Knowledge Entry'}
setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="e.g., Company Guidelines, API Documentation" required className="w-full" />
setFormData(prev => ({ ...prev, description: e.target.value }))} placeholder="Brief description of this knowledge (optional)" />