diff --git a/admin/app/services/ollama_service.ts b/admin/app/services/ollama_service.ts index fa7b9f9..dc4ab35 100644 --- a/admin/app/services/ollama_service.ts +++ b/admin/app/services/ollama_service.ts @@ -199,7 +199,7 @@ export class OllamaService { query: null, limit: 15, } - ): Promise<{ models: NomadOllamaModel[], hasMore: boolean } | null> { + ): Promise<{ models: NomadOllamaModel[], hasMore: boolean, source: 'api' | 'fallback' } | null> { try { const models = await this.retrieveAndRefreshModels(sort, force) if (!models) { @@ -209,7 +209,8 @@ export class OllamaService { ) return { models: FALLBACK_RECOMMENDED_OLLAMA_MODELS, - hasMore: false + hasMore: false, + source: 'fallback', } } @@ -217,7 +218,8 @@ export class OllamaService { const filteredModels = query ? this.fuseSearchModels(models, query) : models return { models: filteredModels.slice(0, limit || 15), - hasMore: filteredModels.length > (limit || 15) + hasMore: filteredModels.length > (limit || 15), + source: 'api', } } @@ -237,13 +239,15 @@ export class OllamaService { const filteredRecommendedModels = this.fuseSearchModels(recommendedModels, query) return { models: filteredRecommendedModels, - hasMore: filteredRecommendedModels.length > (limit || 15) + hasMore: filteredRecommendedModels.length > (limit || 15), + source: 'api', } } return { models: recommendedModels, - hasMore: recommendedModels.length > (limit || 15) + hasMore: recommendedModels.length > (limit || 15), + source: 'api', } } catch (error) { logger.error( diff --git a/admin/app/services/system_service.ts b/admin/app/services/system_service.ts index 84157af..eabab36 100644 --- a/admin/app/services/system_service.ts +++ b/admin/app/services/system_service.ts @@ -14,6 +14,7 @@ import env from '#start/env' import KVStore from '#models/kv_store' import { KV_STORE_SCHEMA, KVStoreKey } from '../../types/kv_store.js' import { isNewerVersion } from '../utils/version.js' +import { NOMAD_API_DEFAULT_BASE_URL } from '../../constants/misc.js' @inject() @@ -384,8 +385,9 @@ export class SystemService { async subscribeToReleaseNotes(email: string): Promise<{ success: boolean; message: string }> { try { + const baseUrl = env.get('NOMAD_API_URL') || NOMAD_API_DEFAULT_BASE_URL const response = await axios.post( - 'https://api.projectnomad.us/api/v1/lists/release-notes/subscribe', + `${baseUrl}/api/v1/lists/release-notes/subscribe`, { email }, { timeout: 5000 } ) @@ -399,13 +401,13 @@ export class SystemService { return { success: false, - message: `Failed to subscribe: ${response.statusText}`, + message: 'Failed to subscribe to release notes. Please try again later.', } } catch (error) { - logger.error('Error subscribing to release notes:', error) + logger.error({ err: error }, '[SystemService] Error subscribing to release notes') return { success: false, - message: `Failed to subscribe: ${error instanceof Error ? error.message : error}`, + message: 'Failed to subscribe to release notes. The service may be temporarily unavailable.', } } } diff --git a/admin/constants/ollama.ts b/admin/constants/ollama.ts index 5581832..b4e9291 100644 --- a/admin/constants/ollama.ts +++ b/admin/constants/ollama.ts @@ -19,14 +19,14 @@ export const FALLBACK_RECOMMENDED_OLLAMA_MODELS: NomadOllamaModel[] = [ context: '128k', input: 'Text', cloud: false, - thinking: false + thinking: false, }, ], }, { name: 'deepseek-r1', description: - 'DeepSeek-R1 is a family of open reasoning models with performance approaching that of leading models, such as O3 and Gemini 2.5 Pro.', + 'DeepSeek-R1 is a family of open reasoning models with performance approaching that of leading models.', estimated_pulls: '77.2M', id: '0b566560-68a6-4964-b0d4-beb3ab1ad694', first_seen: '2026-01-28T23:37:31.000+00:00', @@ -38,7 +38,15 @@ export const FALLBACK_RECOMMENDED_OLLAMA_MODELS: NomadOllamaModel[] = [ context: '128k', input: 'Text', cloud: false, - thinking: true + thinking: true, + }, + { + name: 'deepseek-r1:8b', + size: '4.9 GB', + context: '128k', + input: 'Text', + cloud: false, + thinking: true, }, ], }, @@ -56,7 +64,267 @@ export const FALLBACK_RECOMMENDED_OLLAMA_MODELS: NomadOllamaModel[] = [ context: '128k', input: 'Text', cloud: false, - thinking: false + thinking: false, + }, + { + name: 'llama3.2:3b', + size: '2.0 GB', + context: '128k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'mistral', + description: + 'Mistral 7B is a compact yet powerful language model by Mistral AI, great for general-purpose tasks.', + estimated_pulls: '26.8M', + id: 'a2b3c4d5-e6f7-4890-abcd-ef1234567890', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '6 months ago', + tags: [ + { + name: 'mistral:7b', + size: '4.1 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'gemma2', + description: + 'Gemma 2 is a family of lightweight, state-of-the-art open models by Google.', + estimated_pulls: '18.5M', + id: 'b3c4d5e6-f7a8-4901-bcde-f12345678901', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '8 months ago', + tags: [ + { + name: 'gemma2:2b', + size: '1.6 GB', + context: '8k', + input: 'Text', + cloud: false, + thinking: false, + }, + { + name: 'gemma2:9b', + size: '5.4 GB', + context: '8k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'phi3', + description: + 'Phi-3 is a family of small language models developed by Microsoft with strong reasoning capabilities.', + estimated_pulls: '12.4M', + id: 'c4d5e6f7-a8b9-4012-cdef-123456789012', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '10 months ago', + tags: [ + { + name: 'phi3:mini', + size: '2.3 GB', + context: '128k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'codellama', + description: + 'Code Llama is a model for generating and discussing code, built on top of Llama 2.', + estimated_pulls: '15.2M', + id: 'd5e6f7a8-b9c0-4123-defa-234567890123', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '1 year ago', + tags: [ + { + name: 'codellama:7b', + size: '3.8 GB', + context: '16k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'qwen2.5-coder', + description: + 'Qwen2.5-Coder is a series of code-specific large language models by Alibaba Cloud.', + estimated_pulls: '9.7M', + id: 'e6f7a8b9-c0d1-4234-efab-345678901234', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '4 months ago', + tags: [ + { + name: 'qwen2.5-coder:3b', + size: '1.9 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + { + name: 'qwen2.5-coder:7b', + size: '4.7 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'tinyllama', + description: + 'TinyLlama is a compact 1.1B language model ideal for resource-constrained environments.', + estimated_pulls: '5.3M', + id: 'f7a8b9c0-d1e2-4345-fabc-456789012345', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '1 year ago', + tags: [ + { + name: 'tinyllama:1.1b', + size: '638 MB', + context: '2k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'nomic-embed-text', + description: + 'A high-performing open embedding model with a large token context window.', + estimated_pulls: '22.1M', + id: 'a8b9c0d1-e2f3-4456-abcd-567890123456', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '9 months ago', + tags: [ + { + name: 'nomic-embed-text:latest', + size: '274 MB', + context: '8k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'qwen2.5', + description: + 'Qwen2.5 is a series of large language models by Alibaba Cloud with strong multilingual support.', + estimated_pulls: '14.6M', + id: 'b9c0d1e2-f3a4-4567-bcde-678901234567', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '5 months ago', + tags: [ + { + name: 'qwen2.5:3b', + size: '1.9 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + { + name: 'qwen2.5:7b', + size: '4.7 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'llava', + description: + 'LLaVA is a multimodal model that combines a vision encoder with Vicuna for visual and language understanding.', + estimated_pulls: '8.9M', + id: 'c0d1e2f3-a4b5-4678-cdef-789012345678', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '1 year ago', + tags: [ + { + name: 'llava:7b', + size: '4.7 GB', + context: '4k', + input: 'Text, Images', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'starcoder2', + description: + 'StarCoder2 is a family of code generation models trained on The Stack v2 dataset.', + estimated_pulls: '3.8M', + id: 'd1e2f3a4-b5c6-4789-defa-890123456789', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '1 year ago', + tags: [ + { + name: 'starcoder2:3b', + size: '1.7 GB', + context: '16k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'dolphin-mistral', + description: + 'Dolphin Mistral is an uncensored, fine-tuned Mistral model focused on helpfulness.', + estimated_pulls: '4.2M', + id: 'e2f3a4b5-c6d7-4890-efab-901234567890', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '1 year ago', + tags: [ + { + name: 'dolphin-mistral:7b', + size: '4.1 GB', + context: '32k', + input: 'Text', + cloud: false, + thinking: false, + }, + ], + }, + { + name: 'phi3.5', + description: + 'Phi-3.5 is an updated small language model by Microsoft with improved multilingual and reasoning capabilities.', + estimated_pulls: '6.1M', + id: 'f3a4b5c6-d7e8-4901-fabc-012345678901', + first_seen: '2026-01-28T23:37:31.000+00:00', + model_last_updated: '8 months ago', + tags: [ + { + name: 'phi3.5:3.8b', + size: '2.2 GB', + context: '128k', + input: 'Text', + cloud: false, + thinking: false, }, ], }, diff --git a/admin/inertia/lib/api.ts b/admin/inertia/lib/api.ts index a47f326..13c54c6 100644 --- a/admin/inertia/lib/api.ts +++ b/admin/inertia/lib/api.ts @@ -249,6 +249,7 @@ class API { const response = await this.client.get<{ models: NomadOllamaModel[] hasMore: boolean + source?: 'api' | 'fallback' }>('/ollama/models', { params: { sort: 'pulls', ...params }, }) diff --git a/admin/inertia/pages/settings/models.tsx b/admin/inertia/pages/settings/models.tsx index 2da3be2..837854f 100644 --- a/admin/inertia/pages/settings/models.tsx +++ b/admin/inertia/pages/settings/models.tsx @@ -131,12 +131,20 @@ export default function ModelsPage(props: { initialData: { models: props.models.availableModels, hasMore: false }, }) + const isUsingFallback = availableModelData?.source === 'fallback' + async function handleForceRefresh() { forceRefreshRef.current = true setIsForceRefreshing(true) - await refetch() + const result = await refetch() setIsForceRefreshing(false) - addNotification({ message: 'Model list refreshed from remote.', type: 'success' }) + const refreshSource = result.data?.source + addNotification({ + message: refreshSource === 'fallback' + ? 'Could not reach model service. Showing offline list.' + : 'Model list refreshed from remote.', + type: refreshSource === 'fallback' ? 'warning' : 'success', + }) } async function handleInstallModel(modelName: string) { @@ -312,6 +320,11 @@ export default function ModelsPage(props: { Refresh Models + {isUsingFallback && ( + + Showing offline model list. Connect to the internet and refresh for the full catalog. + + )} className="font-semibold mt-4" rowLines={true}