fix: rename generation model setting to ollama_llm_model to avoid OLLAMA_MODEL env collision
The deployed container sets OLLAMA_MODEL=nomic-embed-text for embeddings, which shadowed the generation-model setting and broke Phase 0 keyword extraction and Phase 4 LLM re-ranking on every request. The setting is now ollama_llm_model (env: OLLAMA_LLM_MODEL, default gemma4:e2b), startup logs the resolved generation model, and Phase 0/Phase 4 LLM calls are wrapped in a 12s asyncio.wait_for with graceful fallback so a hung call cannot gate retrieval for the full 120s client timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@ SEARXNG_URL=http://192.168.86.149:8080
|
||||
REDIS_HOST=192.168.86.149
|
||||
PAPERLESS_URL=http://192.168.86.149:8091
|
||||
|
||||
OLLAMA_MODEL=mistral-nemo-large:latest
|
||||
OLLAMA_LLM_MODEL=gemma4:e2b
|
||||
OLLAMA_EMBEDDING_MODEL=nomic-embed-text
|
||||
|
||||
# Wiki.js auth
|
||||
|
||||
@@ -5,6 +5,13 @@ All notable changes to Library Desk will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Ollama generation model env collision** - Renamed the generation-model setting `ollama_model` to `ollama_llm_model` (env: `OLLAMA_LLM_MODEL`, default `gemma4:e2b`). The container env `OLLAMA_MODEL=nomic-embed-text` (meant for embeddings) was shadowing the generation model, breaking Phase 0 keyword extraction and Phase 4 LLM re-ranking on every request. Startup now logs the resolved generation model.
|
||||
- **LLM call timeouts** - Phase 0 keyword extraction and Phase 4 re-ranking are wrapped in a 12s `asyncio.wait_for` with graceful fallback, so a hung Ollama call can no longer gate retrieval for the full 120s client timeout.
|
||||
|
||||
## [1.7.3] - 2026-01-07
|
||||
|
||||
### Fixed
|
||||
|
||||
+3
-1
@@ -66,7 +66,9 @@ class Settings(BaseSettings):
|
||||
|
||||
# Ollama Configuration
|
||||
ollama_url: str = Field(default="http://ollama:11434", description="Ollama URL")
|
||||
ollama_model: str = Field(default="mistral-nemo-large:latest", description="Ollama LLM model")
|
||||
# Named ollama_llm_model (env: OLLAMA_LLM_MODEL) to avoid collision with the
|
||||
# OLLAMA_MODEL container env var, which is used for the embedding model.
|
||||
ollama_llm_model: str = Field(default="gemma4:e2b", description="Ollama LLM model for generation (keyword extraction, re-ranking, consolidation)")
|
||||
ollama_embedding_model: str = Field(default="nomic-embed-text", description="Ollama embedding model")
|
||||
|
||||
# HybridRAG Configuration
|
||||
|
||||
+2
-1
@@ -142,7 +142,7 @@ async def health(settings: Settings = Depends(get_settings)) -> HealthResponse:
|
||||
},
|
||||
"ollama": {
|
||||
"url": settings.ollama_url,
|
||||
"model": settings.ollama_model,
|
||||
"model": settings.ollama_llm_model,
|
||||
"healthy": service_health.get("ollama", False)
|
||||
}
|
||||
}
|
||||
@@ -412,6 +412,7 @@ async def startup_event():
|
||||
logger.info(f"Wiki.js: {settings.wikijs_url}")
|
||||
logger.info(f"SearXNG: {settings.searxng_url}")
|
||||
logger.info(f"Ollama: {settings.ollama_url}")
|
||||
logger.info(f"Ollama generation model: {settings.ollama_llm_model} (embedding model: {settings.ollama_embedding_model})")
|
||||
|
||||
# Initialize all service clients
|
||||
await startup_clients()
|
||||
|
||||
@@ -505,7 +505,7 @@ JSON:"""
|
||||
# Call Ollama for analysis (temperature=0.0 for consistent classification)
|
||||
response = await self.ollama.generate_text(
|
||||
prompt=prompt,
|
||||
model=self.settings.ollama_model,
|
||||
model=self.settings.ollama_llm_model,
|
||||
stream=False,
|
||||
temperature=0.0
|
||||
)
|
||||
@@ -1079,7 +1079,7 @@ JSON:"""
|
||||
try:
|
||||
response = await self.ollama.generate_text(
|
||||
prompt=prompt,
|
||||
model=self.settings.ollama_model,
|
||||
model=self.settings.ollama_llm_model,
|
||||
stream=False,
|
||||
temperature=0.0
|
||||
)
|
||||
|
||||
@@ -34,6 +34,10 @@ from src.core.multi_tenancy import get_neo4j_user_base_label, get_neo4j_user_lab
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Timeout for auxiliary LLM calls (keyword extraction, re-ranking).
|
||||
# A hung Ollama call must not gate retrieval for the full client timeout.
|
||||
LLM_CALL_TIMEOUT_SECONDS = 12.0
|
||||
|
||||
|
||||
class HybridRAGService:
|
||||
"""
|
||||
@@ -69,7 +73,7 @@ class HybridRAGService:
|
||||
self.content_extractor = content_extractor
|
||||
self.settings = settings
|
||||
self.volatile = volatile_service
|
||||
self.reranker_model = settings.ollama_model
|
||||
self.reranker_model = settings.ollama_llm_model
|
||||
|
||||
async def search(
|
||||
self,
|
||||
@@ -224,10 +228,13 @@ Return format:
|
||||
JSON:"""
|
||||
|
||||
try:
|
||||
response = await self.ollama.generate_text(
|
||||
response = await asyncio.wait_for(
|
||||
self.ollama.generate_text(
|
||||
prompt=prompt,
|
||||
model=self.reranker_model,
|
||||
temperature=0.0 # Deterministic for consistent extraction
|
||||
),
|
||||
timeout=LLM_CALL_TIMEOUT_SECONDS
|
||||
)
|
||||
|
||||
# Parse JSON response (handle potential extra text)
|
||||
@@ -261,6 +268,16 @@ JSON:"""
|
||||
"synonyms": {},
|
||||
"expansions": {}
|
||||
}
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(
|
||||
f"Keyword extraction timed out after {LLM_CALL_TIMEOUT_SECONDS}s, using fallback"
|
||||
)
|
||||
return {
|
||||
"core_keywords": query.split(),
|
||||
"entities": [],
|
||||
"synonyms": {},
|
||||
"expansions": {}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Keyword extraction failed: {e}", exc_info=True)
|
||||
return {
|
||||
@@ -772,10 +789,13 @@ Example output: 3,1,5,2,4
|
||||
|
||||
Ranking:"""
|
||||
|
||||
response = await self.ollama.generate_text(
|
||||
response = await asyncio.wait_for(
|
||||
self.ollama.generate_text(
|
||||
prompt=prompt,
|
||||
model=self.reranker_model,
|
||||
temperature=0.0 # Deterministic for consistent rankings
|
||||
),
|
||||
timeout=LLM_CALL_TIMEOUT_SECONDS
|
||||
)
|
||||
|
||||
# Parse response: "3,1,5,2,4" → [2, 0, 4, 1, 3] (0-indexed)
|
||||
@@ -796,6 +816,11 @@ Ranking:"""
|
||||
logger.info(f"LLM re-ranking: reordered {len(reranked)} results")
|
||||
return reranked
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(
|
||||
f"LLM re-ranking timed out after {LLM_CALL_TIMEOUT_SECONDS}s, using RRF order"
|
||||
)
|
||||
return results # Fallback to RRF order
|
||||
except Exception as e:
|
||||
logger.warning(f"LLM re-ranking failed: {e}, using RRF order")
|
||||
return results # Fallback to RRF order
|
||||
|
||||
@@ -34,7 +34,7 @@ class WikiPageWriter:
|
||||
settings: Application settings
|
||||
"""
|
||||
self.ollama = ollama_client
|
||||
self.model = settings.ollama_model
|
||||
self.model = settings.ollama_llm_model
|
||||
|
||||
async def create_page(
|
||||
self,
|
||||
|
||||
@@ -38,7 +38,7 @@ def settings():
|
||||
"""Get mocked application settings for testing."""
|
||||
mock_settings = MagicMock()
|
||||
mock_settings.reranker_model = "mistral-nemo"
|
||||
mock_settings.ollama_model = "mistral-nemo"
|
||||
mock_settings.ollama_llm_model = "mistral-nemo"
|
||||
return mock_settings
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user