File size: 3,384 Bytes
3a04f21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5124452
 
3a04f21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5124452
 
 
 
3a04f21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5124452
 
 
 
 
 
 
3a04f21
 
 
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
"""Environment-driven application configuration.



Two model backends are supported:



  Cloud model  β€” a remote OpenAI-compatible API (e.g. gpt-5.4 via a hosted

                 endpoint).  Used as the powerful multimodal backbone for

                 tasks like audio transcription.



  Local model  β€” a self-hosted model served via SGLang, vLLM, or any

                 OpenAI-compatible server (e.g. Qwen3.5-2B on localhost).

                 Used for high-throughput image recognition / classification.



Both backends expose ``/v1/chat/completions``; the only difference is the

base URL, API key, and model name.

"""

from __future__ import annotations

import os
from dataclasses import dataclass


@dataclass(frozen=True)
class Config:
    server_host: str
    server_port: int

    # Auth: YesCaptcha clientKey
    client_key: str | None

    # ── Cloud model (remote API) ──
    cloud_base_url: str
    cloud_api_key: str
    cloud_model: str

    # ── Local model (self-hosted via SGLang / vLLM) ──
    local_base_url: str
    local_api_key: str
    local_model: str

    captcha_retries: int
    captcha_timeout: int

    # Playwright browser
    browser_headless: bool
    browser_timeout: int  # seconds
    browser_proxy_url: str | None = None

    # ── Convenience aliases (backward-compat) ──

    @property
    def captcha_base_url(self) -> str:
        return self.cloud_base_url

    @property
    def captcha_api_key(self) -> str:
        return self.cloud_api_key

    @property
    def captcha_model(self) -> str:
        return self.cloud_model

    @property
    def captcha_multimodal_model(self) -> str:
        return self.local_model


def load_config() -> Config:
    return Config(
        server_host=os.environ.get("SERVER_HOST", "0.0.0.0"),
        server_port=int(os.environ.get("SERVER_PORT", "8000")),
        client_key=os.environ.get("CLIENT_KEY", "").strip() or None,
        # Cloud model
        cloud_base_url=os.environ.get(
            "CLOUD_BASE_URL",
            os.environ.get("CAPTCHA_BASE_URL", "https://your-openai-compatible-endpoint/v1"),
        ),
        cloud_api_key=os.environ.get(
            "CLOUD_API_KEY",
            os.environ.get("CAPTCHA_API_KEY", ""),
        ),
        cloud_model=os.environ.get(
            "CLOUD_MODEL",
            os.environ.get("CAPTCHA_MODEL", "gpt-5.4"),
        ),
        # Local model
        local_base_url=os.environ.get(
            "LOCAL_BASE_URL",
            os.environ.get("CAPTCHA_BASE_URL", "http://localhost:30000/v1"),
        ),
        local_api_key=os.environ.get(
            "LOCAL_API_KEY",
            os.environ.get("CAPTCHA_API_KEY", "EMPTY"),
        ),
        local_model=os.environ.get(
            "LOCAL_MODEL",
            os.environ.get("CAPTCHA_MULTIMODAL_MODEL", "Qwen/Qwen3.5-2B"),
        ),
        captcha_retries=int(os.environ.get("CAPTCHA_RETRIES", "3")),
        captcha_timeout=int(os.environ.get("CAPTCHA_TIMEOUT", "30")),
        browser_headless=os.environ.get("BROWSER_HEADLESS", "true").strip().lower()
        in {"1", "true", "yes"},
        browser_timeout=int(os.environ.get("BROWSER_TIMEOUT", "30")),
        browser_proxy_url=os.environ.get("BROWSER_PROXY_URL", "").strip() or None,
    )


config = load_config()