File size: 1,675 Bytes
8981bf6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Runtime configuration, resolved from environment variables with safe defaults."""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path

# Repo root and the seed-data directory. Resolved relative to this file so the app
# works the same whether launched from the repo root, a container, or pytest.
ROOT_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = Path(os.getenv("SUPPORTCOPILOT_DATA_DIR", ROOT_DIR / "data"))


@dataclass(frozen=True)
class Settings:
    """All knobs in one place. Read once at startup via :func:`get_settings`."""

    data_dir: Path = DATA_DIR

    # LLM provider selection. "auto" picks a real provider when its key is present,
    # otherwise falls back to the deterministic offline stub so everything runs with
    # zero paid keys.
    llm_provider: str = os.getenv("LLM_PROVIDER", "auto")
    anthropic_api_key: str | None = os.getenv("ANTHROPIC_API_KEY")
    openai_api_key: str | None = os.getenv("OPENAI_API_KEY")
    anthropic_model: str = os.getenv("ANTHROPIC_MODEL", "claude-3-5-haiku-latest")
    openai_model: str = os.getenv("OPENAI_MODEL", "gpt-4o-mini")

    # Retrieval / decision tuning.
    top_k: int = int(os.getenv("SUPPORTCOPILOT_TOP_K", "3"))
    # Below this retrieval/answer confidence the agent escalates to a human instead
    # of answering. Kept conservative on purpose: a wrong refund costs more than a
    # human-handled ticket.
    escalation_threshold: float = float(os.getenv("SUPPORTCOPILOT_ESCALATION_THRESHOLD", "0.12"))


def get_settings() -> Settings:
    """Return a fresh Settings snapshot from the current environment."""
    return Settings()