| import logging |
| logger = logging.getLogger(__name__) |
| """ |
| Configuration management for Graph RAG Service |
| Extended with: temporal, multi-tenant, eval, semantic-cache, hybrid search, GoT settings |
| """ |
|
|
| from pydantic_settings import BaseSettings, SettingsConfigDict |
| from typing import Optional, List |
| from pathlib import Path |
|
|
|
|
| class Settings(BaseSettings): |
| """Application settings with environment variable support""" |
|
|
| model_config = SettingsConfigDict( |
| env_file=".env", |
| env_file_encoding="utf-8", |
| case_sensitive=False, |
| extra="allow" |
| ) |
|
|
| |
| app_name: str = "Graph RAG Service" |
| |
| |
| app_version: str = "0.1.0" |
| debug: bool = False |
| environment: str = "development" |
|
|
| |
| api_host: str = "0.0.0.0" |
| api_port: int = 8000 |
| api_workers: int = 4 |
|
|
| |
| secret_key: str = "change-this-in-production-to-a-secure-random-key" |
| algorithm: str = "HS256" |
| access_token_expire_minutes: int = 30 |
|
|
| |
| neo4j_uri: str = "bolt://localhost:7687" |
| neo4j_user: str = "neo4j" |
| neo4j_password: str = "password" |
| neo4j_database: str = "neo4j" |
|
|
| |
| redis_host: str = "localhost" |
| redis_port: int = 6379 |
| redis_db: int = 0 |
| redis_password: Optional[str] = None |
|
|
| |
| celery_broker_url: str = "redis://localhost:6379/0" |
| celery_result_backend: str = "redis://localhost:6379/0" |
|
|
| |
| default_llm_provider: str = "ollama" |
|
|
| |
| openai_api_key: Optional[str] = None |
| openai_model: str = "gpt-4" |
|
|
| |
| anthropic_api_key: Optional[str] = None |
| anthropic_model: str = "claude-sonnet-4" |
|
|
| |
| google_api_key: Optional[str] = None |
| gemini_model: str = "gemini-2.5-flash" |
|
|
| |
| llama_cloud_api_key: Optional[str] = None |
| use_llama_parse: bool = True |
|
|
| |
| ollama_base_url: str = "http://localhost:11434" |
| ollama_model: str = "deepseek-v3.1:671b-cloud" |
| ollama_embedding_model: str = "nomic-embed-text" |
|
|
| |
| embedding_provider: str = "ollama" |
| embedding_dimension: int = 768 |
|
|
| |
| chunk_size: int = 1024 |
| chunk_overlap: int = 200 |
| max_concurrent_extractions: int = 2 |
|
|
| |
| ontology_version: str = "v1.0" |
| enable_ontology_evolution: bool = True |
| entity_resolution_threshold: float = 0.85 |
|
|
| |
| max_agent_iterations: int = 5 |
| agent_timeout_seconds: int = 30 |
| enable_hallucination_guard: bool = True |
|
|
| |
| enable_hybrid_search: bool = True |
| hybrid_bm25_weight: float = 0.3 |
| hybrid_vector_weight: float = 0.7 |
| rrf_k: int = 60 |
|
|
| |
| enable_community_search: bool = True |
| community_summary_cache_ttl: int = 7200 |
| max_community_entities: int = 50 |
|
|
| |
| enable_drift_expansion: bool = True |
| drift_expansion_threshold: float = 0.5 |
| max_drift_expansions: int = 2 |
|
|
| |
| enable_llm_judge: bool = True |
| judge_temperature: float = 0.0 |
|
|
| |
| enable_temporal_store: bool = True |
| temporal_default_validity_days: int = 3650 |
|
|
| |
| enable_graph_of_thought: bool = True |
| got_parallel_timeout: float = 20.0 |
|
|
| |
| enable_multi_tenant: bool = True |
| default_tenant_id: str = "default" |
|
|
| |
| enable_semantic_cache: bool = True |
| cache_ttl_seconds: int = 3600 |
| cache_similarity_threshold: float = 0.95 |
|
|
| |
| allowed_file_types: List[str] = [ |
| ".pdf", ".txt", ".md", ".docx", |
| ".csv", ".xlsx", ".pptx", ".json" |
| ] |
|
|
| |
| default_top_k: int = 5 |
| vector_search_similarity_threshold: float = 0.7 |
| graph_max_depth: int = 3 |
|
|
| |
| max_upload_size_mb: int = 100 |
| upload_dir: Path = Path("data/uploads").resolve() |
|
|
| |
| frontend_dist_dir: Path = Path(__file__).parent.parent.parent / "frontend-react" / "dist" |
|
|
| |
| enable_tracing: bool = False |
| enable_metrics: bool = False |
| log_level: str = "INFO" |
| otel_exporter_endpoint: Optional[str] = None |
|
|
| |
| entity_enrichment_min_connections: int = 1 |
| entity_enrichment_batch_size: int = 20 |
|
|
| |
| enable_ontology_evolution: bool = True |
| drift_sample_size: int = 10 |
| drift_detection_schedule: str = "0 3 * * *" |
|
|
|
|
| @property |
| def redis_url(self) -> str: |
| """Construct Redis URL""" |
| if self.redis_password: |
| return f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}" |
| return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}" |
|
|
| def model_post_init(self, __context): |
| """Fallback to local Ollama if cloud API keys are missing""" |
| if self.default_llm_provider == "gemini" and not self.google_api_key: |
| logger.info("WARNING: No GOOGLE_API_KEY found. Falling back to Ollama for LLM.") |
| self.default_llm_provider = "ollama" |
|
|
| if self.embedding_provider == "gemini" and not self.google_api_key: |
| logger.info("WARNING: No GOOGLE_API_KEY found. Falling back to Ollama for embeddings.") |
| self.embedding_provider = "ollama" |
|
|
| def get_llm_config(self, provider: Optional[str] = None) -> dict: |
| """Get LLM configuration for specified provider""" |
| provider = provider or self.default_llm_provider |
|
|
| configs = { |
| "openai": { |
| "api_key": self.openai_api_key, |
| "model": self.openai_model, |
| }, |
| "anthropic": { |
| "api_key": self.anthropic_api_key, |
| "model": self.anthropic_model, |
| }, |
| "gemini": { |
| "api_key": self.google_api_key, |
| "model": self.gemini_model, |
| }, |
| "ollama": { |
| "base_url": self.ollama_base_url, |
| "model": self.ollama_model, |
| }, |
| } |
|
|
| return configs.get(provider, configs["ollama"]) |
|
|
|
|
| |
| settings = Settings() |
|
|