Charger tous les modèles du routeur llama.cpp sans redémarrage

Libérer de la VRAM sans interrompre llama-server.

Sommaire

Mode routeur de llama.cpp est l’un des changements les plus utiles apportés à llama-server depuis des années. Il offre enfin aux opérateurs de LLM locaux une expérience de gestion des modèles proche de celle attendue d’Ollama, tout en conservant les performances brutes et le contrôle de bas niveau qui rendent llama.cpp intéressant à utiliser en premier lieu.

Mais il y a un point délicat : décharger tout n’est pas un simple bouton magique dans l’API HTTP.

Le routeur peut lister les modèles. Il peut charger un modèle. Il peut décharger un modèle. Il peut évacuer le modèle le moins récemment utilisé lorsque l’option --models-max est atteinte. Ce qu’il ne documente pas actuellement comme une fonctionnalité de premier plan, c’est un appel universel de type décharger tous les modèles maintenant.

Ordinateur portable et de bureau avec des LLM

Ce n’est pas un véritable obstacle. La méthode correcte est simple, explicite et automatisable :

  1. Demander au routeur quels modèles existent.
  2. Filtrer les modèles dont le statut est loaded (chargé).
  3. Appeler /models/unload une fois pour chaque modèle chargé.

C’est l’approche que je recommande pour les flux de travail locaux de LLM sérieuses. C’est ennuyeux, visible et facile à déboguer. C’est exactement ce que vous voulez lorsque votre objectif est de libérer la VRAM sans redémarrer tout le service d’inférence.

Ce que fait réellement le mode routeur de llama.cpp

Dans l’utilisation classique de llama-server, vous démarrez un serveur avec un seul modèle :

llama-server \
  --model ./models/qwen3-8b.gguf \
  --port 8080

Le mode routeur change cela. Au lieu de lier le serveur à un seul fichier GGUF, le routeur devient un coordinateur pour plusieurs modèles. Il peut découvrir des modèles dans un cache ou dans un répertoire de modèles, les charger sur demande, router les requêtes vers le bon modèle et décharger les modèles lorsque nécessaire.

Un démarrage typique en mode routeur ressemble à ceci :

llama-server \
  --models-dir ./models \
  --models-max 4 \
  --port 8080

L’option importante ici est --models-max. Elle contrôle le nombre de modèles qui peuvent être chargés simultanément. Si la limite est atteinte, llama.cpp peut évacuer le modèle le moins récemment utilisé. C’est utile, mais ce n’est pas un substitut à une opération de déchargement délibérée. L’évacuation LRU (Least Recently Used) est réactive. Un script de déchargement est un contrôle opérationnel.

Mon avis personnel : si vous utilisez des modèles locaux pour un travail réel, vous devez traiter le mode routeur comme un gestionnaire de processus d’inférence, et non comme un serveur de chat jouet. Les opérations de cycle de vie explicites comptent.

Les points de terminaison de gestion des modèles dont vous avez besoin

Le point de terminaison principal pour la découverte est :

curl -s http://localhost:8080/models | jq

Ce point de terminaison renvoie les modèles connus du routeur et leur statut de cycle de vie actuel. La structure JSON exacte peut varier légèrement entre les versions, donc inspectez votre propre réponse avant de créer une automatisation.

Une forme de réponse courante ressemble à ceci :

{
  "data": [
    {
      "id": "qwen3-8b",
      "status": "loaded"
    },
    {
      "id": "llama-3.2-3b",
      "status": "unloaded"
    }
  ]
}

Pour décharger un modèle, appelez :

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

C’est l’opération primitive. Tout le reste de cet article se construit là-dessus.

Il n’y a pas de point de terminaison “décharger tout” documenté

C’est la partie qui fait trébucher les gens.

Vous pourriez vous attendre à quelque chose comme ceci :

curl -X POST http://localhost:8080/models/unload-all

Ne construisez pas votre logique autour de cette hypothèse. L’opération documentée est par modèle. Vous passez un identifiant de modèle à /models/unload, et llama.cpp décharge ce modèle unique.

Ce n’est pas nécessairement une mauvaise conception d’API. Une opération par modèle est plus sûre. Elle permet à l’appelant de décider ce qui doit être déchargé. Elle évite également un comportement de production surprenant où une seule requête d’administrateur tuerait accidentellement tous les modèles chauds utilisés par d’autres clients.

Pour une station de travail, un raccourci “décharger tout” serait pratique. Pour une boîte d’inférence multi-utilisateurs, des boucles explicites sont meilleures.

Déchargez d’abord un seul modèle

Avant d’automatiser quoi que ce soit, testez l’identifiant de modèle exact que votre routeur attend.

Listez d’abord les modèles :

curl -s http://localhost:8080/models | jq

Choisissez un modèle chargé dans la sortie, puis déchargez-le :

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

Vérifiez à nouveau la liste des modèles :

