LibBee / src /config.py
nikeshn's picture
Upload config.py
40a8f2e verified
Raw
History Blame Contribute Delete
3.28 kB
from functools import lru_cache
from pathlib import Path
from typing import Literal
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
# Single source of truth for the deployed version. app.py, admin.py and all
# outbound User-Agent strings read this constant - never hardcode versions elsewhere.
LIBBEE_VERSION = "3.8.8"
class Settings(BaseSettings):
model_config = SettingsConfigDict(
case_sensitive=False,
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
openai_api_key: str = ""
anthropic_api_key: str = ""
primo_api_key: str = ""
admin_password: str = ""
session_secret: str = "change-me"
environment: Literal["development", "production"] = "production"
maintenance_mode: bool = False
max_results: int = 5
runtime_dir: str = "/tmp/libbee_runtime"
feedback_store_path: str = ""
metrics_store_path: str = ""
config_store_path: str = ""
rag_index_dir: str = ""
knowledge_dir: str = ""
rate_limit_per_minute: int = 40
# Cloudflare Worker URL for persistent analytics
# Set in HF Space secrets (never hardcode the URL in this repository).
cloudflare_worker_url: str = ""
# Shared-secret bearer token for the Worker analytics endpoints (/log, /analytics*, /reports*).
# Must match the ANALYTICS_TOKEN secret configured on the Cloudflare Worker.
cloudflare_worker_token: str = ""
# Contact for polite-pool identification with open scholarly APIs (OpenAlex, Unpaywall, Crossref).
contact_email: str = "libse@ku.ac.ae"
@field_validator("max_results")
@classmethod
def reasonable_max_results(cls, v: int) -> int:
if not 1 <= v <= 20:
raise ValueError("max_results must be between 1 and 20")
return v
@field_validator("rate_limit_per_minute")
@classmethod
def reasonable_rate_limit(cls, v: int) -> int:
if not 5 <= v <= 600:
raise ValueError("rate_limit_per_minute must be between 5 and 600")
return v
@property
def runtime_path(self) -> Path:
return Path(self.runtime_dir)
@property
def feedback_path(self) -> Path:
return Path(self.feedback_store_path) if self.feedback_store_path else self.runtime_path / "feedback.jsonl"
@property
def metrics_path(self) -> Path:
return Path(self.metrics_store_path) if self.metrics_store_path else self.runtime_path / "metrics.json"
@property
def config_path(self) -> Path:
return Path(self.config_store_path) if self.config_store_path else self.runtime_path / "runtime_config.json"
@property
def rag_cache_dir(self) -> Path:
return Path(self.rag_index_dir) if self.rag_index_dir else self.runtime_path / "rag_cache"
@property
def kb_dir(self) -> Path:
if self.knowledge_dir:
return Path(self.knowledge_dir)
if Path("/app/data/knowledge").exists():
return Path("/app/data/knowledge")
return Path("data/knowledge")
@lru_cache
def get_settings() -> Settings:
settings = Settings()
settings.runtime_path.mkdir(parents=True, exist_ok=True)
settings.rag_cache_dir.mkdir(parents=True, exist_ok=True)
return settings