Fix chargement modèles : préchargement avec les mêmes options + alerte VRAM

Recherche : un modèle plus gros que la VRAM (ex. qwen3.6:35b-a3b ~20 Go sur
16 Go) déborde sur le CPU et charge lentement ; et le préchargement chargeait
un runner aux options par défaut, aussitôt rechargé par le 1er message avec un
num_ctx différent — double chargement d'un gros modèle.

- ollama_client.warm : accepte et transmet les options de génération.
- routes/models : start_model_warm calcule les options depuis la config du
  modèle (ollama_options) et les passe au warm -> runner identique au chat,
  plus de rechargement. Après chargement, détecte le placement via /api/ps
  (gpu/cpu/mixte + %) et l'expose dans l'état de warm.
- Frontend : warmModel renvoie l'état ; si le modèle se charge sur CPU/mixte,
  message d'alerte clair (trop gros pour la VRAM, choisir plus petit/quant).

Tests : options transmises au warm, placement 'mixte 65%' détecté, builds OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
This commit is contained in:
Claude committed 2026-07-15 23:26:46 +00:00
1 parent f174c8d58d
commit ffac092a7f
4 files changed
+64 -12

No files matched your search

+7 -2
View File
@@ -99,6 +99,8 @@ async function apiError(res: Response, fallback: string): Promise<Error> {
interface WarmStatus {
state: "idle" | "loading" | "loaded" | "error";
error?: string;
processor?: "gpu" | "cpu" | "mixte";
gpu_percent?: string;
}
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -107,7 +109,10 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
* Lance le préchargement en arrière-plan puis suit son état. Chaque requête
* reste courte afin qu'un reverse proxy ne puisse plus interrompre le warm-up.
*/
export async function warmModel(name: string, keepAlive = "30m"): Promise<void> {
export async function warmModel(
name: string,
keepAlive = "30m"
): Promise<WarmStatus> {
const res = await fetch("/api/models/warm", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -126,7 +131,7 @@ export async function warmModel(name: string, keepAlive = "30m"): Promise<void>
throw await apiError(statusRes, `suivi du préchargement refusé (${statusRes.status})`);
}
const status = (await statusRes.json()) as WarmStatus;
if (status.state === "loaded") return;
if (status.state === "loaded") return status;
if (status.state === "error") {
throw new Error(status.error ?? "préchargement impossible");
}
+13 -1
View File
@@ -159,8 +159,20 @@ export const useStore = create<LokiState>((set, get) => ({
// La configuration est propre au modèle : on attend son chargement avant
// d'utiliser keep_alive, sinon la valeur du modèle précédent est envoyée.
const ka = get().config?.keep_alive ?? "30m";
await warmModel(name, ka);
const st = await warmModel(name, ka);
await get().refreshLoadedModels();
// Modèle chargé hors GPU : prévenir (souvent trop gros pour la VRAM).
if (
(st.processor === "cpu" || st.processor === "mixte") &&
get().selectedModel === name
) {
set({
warmError:
st.processor === "cpu"
? "Modèle chargé sur le CPU (trop gros pour la VRAM) — lent. Choisis un modèle plus petit ou une quantization plus légère."
: `Modèle en partie sur GPU (${st.gpu_percent ?? "?"}%) : dépasse la VRAM, chargement plus lent.`,
});
}
} catch (err) {
if (get().selectedModel === name) {
set({