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}