From cac80f6af0fa034310414f54f152735d25b16579 Mon Sep 17 00:00:00 2001 From: James Pine Date: Fri, 13 Mar 2026 04:44:13 -0700 Subject: [PATCH] feat: add per-model unload endpoint and UI button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /models/{model_name}/unload — unloads a specific model from memory without deleting from disk, supports all engine types - Frontend: Unload button in model detail dialog when model is loaded - Delete button remains disabled while loaded (unload first) --- .../ServerSettings/ModelManagement.tsx | 82 ++++++++++++++----- app/src/lib/api/client.ts | 6 ++ backend/main.py | 64 ++++++++++++++- 3 files changed, 131 insertions(+), 21 deletions(-) diff --git a/app/src/components/ServerSettings/ModelManagement.tsx b/app/src/components/ServerSettings/ModelManagement.tsx index bcc944b2..9811f643 100644 --- a/app/src/components/ServerSettings/ModelManagement.tsx +++ b/app/src/components/ServerSettings/ModelManagement.tsx @@ -13,6 +13,7 @@ import { RotateCcw, Scale, Trash2, + Unplug, X, } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; @@ -300,6 +301,27 @@ export function ModelManagement() { }, }); + const unloadMutation = useMutation({ + mutationFn: async (modelName: string) => { + return await apiClient.unloadModel(modelName); + }, + onSuccess: async (_data, modelName) => { + toast({ + title: 'Model unloaded', + description: `${modelName} has been unloaded from memory.`, + }); + await queryClient.invalidateQueries({ queryKey: ['modelStatus'], refetchType: 'all' }); + await queryClient.refetchQueries({ queryKey: ['modelStatus'] }); + }, + onError: (error: Error) => { + toast({ + title: 'Unload failed', + description: error.message, + variant: 'destructive', + }); + }, + }); + const formatSize = (sizeMb?: number): string => { if (!sizeMb) return 'Unknown size'; if (sizeMb < 1024) return `${sizeMb.toFixed(1)} MB`; @@ -697,26 +719,46 @@ export function ModelManagement() { ) : freshSelectedModel.downloaded ? ( - +
+ {freshSelectedModel.loaded && ( + + )} + +
) : (