diff --git a/admin/inertia/components/chat/ChatSidebar.tsx b/admin/inertia/components/chat/ChatSidebar.tsx index a35e26b..a50cb72 100644 --- a/admin/inertia/components/chat/ChatSidebar.tsx +++ b/admin/inertia/components/chat/ChatSidebar.tsx @@ -3,6 +3,8 @@ import StyledButton from '../StyledButton' import { router } from '@inertiajs/react' import { ChatSession } from '../../../types/chat' import { IconMessage } from '@tabler/icons-react' +import { useState } from 'react' +import KnowledgeBaseModal from './KnowledgeBaseModal' interface ChatSidebarProps { sessions: ChatSession[] @@ -21,6 +23,8 @@ export default function ChatSidebar({ onClearHistory, isInModal = false, }: ChatSidebarProps) { + const [isKnowledgeBaseModalOpen, setIsKnowledgeBaseModalOpen] = useState(false) + return (
@@ -48,7 +52,7 @@ export default function ChatSidebar({
@@ -101,7 +105,7 @@ export default function ChatSidebar({ { - router.visit('/knowledge-base') + setIsKnowledgeBaseModalOpen(true) }} icon="IconBrain" variant="primary" @@ -122,6 +126,9 @@ export default function ChatSidebar({ )}
+ {isKnowledgeBaseModalOpen && ( + setIsKnowledgeBaseModalOpen(false)} /> + )}
) } diff --git a/admin/inertia/components/chat/KnowledgeBaseModal.tsx b/admin/inertia/components/chat/KnowledgeBaseModal.tsx new file mode 100644 index 0000000..cc569fa --- /dev/null +++ b/admin/inertia/components/chat/KnowledgeBaseModal.tsx @@ -0,0 +1,166 @@ +import { useMutation, useQuery } from '@tanstack/react-query' +import { useRef, useState } from 'react' +import FileUploader from '~/components/file-uploader' +import StyledButton from '~/components/StyledButton' +import StyledSectionHeader from '~/components/StyledSectionHeader' +import StyledTable from '~/components/StyledTable' +import { useNotifications } from '~/context/NotificationContext' +import api from '~/lib/api' +import { IconX } from '@tabler/icons-react' + +interface KnowledgeBaseModalProps { + onClose: () => void +} + +export default function KnowledgeBaseModal({ onClose }: KnowledgeBaseModalProps) { + const { addNotification } = useNotifications() + const [files, setFiles] = useState([]) + const fileUploaderRef = useRef>(null) + + const { data: storedFiles = [], isLoading: isLoadingFiles } = useQuery({ + queryKey: ['storedFiles'], + queryFn: () => api.getStoredRAGFiles(), + select: (data) => data || [], + }) + + const uploadMutation = useMutation({ + mutationFn: (file: File) => api.uploadDocument(file), + onSuccess: (data) => { + addNotification({ + type: 'success', + message: data?.message || 'Document uploaded and queued for processing', + }) + setFiles([]) + if (fileUploaderRef.current) { + fileUploaderRef.current.clear() + } + }, + onError: (error: any) => { + addNotification({ + type: 'error', + message: error?.message || 'Failed to upload document', + }) + }, + }) + + const handleUpload = () => { + if (files.length > 0) { + uploadMutation.mutate(files[0]) + } + } + + return ( +
+
+
+

Knowledge Base

+ +
+
+
+
+ { + setFiles(Array.from(uploadedFiles)) + }} + /> +
+ + Upload + +
+
+
+

+ Why upload documents to your Knowledge Base? +

+
+
+
+ 1 +
+
+

+ AI Assistant Knowledge Base Integration +

+

+ When you upload documents to your Knowledge Base, NOMAD processes and embeds + the content, making it directly accessible to the AI Assistant. This allows + the AI Assistant to reference your specific documents during conversations, + providing more accurate and personalized responses based on your uploaded + data. +

+
+
+
+
+ 2 +
+
+

+ Enhanced Document Processing with OCR +

+

+ NOMAD includes built-in Optical Character Recognition (OCR) capabilities, + allowing it to extract text from image-based documents such as scanned PDFs or + photos. This means that even if your documents are not in a standard text + format, NOMAD can still process and embed their content for AI access. +

+
+
+
+
+ 3 +
+
+

+ Information Library Integration +

+

+ NOMAD will automatically discover and extract any content you save to your + Information Library (if installed), making it instantly available to the AI + Assistant without any extra steps. +

+
+
+
+
+
+
+ + + className="font-semibold" + rowLines={true} + columns={[ + { + accessor: 'source', + title: 'File Name', + render(record) { + return {record.source} + }, + }, + ]} + data={storedFiles.map((source) => ({ source }))} + loading={isLoadingFiles} + /> +
+
+
+
+ ) +} diff --git a/admin/inertia/pages/home.tsx b/admin/inertia/pages/home.tsx index 6e731ea..1aababe 100644 --- a/admin/inertia/pages/home.tsx +++ b/admin/inertia/pages/home.tsx @@ -1,6 +1,5 @@ import { IconBolt, - IconBrain, IconHelp, IconMapRoute, IconPlus, @@ -12,7 +11,6 @@ import AppLayout from '~/layouts/AppLayout' import { getServiceLink } from '~/lib/navigation' import { ServiceSlim } from '../../types/services' import DynamicIcon, { DynamicIconName } from '~/components/DynamicIcon' -import { SERVICE_NAMES } from '../../constants/service_names' import { useUpdateAvailable } from '~/hooks/useUpdateAvailable' import Alert from '~/components/Alert' @@ -84,17 +82,6 @@ interface DashboardItem { poweredBy: string | null } -const KNOWLEDGE_BASE_ITEM: DashboardItem = { - label: 'Knowledge Base', - to: '/knowledge-base', - target: '', - description: 'Upload documents to your personal knowledge base for AI access', - icon: , - installed: true, - displayOrder: 5, - poweredBy: null, -} - export default function Home(props: { system: { services: ServiceSlim[] @@ -130,9 +117,6 @@ export default function Home(props: { // Add system items items.push(...SYSTEM_ITEMS) - if (props.system.services.find((s) => s.service_name === SERVICE_NAMES.OLLAMA && s.installed)) { - items.push(KNOWLEDGE_BASE_ITEM) - } // Sort all items by display order items.sort((a, b) => a.displayOrder - b.displayOrder) diff --git a/admin/start/routes.ts b/admin/start/routes.ts index c104b44..336ffca 100644 --- a/admin/start/routes.ts +++ b/admin/start/routes.ts @@ -27,7 +27,6 @@ router.get('/', [HomeController, 'index']) router.get('/home', [HomeController, 'home']) router.on('/about').renderInertia('about') router.get('/chat', [ChatsController, 'inertia']) -router.on('/knowledge-base').renderInertia('knowledge-base') router.get('/maps', [MapsController, 'index']) router.get('/easy-setup', [EasySetupController, 'index'])