curl -s http://localhost:8080/models | jq

Si le statut du modèle passe à unloaded (déchargé), votre point de terminaison, port et identifiant de modèle sont corrects.

Si cela ne fonctionne pas, ne devinez pas. Inspectez le JSON. Les alias du routeur, les noms de fichiers GGUF et les identifiants de modèle ne sont souvent pas la même chaîne de caractères.

Décharger tous les modèles chargés avec curl et jq

Une fois que le déchargement d’un seul modèle fonctionne, le modèle “décharger tout” n’est qu’une boucle shell.

Utilisez ceci lorsque votre réponse /models a .data[].id et .data[].status :

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Déchargement : $model"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "{\"model\":\"$model\"}" \
      | jq
  done

C’est tout le tour. Ce n’est pas glamour, mais c’est la bonne forme pour une opération d’administration :

  • Il ne décharge que les modèles qui sont réellement chargés.
  • Il affiche ce qu’il fait.
  • Il échoue modèle par modèle au lieu de tout cacher derrière une seule action opaque.
  • Il fonctionne depuis cron, les crochets systemd, SSH ou les tâches CI.

Un script réutilisable pour une utilisation en production

Pour tout ce que vous exécutez plus de deux fois, arrêtez de copier-coller des commandes en une ligne. Enregistrez un script.

Créez llama-router-unload-all.sh :

#!/usr/bin/env bash
set -euo pipefail

LLAMA_SERVER_URL="${LLAMA_SERVER_URL:-http://localhost:8080}"

models_json="$(curl -fsS "$LLAMA_SERVER_URL/models")"

loaded_models="$(printf '%s' "$models_json" \
  | jq -r '.data[] | select(.status == "loaded") | .id')"

if [ -z "$loaded_models" ]; then
  echo "Aucun modèle chargé trouvé."
  exit 0
fi

printf '%s\n' "$loaded_models" | while IFS= read -r model; do
  [ -z "$model" ] && continue

  echo "Déchargement : $model"

  curl -fsS -X POST "$LLAMA_SERVER_URL/models/unload" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model\"}" \
    | jq

done

echo "Terminé. État actuel des modèles :"
curl -fsS "$LLAMA_SERVER_URL/models" | jq

Rendez-le exécutable :

chmod +x llama-router-unload-all.sh

Exécutez-le contre le serveur local par défaut :

./llama-router-unload-all.sh

Exécutez-le contre un autre hôte :

LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh

C’est la version que je garderais réellement dans un répertoire d’outils. Elle utilise curl -f pour que les erreurs HTTP échouent le script, et elle vous permet de remplacer l’URL du serveur sans modifier le fichier.

Adapter le script à votre structure JSON

Ne supposez pas aveuglément que chaque build de llama.cpp retournera exactement les mêmes champs pour toujours. Le mode routeur évolue encore, et votre build peut exposer une structure JSON légèrement différente.

Commencez par inspecter la réponse :

curl -s http://localhost:8080/models | jq

Le script utilise ce filtre :

jq -r '.data[] | select(.status == "loaded") | .id'

Si votre identifiant de modèle est dans .name, changez-le en :

jq -r '.data[] | select(.status == "loaded") | .name'

Si votre champ de statut utilise une autre valeur, ajustez le filtre en conséquence. Le principe est ce qui compte : sélectionnez les modèles chargés, extrayez l’identifiant accepté par /models/unload, puis appelez le déchargement pour chacun.

Pourquoi les modèles peuvent se recharger après votre déchargement

C’est la source de confusion la plus courante.

Le mode routeur prend en charge le chargement sur demande. Si un client envoie une requête de complétion de chat pour un modèle qui est actuellement déchargé, le routeur peut le recharger automatiquement.

Cela signifie que cette séquence est possible :

  1. Vous déchargez tous les modèles.
  2. Open WebUI, un script de test ou un agent envoie une requête.
  3. llama.cpp recharge le modèle demandé.
  4. Vous pensez que le déchargement a échoué, mais ce n’est pas le cas.

La solution est opérationnelle, pas technique. Arrêtez d’abord le trafic des clients si votre objectif est de garder la VRAM libre.

Par exemple :

  • Arrêtez les scripts de benchmark.
  • Mettez en pause les agents et les tâches cron.
  • Fermez ou déconnectez les sessions Open WebUI.
  • Désactivez les vérifications de santé qui effectuent accidentellement de vraies requêtes de modèle.

Le déchargement n’est pas un pare-feu. Si les clients continuent de demander des modèles, le mode routeur fait son travail en les servant.

Open WebUI et le bouton Éjection

Open WebUI peut s’intégrer avec le support de déchargement de modèles de llama.cpp. Lorsque le fournisseur est configuré comme llama.cpp, Open WebUI peut afficher l’état des modèles chargés et exposer une action “Éjecter” pour les administrateurs.

Sous le capot, cette action appelle l’API de déchargement d’Open WebUI, qui appelle ensuite le point de terminaison /models/unload de llama.cpp sur la connexion configurée.

C’est agréable pour une opération manuelle, mais je garderais quand même le script shell. Un bouton d’interface est pratique. Un script est auditable, répétable et utilisable sur une boîte sans tête à 2 heures du matin.

Quand utiliser “décharger tout”

Décharger tous les modèles chargés est utile lorsque vous voulez :

  • Libérer la mémoire GPU avant de démarrer un modèle plus grand.
  • Réinitialiser une boîte de développement sans redémarrer llama-server.
  • Vous préparer à une exécution de benchmark avec un état mémoire propre.
  • Vider les charges de travail d’inférence locales avant la maintenance.
  • Vous remettre d’une session confuse où trop de modèles étaient réchauffés.

Ce n’est pas le bon outil lorsque des utilisateurs actifs dépendent de modèles chauds. Dans ce cas, ajustez --models-max, utilisez un routage délibéré et laissez l’évacuation LRU faire une partie du travail. Si vous avez besoin d’un déchargement basé sur les délais plus intelligent avec un contrôle de cycle de vie par modèle, llama-swap est un proxy conçu spécifiquement pour superposer exactement cela sur toute configuration llama-server.

Ma règle est simple : utilisez LRU pour la pression normale, utilisez le déchargement explicite pour l’intention de l’opérateur.

Dépannage

Le point de terminaison des modèles renvoie 404

Vous n’avez peut-être pas une build capable de routeur, ou vous appelez peut-être le mauvais port.

Vérifiez le processus serveur et les options disponibles :

llama-server --help | grep -i models

Testez ensuite les deux points de terminaison :

curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq

Le point de terminaison /v1/models est la liste des modèles compatible OpenAI. Le point de terminaison /models est le point de terminaison de gestion des modèles du routeur. Ils sont liés, mais ce n’est pas la même chose.

jq n’est pas installé

Installez-le avant de scripter l’analyse JSON.

Sur Ubuntu ou Debian :

sudo apt-get update
sudo apt-get install jq

Sur macOS avec Homebrew :

brew install jq

L’appel de déchargement renvoie une erreur

La plupart des échecs proviennent de l’envoi du mauvais identifiant de modèle. Utilisez l’identifiant exact renvoyé par /models, pas le nom de fichier que vous pensez devoir fonctionner.

Vérifiez également si votre nom de modèle contient des guillemets, des barres obliques ou des espaces. Le script ci-dessus gère bien les chaînes normales, mais les noms inhabituels peuvent nécessiter une construction JSON plus soigneuse.

Pour une sécurité maximale, vous pouvez construire le corps POST avec jq :

jq -n --arg model "$model" '{model: $model}'

Une boucle de déchargement plus défensive utiliserait ce corps au lieu d’un JSON échappé manuellement.

La VRAM n’est pas libérée immédiatement

Confirmez d’abord que le statut du modèle a changé. Vérifiez ensuite si une autre requête l’a rechargé. Rappelez-vous également que les outils de mémoire GPU peuvent être en retard ou signaler le comportement de l’allocateur plutôt que l’intention instantanée au niveau de l’application.

Le test pratique est simple : arrêtez le trafic, déchargez les modèles, listez le statut des modèles, puis inspectez la mémoire GPU. Pour une utilisation VRAM mesurée à travers des tailles de modèles et des fenêtres de contexte sur llama.cpp, les benchmarks llama.cpp pour 16 Go de VRAM donnent des chiffres concrets pour vérifier la cohérence.

Une version plus sûre du corps JSON

Si vos identifiants de modèles contiennent des caractères inhabituels, utilisez jq pour générer le corps de la requête JSON :

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Déchargement : $model"
    body="$(jq -n --arg model "$model" '{model: $model}')"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "$body" \
      | jq
  done

C’est la version à utiliser si vos modèles sont nommés avec des identifiants de style dépôt, des alias personnalisés ou des chemins.

Conclusion

Le mode routeur de llama.cpp est un grand pas en avant pour les opérations locales de LLM. Il vous offre un chargement dynamique, un basculement de modèles et une évacution sensible à la mémoire sans renoncer à la directitude de llama-server.

Mais n’attendez pas un point de terminaison “décharger tout” parfait. La solution propre existe déjà : listez les modèles chargés et déchargez-les un par un.

Ce modèle est explicite. Il est scriptable. Il fonctionne via SSH. Il s’intègre bien avec Open WebUI. Et surtout, il libère la VRAM sans redémarrer le routeur.

Pour l’infrastructure IA locale, c’est exactement le genre de surface de contrôle ennuyeuse que vous voulez.

S'abonner

Recevez de nouveaux articles sur les systèmes, l'infrastructure et l'ingénierie IA.