File size: 3,513 Bytes
82e122c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from __future__ import annotations

import os
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path

# Resolve repo root from this file's location:
# app/settings.py → parent = app/ → parent = repo root
REPO_ROOT = Path(__file__).resolve().parents[1]

# Canonical demo DB and pipeline config shipped with the repo
DEFAULT_DEMO_DB = REPO_ROOT / "data" / "demo.db"
DEFAULT_PIPELINE_CONFIG = REPO_ROOT / "configs" / "sqlite_pipeline.yaml"


@dataclass
class Settings:
    """
    Centralized application configuration.

    Does NOT depend on pydantic. Values are loaded from environment
    variables via Settings.from_env().
    """

    # --- DB mode / adapters ---
    db_mode: str = "sqlite"  # "sqlite" or "postgres"
    postgres_dsn: str = ""

    # --- Pipeline config ---
    pipeline_config_path: str = str(DEFAULT_PIPELINE_CONFIG)

    # --- SQLite uploaded DBs ---
    db_upload_dir: str = "/tmp/nl2sql_dbs"
    db_ttl_seconds: int = 7200  # 2 hours

    # --- Upload constraints ---
    upload_max_bytes: int = 20 * 1024 * 1024  # 20MB

    # --- Cache settings ---
    cache_ttl_sec: int = 300
    cache_max_entries: int = 256

    # --- Default SQLite path (demo DB) ---
    default_sqlite_path: str = str(DEFAULT_DEMO_DB)

    # --- API keys (comma-separated) ---
    api_keys_raw: str = ""

    # --- App version ---
    app_version: str = "dev"

    @classmethod
    def from_env(cls) -> "Settings":
        """
        Build Settings from environment variables with sane fallbacks.

        - DEFAULT_SQLITE_PATH and PIPELINE_CONFIG can be absolute or relative.
        - Relative paths are resolved against REPO_ROOT.
        """

        def getenv_int(name: str, default: int) -> int:
            raw = os.getenv(name)
            if raw is None or raw.strip() == "":
                return default
            try:
                return int(raw)
            except ValueError:
                return default

        # --- Default SQLite path ---
        raw_default_db = os.getenv("DEFAULT_SQLITE_PATH", "").strip()
        if raw_default_db:
            db_candidate = Path(raw_default_db)
            if not db_candidate.is_absolute():
                db_candidate = REPO_ROOT / raw_default_db
        else:
            db_candidate = DEFAULT_DEMO_DB

        # --- Pipeline config path ---
        raw_cfg = os.getenv("PIPELINE_CONFIG", "").strip()
        if raw_cfg:
            cfg_candidate = Path(raw_cfg)
            if not cfg_candidate.is_absolute():
                cfg_candidate = REPO_ROOT / raw_cfg
        else:
            cfg_candidate = DEFAULT_PIPELINE_CONFIG

        return cls(
            db_mode=os.getenv("DB_MODE", cls.db_mode),
            postgres_dsn=os.getenv("POSTGRES_DSN", cls.postgres_dsn),
            pipeline_config_path=str(cfg_candidate),
            db_upload_dir=os.getenv("DB_UPLOAD_DIR", cls.db_upload_dir),
            db_ttl_seconds=getenv_int("DB_TTL_SECONDS", cls.db_ttl_seconds),
            upload_max_bytes=getenv_int("UPLOAD_MAX_BYTES", cls.upload_max_bytes),
            cache_ttl_sec=getenv_int("NL2SQL_CACHE_TTL_SEC", cls.cache_ttl_sec),
            cache_max_entries=getenv_int("NL2SQL_CACHE_MAX", cls.cache_max_entries),
            default_sqlite_path=str(db_candidate),
            api_keys_raw=os.getenv("API_KEYS", cls.api_keys_raw),
            app_version=os.getenv("APP_VERSION", cls.app_version),
        )


@lru_cache()
def get_settings() -> Settings:
    return Settings.from_env()