Add SchedulerClient to communicate with external scheduler service for registering volatile prefetch tasks discovered during HybridRAG searches. - Add scheduler_client.py with full REST API for task CRUD operations - Add scheduler_url config setting (default: http://scheduler:8090) - Update consolidation service to use scheduler for prefetch registration - Add scheduler health checks to startup/shutdown lifecycle When HybridRAG classifies web content as prefetch-worthy, it now creates scheduled tasks that periodically refresh the volatile cache via the external scheduler service. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
162 lines
8.2 KiB
Python
162 lines
8.2 KiB
Python
"""
|
|
Application configuration using Pydantic Settings.
|
|
Following best practices: modular settings, environment-based config.
|
|
"""
|
|
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
# Read version from pyproject.toml
|
|
try:
|
|
import tomllib
|
|
_pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
|
|
with open(_pyproject_path, "rb") as f:
|
|
_pyproject = tomllib.load(f)
|
|
__version__ = _pyproject["project"]["version"]
|
|
except Exception:
|
|
__version__ = "0.0.0" # Fallback if pyproject.toml not found
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings loaded from environment variables."""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
case_sensitive=False,
|
|
extra="ignore",
|
|
)
|
|
|
|
# API Configuration
|
|
library_api_key: str = Field(..., description="API key for authentication")
|
|
|
|
# Neo4j Configuration
|
|
neo4j_uri: str = Field(default="bolt://neo4j:7687", description="Neo4j Bolt URI")
|
|
neo4j_user: str = Field(default="neo4j", description="Neo4j username")
|
|
neo4j_password: str = Field(..., description="Neo4j password")
|
|
|
|
# Qdrant Configuration
|
|
qdrant_host: str = Field(default="qdrant", description="Qdrant host")
|
|
qdrant_port: int = Field(default=6333, description="Qdrant port")
|
|
|
|
# Wiki.js Configuration
|
|
wikijs_url: str = Field(default="http://wiki:3000", description="Wiki.js URL")
|
|
wiki_graphql_api: str = Field(default="", description="Wiki.js GraphQL API token (optional - API may be open)")
|
|
# Legacy auth fields - kept for backwards compatibility but deprecated
|
|
wikijs_username: str = Field(default="", description="Wiki.js username (deprecated, use wiki_graphql_api)")
|
|
wikijs_password: str = Field(default="", description="Wiki.js password (deprecated, use wiki_graphql_api)")
|
|
|
|
# Wiki.js Database Configuration (for change listener)
|
|
wikijs_db_host: str = Field(default="postgres-shared", description="Wiki.js PostgreSQL host")
|
|
wikijs_db_port: int = Field(default=5432, description="Wiki.js PostgreSQL port")
|
|
wikijs_db_name: str = Field(default="library", description="Wiki.js database name")
|
|
wikijs_db_user: str = Field(default="library_desk_listener", description="Wiki.js database user (read-only)")
|
|
wikijs_db_password: str = Field(..., description="Wiki.js database password")
|
|
wikijs_change_listener_debounce_seconds: int = Field(
|
|
default=5,
|
|
ge=1,
|
|
le=60,
|
|
description="Debounce duration to prevent processing duplicate notifications"
|
|
)
|
|
|
|
# SearXNG Configuration
|
|
searxng_url: str = Field(default="http://searxng:8080", description="SearXNG URL")
|
|
|
|
# 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")
|
|
ollama_embedding_model: str = Field(default="nomic-embed-text", description="Ollama embedding model")
|
|
|
|
# HybridRAG Configuration
|
|
reranker_enabled: bool = Field(default=True, description="Enable LLM re-ranking")
|
|
hybrid_rag_vector_limit: int = Field(default=10, ge=1, le=50, description="Vector search limit")
|
|
hybrid_rag_graph_limit: int = Field(default=10, ge=1, le=50, description="Graph search limit")
|
|
hybrid_rag_web_limit: int = Field(default=5, ge=1, le=20, description="Web search limit")
|
|
vector_similarity_threshold: float = Field(default=0.7, ge=0.0, le=1.0, description="Minimum similarity score for vector results")
|
|
|
|
# Entity Linking Fuzzy Matching Configuration
|
|
entity_linking_min_confidence: float = Field(default=0.70, ge=0.0, le=1.0, description="Minimum confidence for entity-document matching")
|
|
entity_linking_min_entity_length: int = Field(default=5, ge=1, le=50, description="Minimum entity name length for matching")
|
|
entity_linking_min_containment_ratio: float = Field(default=0.30, ge=0.0, le=1.0, description="Minimum containment ratio for substring matching")
|
|
entity_linking_min_token_overlap: float = Field(default=0.60, ge=0.0, le=1.0, description="Minimum token overlap ratio for matching")
|
|
|
|
# Redis Configuration (for job tracking - separate DB from wiki)
|
|
redis_host: str = Field(default="redis-shared", description="Redis host")
|
|
redis_port: int = Field(default=6379, description="Redis port")
|
|
redis_db: int = Field(default=4, description="Redis database number (4 for library-desk jobs)")
|
|
|
|
# Application
|
|
app_name: str = Field(default="Library Desk", description="Application name")
|
|
app_version: str = Field(default=__version__, description="Application version")
|
|
debug: bool = Field(default=False, description="Debug mode")
|
|
|
|
# RAG Search Configuration
|
|
search_cache_ttl: int = Field(default=300, ge=0, le=3600, description="Search cache TTL in seconds")
|
|
search_timeout: int = Field(default=10, ge=1, le=60, description="SearXNG timeout in seconds")
|
|
search_default_limit: int = Field(default=10, ge=1, le=20, description="Default number of search results")
|
|
|
|
# Content Extraction Configuration
|
|
content_extraction_timeout: int = Field(default=5, ge=1, le=30, description="Trafilatura per-URL timeout in seconds")
|
|
content_max_length: int = Field(default=2000, ge=500, le=10000, description="Max extracted content length per result")
|
|
|
|
# Paperless-ngx Configuration
|
|
paperless_url: str = Field(default="http://paperless:8000", description="Paperless-ngx URL")
|
|
paperless_token: str = Field(default="", description="Paperless-ngx API token")
|
|
paperless_timeout: int = Field(default=30, ge=5, le=120, description="Paperless API timeout in seconds")
|
|
|
|
# Document Store Configuration
|
|
document_store_enabled: bool = Field(default=True, description="Enable document store feature")
|
|
document_catalog_path_prefix: str = Field(default="docs", description="Wiki path prefix for catalog pages")
|
|
|
|
# Volatile Cache Configuration
|
|
volatile_cache_enabled: bool = Field(default=True, description="Enable volatile cache feature")
|
|
volatile_default_ttl: int = Field(default=3600, ge=60, le=86400, description="Default TTL in seconds")
|
|
volatile_weather_ttl: int = Field(default=1800, ge=60, le=7200, description="Weather data TTL in seconds")
|
|
volatile_news_ttl: int = Field(default=7200, ge=300, le=86400, description="News data TTL in seconds")
|
|
volatile_financial_ttl: int = Field(default=300, ge=60, le=3600, description="Financial data TTL in seconds")
|
|
|
|
# Maintenance Configuration
|
|
maintenance_orphan_cleanup_enabled: bool = Field(default=True, description="Enable automatic orphan cleanup")
|
|
maintenance_cleanup_batch_size: int = Field(default=100, ge=10, le=1000, description="Cleanup batch size")
|
|
|
|
# Central Settings Database (Tatlock-wide)
|
|
system_settings_host: str = Field(default="postgres-shared", description="System settings PostgreSQL host")
|
|
system_settings_port: int = Field(default=5432, description="System settings PostgreSQL port")
|
|
system_settings_db: str = Field(default="system_settings", description="System settings database name")
|
|
system_settings_user: str = Field(default="settings", description="System settings database user")
|
|
system_settings_password: str = Field(default="", description="System settings database password")
|
|
|
|
# Scheduler Service
|
|
scheduler_url: str = Field(default="http://scheduler:8090", description="Scheduler service URL")
|
|
|
|
@property
|
|
def qdrant_url(self) -> str:
|
|
"""Computed Qdrant URL."""
|
|
return f"http://{self.qdrant_host}:{self.qdrant_port}"
|
|
|
|
@property
|
|
def redis_url(self) -> str:
|
|
"""Computed Redis URL."""
|
|
return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}"
|
|
|
|
@property
|
|
def system_settings_dsn(self) -> str:
|
|
"""Computed System Settings PostgreSQL DSN."""
|
|
if not self.system_settings_password:
|
|
return ""
|
|
return (
|
|
f"postgresql://{self.system_settings_user}:{self.system_settings_password}"
|
|
f"@{self.system_settings_host}:{self.system_settings_port}/{self.system_settings_db}"
|
|
)
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
"""
|
|
Get cached settings instance.
|
|
Uses lru_cache to ensure single instance across app.
|
|
"""
|
|
return Settings()
|