Skip to content

Commit db69428

Browse files
committed
fix(AI): allow force refresh of models list
1 parent bc016e6 commit db69428

File tree

6 files changed

+45
-13
lines changed

6 files changed

+45
-13
lines changed

admin/app/controllers/ollama_controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default class OllamaController {
2222
recommendedOnly: reqData.recommendedOnly,
2323
query: reqData.query || null,
2424
limit: reqData.limit || 15,
25+
force: reqData.force,
2526
})
2627
}
2728

admin/app/services/ollama_service.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,15 @@ export class OllamaService {
183183
}
184184

185185
async getAvailableModels(
186-
{ sort, recommendedOnly, query, limit }: { sort?: 'pulls' | 'name'; recommendedOnly?: boolean, query: string | null, limit?: number } = {
186+
{ sort, recommendedOnly, query, limit, force }: { sort?: 'pulls' | 'name'; recommendedOnly?: boolean, query: string | null, limit?: number, force?: boolean } = {
187187
sort: 'pulls',
188188
recommendedOnly: false,
189189
query: null,
190190
limit: 15,
191191
}
192192
): Promise<{ models: NomadOllamaModel[], hasMore: boolean } | null> {
193193
try {
194-
const models = await this.retrieveAndRefreshModels(sort)
194+
const models = await this.retrieveAndRefreshModels(sort, force)
195195
if (!models) {
196196
// If we fail to get models from the API, return the fallback recommended models
197197
logger.warn(
@@ -244,13 +244,18 @@ export class OllamaService {
244244
}
245245

246246
private async retrieveAndRefreshModels(
247-
sort?: 'pulls' | 'name'
247+
sort?: 'pulls' | 'name',
248+
force?: boolean
248249
): Promise<NomadOllamaModel[] | null> {
249250
try {
250-
const cachedModels = await this.readModelsFromCache()
251-
if (cachedModels) {
252-
logger.info('[OllamaService] Using cached available models data')
253-
return this.sortModels(cachedModels, sort)
251+
if (!force) {
252+
const cachedModels = await this.readModelsFromCache()
253+
if (cachedModels) {
254+
logger.info('[OllamaService] Using cached available models data')
255+
return this.sortModels(cachedModels, sort)
256+
}
257+
} else {
258+
logger.info('[OllamaService] Force refresh requested, bypassing cache')
254259
}
255260

256261
logger.info('[OllamaService] Fetching fresh available models from API')

admin/app/validators/ollama.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export const getAvailableModelsSchema = vine.compile(
1919
recommendedOnly: vine.boolean().optional(),
2020
query: vine.string().trim().optional(),
2121
limit: vine.number().positive().optional(),
22+
force: vine.boolean().optional(),
2223
})
2324
)

admin/inertia/components/StyledButton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const StyledButton: React.FC<StyledButtonProps> = ({
2020
size = 'md',
2121
loading = false,
2222
fullWidth = false,
23+
className,
2324
...props
2425
}) => {
2526
const isDisabled = useMemo(() => {
@@ -152,7 +153,8 @@ const StyledButton: React.FC<StyledButtonProps> = ({
152153
getSizeClasses(),
153154
getVariantClasses(),
154155
isDisabled ? 'pointer-events-none opacity-60' : 'cursor-pointer',
155-
'items-center justify-center rounded-md font-semibold focus:outline-none focus:ring-2 focus:ring-desert-green-light focus:ring-offset-2 focus:ring-offset-desert-sand disabled:cursor-not-allowed disabled:shadow-none'
156+
'items-center justify-center rounded-md font-semibold focus:outline-none focus:ring-2 focus:ring-desert-green-light focus:ring-offset-2 focus:ring-offset-desert-sand disabled:cursor-not-allowed disabled:shadow-none',
157+
className
156158
)}
157159
{...props}
158160
disabled={isDisabled}

admin/inertia/lib/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class API {
197197
})()
198198
}
199199

200-
async getAvailableModels(params: { query?: string; recommendedOnly?: boolean; limit?: number }) {
200+
async getAvailableModels(params: { query?: string; recommendedOnly?: boolean; limit?: number; force?: boolean }) {
201201
return catchInternal(async () => {
202202
const response = await this.client.get<{
203203
models: NomadOllamaModel[]

admin/inertia/pages/settings/models.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Head, router, usePage } from '@inertiajs/react'
2-
import { useState } from 'react'
2+
import { useRef, useState } from 'react'
33
import StyledTable from '~/components/StyledTable'
44
import SettingsLayout from '~/layouts/SettingsLayout'
55
import { NomadOllamaModel } from '../../../types/ollama'
@@ -16,7 +16,7 @@ import Switch from '~/components/inputs/Switch'
1616
import StyledSectionHeader from '~/components/StyledSectionHeader'
1717
import { useMutation, useQuery } from '@tanstack/react-query'
1818
import Input from '~/components/inputs/Input'
19-
import { IconSearch } from '@tabler/icons-react'
19+
import { IconSearch, IconRefresh } from '@tabler/icons-react'
2020
import useDebounce from '~/hooks/useDebounce'
2121
import ActiveModelDownloads from '~/components/ActiveModelDownloads'
2222

@@ -47,13 +47,19 @@ export default function ModelsPage(props: {
4747
setQuery(val)
4848
}, 300)
4949

50-
const { data: availableModelData, isFetching } = useQuery({
50+
const forceRefreshRef = useRef(false)
51+
const [isForceRefreshing, setIsForceRefreshing] = useState(false)
52+
53+
const { data: availableModelData, isFetching, refetch } = useQuery({
5154
queryKey: ['ollama', 'availableModels', query, limit],
5255
queryFn: async () => {
56+
const force = forceRefreshRef.current
57+
forceRefreshRef.current = false
5358
const res = await api.getAvailableModels({
5459
query,
5560
recommendedOnly: false,
5661
limit,
62+
force: force || undefined,
5763
})
5864
if (!res) {
5965
return {
@@ -66,6 +72,14 @@ export default function ModelsPage(props: {
6672
initialData: { models: props.models.availableModels, hasMore: false },
6773
})
6874

75+
async function handleForceRefresh() {
76+
forceRefreshRef.current = true
77+
setIsForceRefreshing(true)
78+
await refetch()
79+
setIsForceRefreshing(false)
80+
addNotification({ message: 'Model list refreshed from remote.', type: 'success' })
81+
}
82+
6983
async function handleInstallModel(modelName: string) {
7084
try {
7185
const res = await api.downloadModel(modelName)
@@ -196,7 +210,7 @@ export default function ModelsPage(props: {
196210
<ActiveModelDownloads withHeader />
197211

198212
<StyledSectionHeader title="Models" className="mt-12 mb-4" />
199-
<div className="flex justify-start mt-4">
213+
<div className="flex justify-start items-center gap-3 mt-4">
200214
<Input
201215
name="search"
202216
label=""
@@ -209,6 +223,15 @@ export default function ModelsPage(props: {
209223
className="w-1/3"
210224
leftIcon={<IconSearch className="w-5 h-5 text-gray-400" />}
211225
/>
226+
<StyledButton
227+
variant="secondary"
228+
onClick={handleForceRefresh}
229+
icon="IconRefresh"
230+
loading={isForceRefreshing}
231+
className='mt-1'
232+
>
233+
Refresh Models
234+
</StyledButton>
212235
</div>
213236
<StyledTable<NomadOllamaModel>
214237
className="font-semibold mt-4"

0 commit comments

Comments
 (0)