You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
2.5 KiB
Python

"""Application configuration via pydantic-settings + YAML merge."""
from __future__ import annotations
import functools
from pathlib import Path
from typing import Any
import yaml
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
def _load_yaml(path: str | Path) -> dict[str, Any]:
p = Path(path)
if p.exists():
with open(p) as f:
return yaml.safe_load(f) or {}
return {}
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)
app_env: str = "dev"
# Stock Oracle
stock_oracle_url: str = "http://localhost:18001"
stock_oracle_timeout: int = 30
# Database
postgres_dsn: str = "postgresql+asyncpg://acef:acef@localhost:5432/acef"
# File storage
data_root: str = "./data"
earnings_calendar_pit_file: str | None = None
# Symbol universe
symbols_file: str = "configs/symbols.yaml"
# Logging
log_level: str = "INFO"
# LLM (OpenAI — legacy placeholder)
openai_api_key: str = "sk-placeholder"
llm_enabled: bool = False
# Ollama
ollama_url: str = "http://localhost:11434"
ollama_model: str = "llama3.2"
ollama_timeout: int = 600
# App YAML overrides (loaded separately)
_app_config: dict[str, Any] = {}
@field_validator("log_level")
@classmethod
def normalize_log_level(cls, v: str) -> str:
return v.upper()
@property
def exhibit_cache_dir(self) -> Path:
return Path(self.data_root) / "cache" / "exhibits"
@property
def parquet_dir(self) -> Path:
return Path(self.data_root) / "parquet"
@property
def earnings_calendar_pit_path(self) -> Path:
if self.earnings_calendar_pit_file:
return Path(self.earnings_calendar_pit_file)
return Path(self.data_root) / "reference" / "earnings_calendar_pit.parquet"
def get_app_config(self) -> dict[str, Any]:
if not self._app_config:
object.__setattr__(self, "_app_config", _load_yaml("configs/app.yaml"))
return self._app_config
def get_symbols(self) -> list[str]:
cfg = _load_yaml(self.symbols_file)
return cfg.get("symbols", [])
def get_fred_series(self) -> list[dict[str, Any]]:
cfg = _load_yaml("configs/fred_series.yaml")
return cfg.get("series", [])
@functools.lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings()