diff --git a/app/ai.py b/app/ai.py index 400b3fa..7d16318 100644 --- a/app/ai.py +++ b/app/ai.py @@ -223,6 +223,7 @@ async def translate_sentence_ai( "prompt": prompt, "stream": False, "format": "json", + "keep_alive": "24h", "options": { "temperature": 0.2, "num_predict": 300, @@ -234,7 +235,7 @@ async def translate_sentence_ai( source = f"ollama:{config.OLLAMA_MODEL}" try: - async with httpx.AsyncClient(timeout=35.0) as client: + async with httpx.AsyncClient(timeout=60.0) as client: resp = await client.post(f"{config.OLLAMA_BASE_URL}/api/generate", json=payload) if resp.status_code == 200: data = resp.json() @@ -282,7 +283,8 @@ async def translate_sentence_ai( else: logger.warning(f"Ollama returned HTTP {resp.status_code}: {resp.text}") except Exception as e: - logger.warning(f"Ollama connection error: {e}") + err_msg = f"{type(e).__name__}: {e}" if str(e) else type(e).__name__ + logger.warning(f"Ollama connection error ({err_msg})") # 4. Graceful Fallback if Ollama is unreachable if not sentence_ru: @@ -329,3 +331,22 @@ async def get_ollama_status() -> Dict[str, Any]: "models": [], "active_model": config.OLLAMA_MODEL, } + + +async def warmup_ollama_model() -> bool: + """Preloads the configured Ollama model into RAM with keep_alive to eliminate cold-start latency.""" + try: + async with httpx.AsyncClient(timeout=60.0) as client: + resp = await client.post( + f"{config.OLLAMA_BASE_URL}/api/generate", + json={"model": config.OLLAMA_MODEL, "keep_alive": "24h"}, + ) + if resp.status_code == 200: + logger.info(f"Ollama model '{config.OLLAMA_MODEL}' warmed up successfully (keep_alive=24h).") + return True + else: + logger.warning(f"Ollama warmup returned HTTP {resp.status_code}: {resp.text}") + except Exception as e: + err_msg = f"{type(e).__name__}: {e}" if str(e) else type(e).__name__ + logger.warning(f"Ollama warmup deferred ({err_msg})") + return False diff --git a/app/main.py b/app/main.py index cc7bb5e..1061a8c 100644 --- a/app/main.py +++ b/app/main.py @@ -35,7 +35,7 @@ from app.reader import ( fetch_and_parse_rfc, parse_rfc_text, fetch_and_parse_wikipedia, extract_wikipedia_title_and_lang ) -from app.ai import translate_sentence_ai, get_ollama_status +from app.ai import translate_sentence_ai, get_ollama_status, warmup_ollama_model from app.phrasal import detect_phrasal_verb from app.cefr import ( get_cefr_level_summaries, @@ -68,6 +68,7 @@ from app.miner import run_mining_task_background async def lifespan(app: FastAPI): init_db() ensure_admin_user() + asyncio.create_task(warmup_ollama_model()) yield await close_http_client() diff --git a/docker-compose.yml b/docker-compose.yml index 19c80a6..a1def3e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,4 +29,4 @@ services: volumes: - ./data/ollama:/root/.ollama environment: - - OLLAMA_KEEP_ALIVE=5m + - OLLAMA_KEEP_ALIVE=24h