Cashy / src /config.py
GitHub Actions
Deploy to HF Spaces
17a78b5
from typing import Literal, Optional
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
# App mode β€” "demo" (seeded showcase) or "personal" (real financial data)
app_mode: Literal["demo", "personal"] = "personal"
# Database β€” auto-reads DB_HOST, DB_PORT, etc. from .env
db_host: str = "localhost"
db_port: int = 5432
db_name: str = "financial_db" # fallback; overridden by resolved_db_name
db_name_demo: str = "cashy_demo"
db_name_personal: str = "financial_db"
db_user: str = "financial_advisor"
db_password: str = ""
db_sslmode: str = "" # "require" for Neon; empty for local
# LLM provider β€” set explicitly or auto-detected from API keys
llm_provider: Optional[Literal["openai", "anthropic", "google", "huggingface", "free-tier", ""]] = None
# Per-provider API keys (only one needed)
openai_api_key: str = ""
anthropic_api_key: str = ""
google_api_key: str = ""
hf_token: str = ""
# Model configuration
model_name: str = "" # Optional override; defaults per provider
model_max_tokens: int = 512
model_temperature: float = 0.1
# HuggingFace-specific
hf_inference_provider: str = "together"
# LangSmith β€” auto-reads LANGSMITH_* from .env
langsmith_tracing: str = "true"
langsmith_api_key: str = ""
langsmith_project: str = "cashy-financial-advisor"
# App
environment: str = "development"
debug: bool = True
@property
def resolved_db_name(self) -> str:
"""Return the database name based on app_mode."""
if self.app_mode == "demo":
return self.db_name_demo
return self.db_name_personal
@property
def database_url(self) -> str:
"""Build DATABASE_URL from individual DB components."""
url = (
f"postgresql://{self.db_user}:{self.db_password}"
f"@{self.db_host}:{self.db_port}/{self.resolved_db_name}"
)
if self.db_sslmode:
url += f"?sslmode={self.db_sslmode}"
return url
@property
def database_url_safe(self) -> str:
"""Database URL with password redacted for logging."""
return self.database_url.replace(f":{self.db_password}@", ":***@")
@property
def resolved_provider(self) -> Optional[str]:
"""Return the active LLM provider: explicit setting, auto-detected from keys, or None."""
if self.llm_provider and self.llm_provider != "":
return self.llm_provider
# Auto-detect from populated API keys (priority order)
if self.openai_api_key and self.openai_api_key != "sk-...":
return "openai"
if self.anthropic_api_key and self.anthropic_api_key != "sk-ant-...":
return "anthropic"
if self.google_api_key and self.google_api_key != "AI...":
return "google"
if self.hf_token and self.hf_token != "hf_...":
# In demo mode, default to free-tier (user can switch to huggingface BYOK)
if self.app_mode == "demo":
return "free-tier"
return "huggingface"
return None
settings = Settings()