import { Head, router } from '@inertiajs/react' import { useState } from 'react' import StyledTable from '~/components/StyledTable' import SettingsLayout from '~/layouts/SettingsLayout' import { NomadOllamaModel } from '../../../types/ollama' import StyledButton from '~/components/StyledButton' import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus' import Alert from '~/components/Alert' import { useNotifications } from '~/context/NotificationContext' import api from '~/lib/api' import { useModals } from '~/context/ModalContext' import StyledModal from '~/components/StyledModal' import { ModelResponse } from 'ollama' import { SERVICE_NAMES } from '../../../constants/service_names' import Switch from '~/components/inputs/Switch' import StyledSectionHeader from '~/components/StyledSectionHeader' import { useMutation, useQuery } from '@tanstack/react-query' import Input from '~/components/inputs/Input' import { IconSearch } from '@tabler/icons-react' import useDebounce from '~/hooks/useDebounce' import ActiveModelDownloads from '~/components/ActiveModelDownloads' export default function ModelsPage(props: { models: { availableModels: NomadOllamaModel[] installedModels: ModelResponse[] settings: { chatSuggestionsEnabled: boolean } } }) { const { isInstalled } = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA) const { addNotification } = useNotifications() const { openModal, closeAllModals } = useModals() const { debounce } = useDebounce() const [chatSuggestionsEnabled, setChatSuggestionsEnabled] = useState( props.models.settings.chatSuggestionsEnabled ) const [query, setQuery] = useState('') const [queryUI, setQueryUI] = useState('') const debouncedSetQuery = debounce((val: string) => { setQuery(val) }, 300) const { data: availableModels, isLoading } = useQuery({ queryKey: ['ollama', 'availableModels', query], queryFn: async () => { const res = await api.getAvailableModels(query, false) if (!res) { return [] } return res }, initialData: props.models.availableModels, }) async function handleInstallModel(modelName: string) { try { const res = await api.downloadModel(modelName) if (res.success) { addNotification({ message: `Model download initiated for ${modelName}. It may take some time to complete.`, type: 'success', }) } } catch (error) { console.error('Error installing model:', error) addNotification({ message: `There was an error installing the model: ${modelName}. Please try again.`, type: 'error', }) } } async function handleDeleteModel(modelName: string) { try { const res = await api.deleteModel(modelName) if (res.success) { addNotification({ message: `Model deleted: ${modelName}.`, type: 'success', }) } closeAllModals() router.reload() } catch (error) { console.error('Error deleting model:', error) addNotification({ message: `There was an error deleting the model: ${modelName}. Please try again.`, type: 'error', }) } } async function confirmDeleteModel(model: string) { openModal( { handleDeleteModel(model) }} onCancel={closeAllModals} open={true} confirmText="Delete" cancelText="Cancel" confirmVariant="primary" >

Are you sure you want to delete this model? You will need to download it again if you want to use it in the future.

, 'confirm-delete-model-modal' ) } const updateSettingMutation = useMutation({ mutationFn: async ({ key, value }: { key: string; value: boolean }) => { return await api.updateSetting(key, value) }, onSuccess: () => { addNotification({ message: 'Setting updated successfully.', type: 'success', }) }, onError: (error) => { console.error('Error updating setting:', error) addNotification({ message: 'There was an error updating the setting. Please try again.', type: 'error', }) }, }) return (

AI Assistant

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.

{!isInstalled && ( )}
{ setChatSuggestionsEnabled(newVal) updateSettingMutation.mutate({ key: 'chat.suggestionsEnabled', value: newVal }) }} label="Chat Suggestions" description="Display AI-generated conversation starters in the chat interface" />
{ setQueryUI(e.target.value) debouncedSetQuery(e.target.value) }} className="w-1/3" leftIcon={} />
className="font-semibold mt-4" rowLines={true} columns={[ { accessor: 'name', title: 'Name', render(record) { return (

{record.name}

{record.description}

) }, }, { accessor: 'estimated_pulls', title: 'Estimated Pulls', }, { accessor: 'model_last_updated', title: 'Last Updated', }, ]} data={availableModels || []} loading={isLoading} expandable={{ expandedRowRender: (record) => (
{record.tags.map((tag, tagIndex) => { const isInstalled = props.models.installedModels.some( (mod) => mod.name === tag.name ) return ( ) })}
Tag Input Type Context Size Model Size Action
{tag.name} {tag.input || 'N/A'} {tag.context || 'N/A'} {tag.size || 'N/A'} { if (!isInstalled) { handleInstallModel(tag.name) } else { confirmDeleteModel(tag.name) } }} icon={isInstalled ? 'IconTrash' : 'IconDownload'} > {isInstalled ? 'Delete' : 'Install'}
), }} />
) }