File size: 4,915 Bytes
212c959
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import os
from dataclasses import dataclass
from typing import List

from dotenv import load_dotenv


load_dotenv(override=False)


def _clean_env_value(value: str) -> str:
    # Strip whitespace and optional surrounding quotes.
    v = value.strip()
    if len(v) >= 2 and ((v[0] == v[-1] == '"') or (v[0] == v[-1] == "'")):
        v = v[1:-1].strip()
    return v


def _get_env(name: str, default: str | None = None) -> str:
    value = os.getenv(name, default)
    if value is None or value == "":
        raise RuntimeError(f"Missing required environment variable: {name}")
    return _clean_env_value(value)


def _get_env_optional(name: str, default: str | None = None) -> str | None:
    value = os.getenv(name, default)
    if value is None or value == "":
        return None
    return _clean_env_value(value)


def _get_bool_env(name: str, default: bool = False) -> bool:
    raw = os.getenv(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "y", "on"}


def _get_bool_env_optional(name: str) -> bool | None:
    raw = os.getenv(name)
    if raw is None or raw.strip() == "":
        return None
    return raw.strip().lower() in {"1", "true", "yes", "y", "on"}


def _parse_cors_origins(raw: str | None) -> List[str]:
    if not raw:
        return ["*"]
    parts = [p.strip() for p in raw.split(",")]
    return [p for p in parts if p]


@dataclass(frozen=True)
class Settings:
    groq_api_key: str
    groq_model: str
    voice_transcription_model: str
    google_api_key: str | None
    openrouter_api_key: str | None
    default_provider: str
    default_model: str
    mongodb_uri: str
    mongodb_tls: bool | None
    mongodb_db: str
    mongo_sessions_collection: str
    mongo_messages_collection: str
    mongo_users_collection: str
    max_history_messages: int
    max_attachment_chars_per_file: int
    max_total_attachment_chars: int
    temperature: float
    debug: bool
    cors_origins: List[str]
    jwt_secret: str
    jwt_algorithm: str
    jwt_exp_minutes: int
    tavily_api_key: str | None
    tavily_max_results: int
    google_client_id: str | None
    google_client_secret: str | None
    google_redirect_uri: str | None
    google_scope: str
    enable_vector_memory: bool
    enable_local_vision: bool


def get_settings() -> Settings:
    debug = _get_bool_env("DEBUG", False)
    jwt_secret = _get_env_optional("JWT_SECRET", None)
    if not jwt_secret and not debug:
        raise RuntimeError(
            "Missing JWT_SECRET. Set it in your .env (or enable DEBUG=true for a dev-only insecure secret)."
        )
    return Settings(
        groq_api_key=_get_env_optional("GROQ_API_KEY", None) or "",
        groq_model=os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile"),
        voice_transcription_model=os.getenv("VOICE_TRANSCRIPTION_MODEL", "whisper-large-v3-turbo").strip(),
        google_api_key=_get_env_optional("GOOGLE_API_KEY", None),
        openrouter_api_key=_get_env_optional("OPENROUTER_API_KEY", None),
        default_provider=os.getenv("DEFAULT_LLM_PROVIDER", "groq").strip().lower(),
        default_model=os.getenv("DEFAULT_LLM_MODEL", "llama-3.3-70b-versatile").strip(),
        mongodb_uri=_get_env("MONGODB_URI", "mongodb://localhost:27017"),
        mongodb_tls=_get_bool_env_optional("MONGODB_TLS"),
        mongodb_db=os.getenv("MONGODB_DB", "own_gpt"),
        mongo_sessions_collection=os.getenv("MONGO_SESSIONS_COLLECTION", "sessions"),
        mongo_messages_collection=os.getenv("MONGO_MESSAGES_COLLECTION", "messages"),
        mongo_users_collection=os.getenv("MONGO_USERS_COLLECTION", "users"),
        max_history_messages=int(os.getenv("MAX_HISTORY_MESSAGES", "20")),
        max_attachment_chars_per_file=int(os.getenv("MAX_ATTACHMENT_CHARS_PER_FILE", "8000")),
        max_total_attachment_chars=int(os.getenv("MAX_TOTAL_ATTACHMENT_CHARS", "16000")),
        temperature=float(os.getenv("GROQ_TEMPERATURE", "0.2")),
        debug=debug,
        cors_origins=_parse_cors_origins(_get_env_optional("CORS_ORIGINS", "*")),
        jwt_secret=jwt_secret or "dev-only-insecure-jwt-secret",
        jwt_algorithm=os.getenv("JWT_ALGORITHM", "HS256"),
        jwt_exp_minutes=int(os.getenv("JWT_EXP_MINUTES", "60")),
        tavily_api_key=_get_env_optional("TAVILY_API_KEY", None),
        tavily_max_results=int(os.getenv("TAVILY_MAX_RESULTS", "5")),
        google_client_id=_get_env_optional("GOOGLE_CLIENT_ID", None),
        google_client_secret=_get_env_optional("GOOGLE_CLIENT_SECRET", None),
        google_redirect_uri=_get_env_optional("GOOGLE_REDIRECT_URI", None),
        google_scope=os.getenv("GOOGLE_SCOPE", "openid email profile"),
        enable_vector_memory=_get_bool_env("ENABLE_VECTOR_MEMORY", True),
        enable_local_vision=_get_bool_env("ENABLE_LOCAL_VISION", False),
    )