diff --git a/admin/app/controllers/settings_controller.ts b/admin/app/controllers/settings_controller.ts index 2aebdd8..c0a312f 100644 --- a/admin/app/controllers/settings_controller.ts +++ b/admin/app/controllers/settings_controller.ts @@ -54,12 +54,14 @@ export default class SettingsController { const availableModels = await this.ollamaService.getAvailableModels({ sort: 'pulls', recommendedOnly: false, query: null, limit: 15 }); const installedModels = await this.ollamaService.getModels(); const chatSuggestionsEnabled = await KVStore.getValue('chat.suggestionsEnabled') + const aiAssistantCustomName = await KVStore.getValue('ai.assistantCustomName') return inertia.render('settings/models', { models: { availableModels: availableModels?.models || [], installedModels: installedModels || [], settings: { - chatSuggestionsEnabled: chatSuggestionsEnabled ?? false + chatSuggestionsEnabled: chatSuggestionsEnabled ?? false, + aiAssistantCustomName: aiAssistantCustomName ?? '', } } }); diff --git a/admin/app/models/kv_store.ts b/admin/app/models/kv_store.ts index caad66f..8df2b03 100644 --- a/admin/app/models/kv_store.ts +++ b/admin/app/models/kv_store.ts @@ -50,4 +50,15 @@ export default class KVStore extends BaseModel { } return setting } + + /** + * Clear a setting value by key, storing null so getValue returns null. + */ + static async clearValue(key: K): Promise { + const setting = await this.findBy('key', key) + if (setting && setting.value !== null) { + setting.value = null + await setting.save() + } + } } diff --git a/admin/app/services/system_service.ts b/admin/app/services/system_service.ts index fc0b5db..0d5d3a6 100644 --- a/admin/app/services/system_service.ts +++ b/admin/app/services/system_service.ts @@ -12,7 +12,7 @@ import { getAllFilesystems, getFile } from '../utils/fs.js' import axios from 'axios' import env from '#start/env' import KVStore from '#models/kv_store' -import { KVStoreKey } from '../../types/kv_store.js' +import { KV_STORE_SCHEMA, KVStoreKey } from '../../types/kv_store.js' @inject() @@ -388,7 +388,11 @@ export class SystemService { } async updateSetting(key: KVStoreKey, value: any): Promise { - await KVStore.setValue(key, value); + if ((value === '' || value === undefined || value === null) && KV_STORE_SCHEMA[key] === 'string') { + await KVStore.clearValue(key) + } else { + await KVStore.setValue(key, value) + } } /** diff --git a/admin/app/validators/settings.ts b/admin/app/validators/settings.ts index 0dbab46..3bd41a3 100644 --- a/admin/app/validators/settings.ts +++ b/admin/app/validators/settings.ts @@ -4,5 +4,5 @@ import { SETTINGS_KEYS } from "../../constants/kv_store.js"; export const updateSettingSchema = vine.compile(vine.object({ key: vine.enum(SETTINGS_KEYS), - value: vine.any(), + value: vine.any().optional(), })) \ No newline at end of file diff --git a/admin/config/inertia.ts b/admin/config/inertia.ts index 0e4b4cf..9b1b9b3 100644 --- a/admin/config/inertia.ts +++ b/admin/config/inertia.ts @@ -1,3 +1,4 @@ +import KVStore from '#models/kv_store' import { SystemService } from '#services/system_service' import { defineConfig } from '@adonisjs/inertia' import type { InferSharedProps } from '@adonisjs/inertia/types' @@ -14,6 +15,10 @@ const inertiaConfig = defineConfig({ sharedData: { appVersion: () => SystemService.getAppVersion(), environment: process.env.NODE_ENV || 'production', + aiAssistantName: async () => { + const customName = await KVStore.getValue('ai.assistantCustomName') + return (customName && customName.trim()) ? customName : 'AI Assistant' + }, }, /** diff --git a/admin/constants/kv_store.ts b/admin/constants/kv_store.ts index 59ae9c9..4f4196d 100644 --- a/admin/constants/kv_store.ts +++ b/admin/constants/kv_store.ts @@ -1,3 +1,3 @@ import { KVStoreKey } from "../types/kv_store.js"; -export const SETTINGS_KEYS: KVStoreKey[] = ['chat.suggestionsEnabled', 'ui.hasVisitedEasySetup', 'system.earlyAccess']; \ No newline at end of file +export const SETTINGS_KEYS: KVStoreKey[] = ['chat.suggestionsEnabled', 'ui.hasVisitedEasySetup', 'system.earlyAccess', 'ai.assistantCustomName']; \ No newline at end of file diff --git a/admin/inertia/components/chat/ChatInterface.tsx b/admin/inertia/components/chat/ChatInterface.tsx index af2961f..75d4168 100644 --- a/admin/inertia/components/chat/ChatInterface.tsx +++ b/admin/inertia/components/chat/ChatInterface.tsx @@ -9,6 +9,7 @@ import StyledModal from '../StyledModal' import api from '~/lib/api' import { DEFAULT_QUERY_REWRITE_MODEL } from '../../../constants/ollama' import { useNotifications } from '~/context/NotificationContext' +import { usePage } from '@inertiajs/react' interface ChatInterfaceProps { messages: ChatMessage[] @@ -29,6 +30,7 @@ export default function ChatInterface({ chatSuggestionsLoading = false, rewriteModelAvailable = false }: ChatInterfaceProps) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props const { addNotification } = useNotifications() const [input, setInput] = useState('') const [downloadDialogOpen, setDownloadDialogOpen] = useState(false) @@ -160,7 +162,7 @@ export default function ChatInterface({ value={input} onChange={handleInput} onKeyDown={handleKeyDown} - placeholder="Type your message... (Shift+Enter for new line)" + placeholder={`Type your message to ${aiAssistantName}... (Shift+Enter for new line)`} className="w-full resize-none rounded-lg border border-gray-300 px-4 py-3 pr-12 focus:outline-none focus:ring-2 focus:ring-desert-green focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500" rows={1} disabled={isLoading} diff --git a/admin/inertia/components/chat/ChatSidebar.tsx b/admin/inertia/components/chat/ChatSidebar.tsx index c378afd..387ff72 100644 --- a/admin/inertia/components/chat/ChatSidebar.tsx +++ b/admin/inertia/components/chat/ChatSidebar.tsx @@ -1,6 +1,6 @@ import classNames from '~/lib/classNames' import StyledButton from '../StyledButton' -import { router } from '@inertiajs/react' +import { router, usePage } from '@inertiajs/react' import { ChatSession } from '../../../types/chat' import { IconMessage } from '@tabler/icons-react' import { useState } from 'react' @@ -23,6 +23,7 @@ export default function ChatSidebar({ onClearHistory, isInModal = false, }: ChatSidebarProps) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props const [isKnowledgeBaseModalOpen, setIsKnowledgeBaseModalOpen] = useState( () => new URLSearchParams(window.location.search).get('knowledge_base') === 'true' ) @@ -139,7 +140,7 @@ export default function ChatSidebar({ )} {isKnowledgeBaseModalOpen && ( - + )} ) diff --git a/admin/inertia/components/chat/KnowledgeBaseModal.tsx b/admin/inertia/components/chat/KnowledgeBaseModal.tsx index e95370d..195fcb1 100644 --- a/admin/inertia/components/chat/KnowledgeBaseModal.tsx +++ b/admin/inertia/components/chat/KnowledgeBaseModal.tsx @@ -11,10 +11,11 @@ import { useModals } from '~/context/ModalContext' import StyledModal from '../StyledModal' interface KnowledgeBaseModalProps { + aiAssistantName?: string onClose: () => void } -export default function KnowledgeBaseModal({ onClose }: KnowledgeBaseModalProps) { +export default function KnowledgeBaseModal({ aiAssistantName = "AI Assistant", onClose }: KnowledgeBaseModalProps) { const { addNotification } = useNotifications() const [files, setFiles] = useState([]) const fileUploaderRef = useRef>(null) @@ -140,12 +141,12 @@ export default function KnowledgeBaseModal({ onClose }: KnowledgeBaseModalProps)

- AI Assistant Knowledge Base Integration + {aiAssistantName} 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, + the content, making it directly accessible to {aiAssistantName}. This allows{' '} + {aiAssistantName} to reference your specific documents during conversations, providing more accurate and personalized responses based on your uploaded data.

@@ -177,8 +178,7 @@ export default function KnowledgeBaseModal({ onClose }: KnowledgeBaseModalProps)

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. + Information Library (if installed), making it instantly available to {aiAssistantName} without any extra steps.

diff --git a/admin/inertia/components/inputs/Input.tsx b/admin/inertia/components/inputs/Input.tsx index 928869b..3b61d6c 100644 --- a/admin/inertia/components/inputs/Input.tsx +++ b/admin/inertia/components/inputs/Input.tsx @@ -4,6 +4,7 @@ import { InputHTMLAttributes } from "react"; export interface InputProps extends InputHTMLAttributes { name: string; label: string; + helpText?: string; className?: string; labelClassName?: string; inputClassName?: string; @@ -17,6 +18,7 @@ const Input: React.FC = ({ className, label, name, + helpText, labelClassName, inputClassName, containerClassName, @@ -33,6 +35,7 @@ const Input: React.FC = ({ > {label}{required ? "*" : ""} + {helpText &&

{helpText}

}
{leftIcon && ( diff --git a/admin/inertia/layouts/SettingsLayout.tsx b/admin/inertia/layouts/SettingsLayout.tsx index 76dc4cd..b670e75 100644 --- a/admin/inertia/layouts/SettingsLayout.tsx +++ b/admin/inertia/layouts/SettingsLayout.tsx @@ -10,34 +10,37 @@ import { IconWand, IconZoom } from '@tabler/icons-react' +import { usePage } from '@inertiajs/react' import StyledSidebar from '~/components/StyledSidebar' import { getServiceLink } from '~/lib/navigation' -const navigation = [ - { name: 'AI Assistant', href: '/settings/models', icon: IconWand, current: false }, - { name: 'Apps', href: '/settings/apps', icon: IconTerminal2, current: false }, - { name: 'Benchmark', href: '/settings/benchmark', icon: IconChartBar, current: false }, - { name: 'Content Explorer', href: '/settings/zim/remote-explorer', icon: IconZoom, current: false }, - { name: 'Content Manager', href: '/settings/zim', icon: IconFolder, current: false }, - { name: 'Maps Manager', href: '/settings/maps', icon: IconMapRoute, current: false }, - { - name: 'Service Logs & Metrics', - href: getServiceLink('9999'), - icon: IconDashboard, - current: false, - target: '_blank', - }, - { - name: 'Check for Updates', - href: '/settings/update', - icon: IconArrowBigUpLines, - current: false, - }, - { name: 'System', href: '/settings/system', icon: IconSettings, current: false }, - { name: 'Legal Notices', href: '/settings/legal', icon: IconGavel, current: false }, -] - export default function SettingsLayout({ children }: { children: React.ReactNode }) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props + + const navigation = [ + { name: aiAssistantName, href: '/settings/models', icon: IconWand, current: false }, + { name: 'Apps', href: '/settings/apps', icon: IconTerminal2, current: false }, + { name: 'Benchmark', href: '/settings/benchmark', icon: IconChartBar, current: false }, + { name: 'Content Explorer', href: '/settings/zim/remote-explorer', icon: IconZoom, current: false }, + { name: 'Content Manager', href: '/settings/zim', icon: IconFolder, current: false }, + { name: 'Maps Manager', href: '/settings/maps', icon: IconMapRoute, current: false }, + { + name: 'Service Logs & Metrics', + href: getServiceLink('9999'), + icon: IconDashboard, + current: false, + target: '_blank', + }, + { + name: 'Check for Updates', + href: '/settings/update', + icon: IconArrowBigUpLines, + current: false, + }, + { name: 'System', href: '/settings/system', icon: IconSettings, current: false }, + { name: 'Legal Notices', href: '/settings/legal', icon: IconGavel, current: false }, + ] + return (
diff --git a/admin/inertia/pages/chat.tsx b/admin/inertia/pages/chat.tsx index 3c9c118..f268be2 100644 --- a/admin/inertia/pages/chat.tsx +++ b/admin/inertia/pages/chat.tsx @@ -1,10 +1,11 @@ -import { Head } from '@inertiajs/react' +import { Head, usePage } from '@inertiajs/react' import ChatComponent from '~/components/chat' export default function Chat(props: { settings: { chatSuggestionsEnabled: boolean } }) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props return (
- +
) diff --git a/admin/inertia/pages/easy-setup/index.tsx b/admin/inertia/pages/easy-setup/index.tsx index 2f23c58..744bcb8 100644 --- a/admin/inertia/pages/easy-setup/index.tsx +++ b/admin/inertia/pages/easy-setup/index.tsx @@ -1,4 +1,4 @@ -import { Head, router } from '@inertiajs/react' +import { Head, router, usePage } from '@inertiajs/react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useEffect, useState, useMemo } from 'react' import AppLayout from '~/layouts/AppLayout' @@ -32,51 +32,53 @@ interface Capability { icon: string } -const CORE_CAPABILITIES: Capability[] = [ - { - id: 'information', - name: 'Information Library', - technicalName: 'Kiwix', - description: - 'Offline access to Wikipedia, medical references, how-to guides, and encyclopedias', - features: [ - 'Complete Wikipedia offline', - 'Medical references and first aid guides', - 'WikiHow articles and tutorials', - 'Project Gutenberg books and literature', - ], - services: [SERVICE_NAMES.KIWIX], - icon: 'IconBooks', - }, - { - id: 'education', - name: 'Education Platform', - technicalName: 'Kolibri', - description: 'Interactive learning platform with video courses and exercises', - features: [ - 'Khan Academy math and science courses', - 'K-12 curriculum content', - 'Interactive exercises and quizzes', - 'Progress tracking for learners', - ], - services: [SERVICE_NAMES.KOLIBRI], - icon: 'IconSchool', - }, - { - id: 'ai', - name: 'AI Assistant', - technicalName: 'Ollama', - description: 'Local AI chat that runs entirely on your hardware - no internet required', - features: [ - 'Private conversations that never leave your device', - 'No internet connection needed after setup', - 'Ask questions, get help with writing, brainstorm ideas', - 'Runs on your own hardware with local AI models', - ], - services: [SERVICE_NAMES.OLLAMA], - icon: 'IconRobot', - }, -] +function buildCoreCapabilities(aiAssistantName: string): Capability[] { + return [ + { + id: 'information', + name: 'Information Library', + technicalName: 'Kiwix', + description: + 'Offline access to Wikipedia, medical references, how-to guides, and encyclopedias', + features: [ + 'Complete Wikipedia offline', + 'Medical references and first aid guides', + 'WikiHow articles and tutorials', + 'Project Gutenberg books and literature', + ], + services: [SERVICE_NAMES.KIWIX], + icon: 'IconBooks', + }, + { + id: 'education', + name: 'Education Platform', + technicalName: 'Kolibri', + description: 'Interactive learning platform with video courses and exercises', + features: [ + 'Khan Academy math and science courses', + 'K-12 curriculum content', + 'Interactive exercises and quizzes', + 'Progress tracking for learners', + ], + services: [SERVICE_NAMES.KOLIBRI], + icon: 'IconSchool', + }, + { + id: 'ai', + name: aiAssistantName, + technicalName: 'Ollama', + description: 'Local AI chat that runs entirely on your hardware - no internet required', + features: [ + 'Private conversations that never leave your device', + 'No internet connection needed after setup', + 'Ask questions, get help with writing, brainstorm ideas', + 'Runs on your own hardware with local AI models', + ], + services: [SERVICE_NAMES.OLLAMA], + icon: 'IconRobot', + }, + ] +} const ADDITIONAL_TOOLS: Capability[] = [ { @@ -110,6 +112,9 @@ const CURATED_CATEGORIES_KEY = 'curated-categories' const WIKIPEDIA_STATE_KEY = 'wikipedia-state' export default function EasySetupWizard(props: { system: { services: ServiceSlim[] } }) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props + const CORE_CAPABILITIES = buildCoreCapabilities(aiAssistantName) + const [currentStep, setCurrentStep] = useState(1) const [selectedServices, setSelectedServices] = useState([]) const [selectedMapCollections, setSelectedMapCollections] = useState([]) diff --git a/admin/inertia/pages/home.tsx b/admin/inertia/pages/home.tsx index 3f3fb8a..f2b2a26 100644 --- a/admin/inertia/pages/home.tsx +++ b/admin/inertia/pages/home.tsx @@ -6,7 +6,7 @@ import { IconSettings, IconWifiOff, } from '@tabler/icons-react' -import { Head } from '@inertiajs/react' +import { Head, usePage } from '@inertiajs/react' import AppLayout from '~/layouts/AppLayout' import { getServiceLink } from '~/lib/navigation' import { ServiceSlim } from '../../types/services' @@ -14,6 +14,7 @@ import DynamicIcon, { DynamicIconName } from '~/components/DynamicIcon' import { useUpdateAvailable } from '~/hooks/useUpdateAvailable' import { useSystemSetting } from '~/hooks/useSystemSetting' import Alert from '~/components/Alert' +import { SERVICE_NAMES } from '../../constants/service_names' // Maps is a Core Capability (display_order: 4) const MAPS_ITEM = { @@ -90,6 +91,7 @@ export default function Home(props: { }) { const items: DashboardItem[] = [] const updateInfo = useUpdateAvailable(); + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props // Check if user has visited Easy Setup const { data: easySetupVisited } = useSystemSetting({ @@ -102,7 +104,8 @@ export default function Home(props: { .filter((service) => service.installed && service.ui_location) .forEach((service) => { items.push({ - label: service.friendly_name || service.service_name, + // Inject custom AI Assistant name if this is the chat service + label: service.service_name === SERVICE_NAMES.OLLAMA && aiAssistantName ? aiAssistantName : (service.friendly_name || service.service_name), to: service.ui_location ? getServiceLink(service.ui_location) : '#', target: '_blank', description: diff --git a/admin/inertia/pages/knowledge-base.tsx b/admin/inertia/pages/knowledge-base.tsx deleted file mode 100644 index b1ea484..0000000 --- a/admin/inertia/pages/knowledge-base.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { Head } from '@inertiajs/react' -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 AppLayout from '~/layouts/AppLayout' -import api from '~/lib/api' - -export default function KnowledgeBase() { - 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 ( - - -
-
-
- { - 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/settings/benchmark.tsx b/admin/inertia/pages/settings/benchmark.tsx index f08a57a..69a4791 100644 --- a/admin/inertia/pages/settings/benchmark.tsx +++ b/admin/inertia/pages/settings/benchmark.tsx @@ -1,4 +1,4 @@ -import { Head, Link } from '@inertiajs/react' +import { Head, Link, usePage } from '@inertiajs/react' import { useState, useEffect } from 'react' import SettingsLayout from '~/layouts/SettingsLayout' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' @@ -34,6 +34,7 @@ export default function BenchmarkPage(props: { currentBenchmarkId: string | null } }) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props const { subscribe } = useTransmit() const queryClient = useQueryClient() const aiInstalled = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA) @@ -403,8 +404,8 @@ export default function BenchmarkPage(props: { {showAIRequiredAlert && ( setShowAIRequiredAlert(false)} @@ -413,7 +414,7 @@ export default function BenchmarkPage(props: { href="/settings/apps" className="text-sm text-desert-green hover:underline mt-2 inline-block font-medium" > - Go to Apps to install AI Assistant → + Go to Apps to install {aiAssistantName} → )} @@ -444,7 +445,7 @@ export default function BenchmarkPage(props: { icon="IconWand" title={ !aiInstalled - ? 'AI Assistant must be installed to run AI benchmark' + ? `${aiAssistantName} must be installed to run AI benchmark` : undefined } > @@ -453,7 +454,8 @@ export default function BenchmarkPage(props: {
{!aiInstalled && (

- Note: AI Assistant is not installed. + Note: {aiAssistantName} is not + installed. )} diff --git a/admin/inertia/pages/settings/models.tsx b/admin/inertia/pages/settings/models.tsx index c817fa3..f21963f 100644 --- a/admin/inertia/pages/settings/models.tsx +++ b/admin/inertia/pages/settings/models.tsx @@ -1,4 +1,4 @@ -import { Head, router } from '@inertiajs/react' +import { Head, router, usePage } from '@inertiajs/react' import { useState } from 'react' import StyledTable from '~/components/StyledTable' import SettingsLayout from '~/layouts/SettingsLayout' @@ -24,9 +24,10 @@ export default function ModelsPage(props: { models: { availableModels: NomadOllamaModel[] installedModels: ModelResponse[] - settings: { chatSuggestionsEnabled: boolean } + settings: { chatSuggestionsEnabled: boolean; aiAssistantCustomName: string } } }) { + const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props const { isInstalled } = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA) const { addNotification } = useNotifications() const { openModal, closeAllModals } = useModals() @@ -34,6 +35,9 @@ export default function ModelsPage(props: { const [chatSuggestionsEnabled, setChatSuggestionsEnabled] = useState( props.models.settings.chatSuggestionsEnabled ) + const [aiAssistantCustomName, setAiAssistantCustomName] = useState( + props.models.settings.aiAssistantCustomName + ) const [query, setQuery] = useState('') const [queryUI, setQueryUI] = useState('') @@ -123,7 +127,7 @@ export default function ModelsPage(props: { } const updateSettingMutation = useMutation({ - mutationFn: async ({ key, value }: { key: string; value: boolean }) => { + mutationFn: async ({ key, value }: { key: string; value: boolean | string }) => { return await api.updateSetting(key, value) }, onSuccess: () => { @@ -143,18 +147,18 @@ export default function ModelsPage(props: { return ( - +

-

AI Assistant

+

{aiAssistantName}

- Easily manage the AI Assistant's settings and installed models. We recommend starting - with smaller models first to see how they perform on your system before moving on to - larger ones. + Easily manage the {aiAssistantName}'s settings and installed models. We recommend + starting with smaller models first to see how they perform on your system before moving + on to larger ones.

{!isInstalled && ( + setAiAssistantCustomName(e.target.value)} + onBlur={() => + updateSettingMutation.mutate({ + key: 'ai.assistantCustomName', + value: aiAssistantCustomName, + }) + } + />
diff --git a/admin/types/kv_store.ts b/admin/types/kv_store.ts index 37634ca..5b9dccd 100644 --- a/admin/types/kv_store.ts +++ b/admin/types/kv_store.ts @@ -1,11 +1,12 @@ export const KV_STORE_SCHEMA = { - 'chat.suggestionsEnabled': 'boolean', - 'rag.docsEmbedded': 'boolean', - 'system.updateAvailable': 'boolean', - 'system.latestVersion': 'string', - 'system.earlyAccess': 'boolean', - 'ui.hasVisitedEasySetup': 'boolean', + 'chat.suggestionsEnabled': 'boolean', + 'rag.docsEmbedded': 'boolean', + 'system.updateAvailable': 'boolean', + 'system.latestVersion': 'string', + 'system.earlyAccess': 'boolean', + 'ui.hasVisitedEasySetup': 'boolean', + 'ai.assistantCustomName': 'string', } as const type KVTagToType = T extends 'boolean' ? boolean : string