feat: keep Ollama models in memory for 5 minutes

- Add OLLAMA_KEEP_ALIVE_MINUTES config (default: 5)
- schedule_ollama_unload(): delays Ollama unload when switching to Jean
- cancel_ollama_unload(): cancels timer if returning to Ollama
- Ollama models stay in VRAM for X minutes after last use
- Timer resets on each Ollama request
This commit is contained in:
Folzo
2026-07-20 10:46:58 +02:00
parent 8b7c4fbd9c
commit 20767c2300
+38 -4
View File
@@ -24,6 +24,7 @@ OLLAMA_DIRECT_HOST = os.getenv("OLLAMA_DIRECT_HOST", JEAN_WEB_HOST)
OLLAMA_DIRECT_PORT = int(os.getenv("OLLAMA_DIRECT_PORT", "11434")) OLLAMA_DIRECT_PORT = int(os.getenv("OLLAMA_DIRECT_PORT", "11434"))
ENABLE_OLLAMA = os.getenv("ENABLE_OLLAMA", "true").lower() == "true" ENABLE_OLLAMA = os.getenv("ENABLE_OLLAMA", "true").lower() == "true"
ENABLE_VRAM_MANAGEMENT = os.getenv("ENABLE_VRAM_MANAGEMENT", "true").lower() == "true" ENABLE_VRAM_MANAGEMENT = os.getenv("ENABLE_VRAM_MANAGEMENT", "true").lower() == "true"
OLLAMA_KEEP_ALIVE_MINUTES = int(os.getenv("OLLAMA_KEEP_ALIVE_MINUTES", "5"))
LISTEN_PORT = int(os.getenv("LISTEN_PORT", "3014")) LISTEN_PORT = int(os.getenv("LISTEN_PORT", "3014"))
SWITCH_TIMEOUT = int(os.getenv("SWITCH_TIMEOUT", "90")) SWITCH_TIMEOUT = int(os.getenv("SWITCH_TIMEOUT", "90"))
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "2")) CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "2"))
@@ -41,6 +42,7 @@ CACHE_TTL = 30
switch_lock = threading.Lock() switch_lock = threading.Lock()
vram_lock = threading.Lock() vram_lock = threading.Lock()
vram_state = {"active_backend": "jean"} vram_state = {"active_backend": "jean"}
ollama_unload_timer = None
def get_presets(force_refresh=False): def get_presets(force_refresh=False):
@@ -246,11 +248,41 @@ def unload_ollama() -> bool:
return True return True
def schedule_ollama_unload():
"""Programme le déchargement d'Ollama après OLLAMA_KEEP_ALIVE_MINUTES"""
global ollama_unload_timer
def delayed_unload():
log.info(f"[VRAM] Timer écoulé ({OLLAMA_KEEP_ALIVE_MINUTES}min), déchargement Ollama...")
unload_ollama()
if ollama_unload_timer:
ollama_unload_timer.cancel()
log.info("[VRAM] Timer précédent annulé")
ollama_unload_timer = threading.Timer(
OLLAMA_KEEP_ALIVE_MINUTES * 60,
delayed_unload
)
ollama_unload_timer.daemon = True
ollama_unload_timer.start()
log.info(f"[VRAM] Déchargement Ollama programmé dans {OLLAMA_KEEP_ALIVE_MINUTES} minutes")
def cancel_ollama_unload():
"""Annule le déchargement programmé d'Ollama"""
global ollama_unload_timer
if ollama_unload_timer:
ollama_unload_timer.cancel()
ollama_unload_timer = None
log.info("[VRAM] Timer de déchargement Ollama annulé")
def ensure_vram_for_backend(target_backend: str) -> bool: def ensure_vram_for_backend(target_backend: str) -> bool:
""" """
S'assure que la VRAM est disponible pour le backend cible. S'assure que la VRAM est disponible pour le backend cible.
- Si target=ollama et jean actif → arrête Jean - Si target=ollama et jean actif → arrête Jean, annule timer unload Ollama
- Si target=jean et ollama actif → décharge Ollama - Si target=jean et ollama actif → programme déchargement Ollama dans X min, démarre Jean
""" """
if not ENABLE_VRAM_MANAGEMENT: if not ENABLE_VRAM_MANAGEMENT:
return True return True
@@ -263,6 +295,7 @@ def ensure_vram_for_backend(target_backend: str) -> bool:
if target_backend == "ollama" and current == "jean": if target_backend == "ollama" and current == "jean":
log.info("[VRAM] Switch Jean → Ollama") log.info("[VRAM] Switch Jean → Ollama")
cancel_ollama_unload()
if not stop_jean(): if not stop_jean():
return False return False
vram_state["active_backend"] = "ollama" vram_state["active_backend"] = "ollama"
@@ -270,8 +303,7 @@ def ensure_vram_for_backend(target_backend: str) -> bool:
elif target_backend == "jean" and current == "ollama": elif target_backend == "jean" and current == "ollama":
log.info("[VRAM] Switch Ollama → Jean") log.info("[VRAM] Switch Ollama → Jean")
if not unload_ollama(): schedule_ollama_unload()
return False
if not start_jean(): if not start_jean():
return False return False
vram_state["active_backend"] = "jean" vram_state["active_backend"] = "jean"
@@ -384,6 +416,8 @@ def chat_completions():
if not ensure_vram_for_backend("ollama"): if not ensure_vram_for_backend("ollama"):
return jsonify({"error": "Failed to prepare VRAM for Ollama"}), 503 return jsonify({"error": "Failed to prepare VRAM for Ollama"}), 503
cancel_ollama_unload()
upstream_url = f"http://{OLLAMA_PROXY_HOST}:{OLLAMA_PROXY_PORT}/v1/chat/completions" upstream_url = f"http://{OLLAMA_PROXY_HOST}:{OLLAMA_PROXY_PORT}/v1/chat/completions"
log.info(f"[Router] → Ollama ({model_id})") log.info(f"[Router] → Ollama ({model_id})")
else: else: