config

Конфигурация LLM code review бота.

Все настройки берутся из переменных окружения (env-файл на хосте), чтобы менять модель и поведение без пересборки образа.

 1"""
 2Конфигурация LLM code review бота.
 3
 4Все настройки берутся из переменных окружения (env-файл на хосте),
 5чтобы менять модель и поведение без пересборки образа.
 6"""
 7
 8from __future__ import annotations
 9
10import os
11from dataclasses import dataclass
12
13
14def _get_bool(name: str, default: bool) -> bool:
15    """Читает булеву переменную окружения ('1', 'true', 'yes', 'on')."""
16    raw = os.getenv(name)
17    if raw is None:
18        return default
19    return raw.strip().lower() in ("1", "true", "yes", "on")
20
21
22def _get_int(name: str, default: int) -> int:
23    """Читает целочисленную переменную окружения, бросает ValueError при битом значении."""
24    raw = os.getenv(name)
25    if raw is None:
26        return default
27    try:
28        return int(raw)
29    except ValueError:
30        raise ValueError(
31            f"Переменная {name} должна быть целым числом, получено: {raw!r}"
32        )
33
34
35@dataclass(frozen=True)
36class Config:
37    """Настройки бота, прочитанные из окружения."""
38
39    gitea_url: str
40    gitea_token: str
41    gitea_bot_username: str
42    llm_base_url: str
43    llm_api_key: str
44    llm_model: str
45    llm_max_tokens: int
46    llm_temperature: float
47    webhook_secret: str
48    block_on_critical: bool
49    max_diff_chars: int
50    port: int
51
52    @classmethod
53    def from_env(cls) -> "Config":
54        """Собирает Config из переменных окружения, валидируя обязательные."""
55        gitea_token = os.getenv("GITEA_TOKEN", "")
56        llm_api_key = os.getenv("LLM_API_KEY", "")
57        if not gitea_token:
58            raise ValueError("GITEA_TOKEN обязателен")
59        if not llm_api_key:
60            raise ValueError("LLM_API_KEY обязателен")
61
62        temperature = float(os.getenv("LLM_TEMPERATURE", "0.1"))
63        if not (0.0 <= temperature <= 2.0):
64            raise ValueError(f"LLM_TEMPERATURE вне диапазона [0, 2]: {temperature}")
65
66        return cls(
67            gitea_url=os.getenv("GITEA_URL", "http://gitea:3000"),
68            gitea_token=gitea_token,
69            gitea_bot_username=os.getenv("GITEA_BOT_USERNAME", "code-review-bot"),
70            llm_base_url=os.getenv("LLM_BASE_URL", "http://192.168.0.251:1234/v1"),
71            llm_api_key=llm_api_key,
72            llm_model=os.getenv("LLM_MODEL", "openai/gpt-oss-20b"),
73            llm_max_tokens=_get_int("LLM_MAX_TOKENS", 8000),
74            llm_temperature=temperature,
75            webhook_secret=os.getenv("WEBHOOK_SECRET", ""),
76            block_on_critical=_get_bool("BLOCK_ON_CRITICAL", True),
77            max_diff_chars=_get_int("MAX_DIFF_CHARS", 120_000),
78            port=_get_int("PORT", 18002),
79        )
@dataclass(frozen=True)
class Config:
36@dataclass(frozen=True)
37class Config:
38    """Настройки бота, прочитанные из окружения."""
39
40    gitea_url: str
41    gitea_token: str
42    gitea_bot_username: str
43    llm_base_url: str
44    llm_api_key: str
45    llm_model: str
46    llm_max_tokens: int
47    llm_temperature: float
48    webhook_secret: str
49    block_on_critical: bool
50    max_diff_chars: int
51    port: int
52
53    @classmethod
54    def from_env(cls) -> "Config":
55        """Собирает Config из переменных окружения, валидируя обязательные."""
56        gitea_token = os.getenv("GITEA_TOKEN", "")
57        llm_api_key = os.getenv("LLM_API_KEY", "")
58        if not gitea_token:
59            raise ValueError("GITEA_TOKEN обязателен")
60        if not llm_api_key:
61            raise ValueError("LLM_API_KEY обязателен")
62
63        temperature = float(os.getenv("LLM_TEMPERATURE", "0.1"))
64        if not (0.0 <= temperature <= 2.0):
65            raise ValueError(f"LLM_TEMPERATURE вне диапазона [0, 2]: {temperature}")
66
67        return cls(
68            gitea_url=os.getenv("GITEA_URL", "http://gitea:3000"),
69            gitea_token=gitea_token,
70            gitea_bot_username=os.getenv("GITEA_BOT_USERNAME", "code-review-bot"),
71            llm_base_url=os.getenv("LLM_BASE_URL", "http://192.168.0.251:1234/v1"),
72            llm_api_key=llm_api_key,
73            llm_model=os.getenv("LLM_MODEL", "openai/gpt-oss-20b"),
74            llm_max_tokens=_get_int("LLM_MAX_TOKENS", 8000),
75            llm_temperature=temperature,
76            webhook_secret=os.getenv("WEBHOOK_SECRET", ""),
77            block_on_critical=_get_bool("BLOCK_ON_CRITICAL", True),
78            max_diff_chars=_get_int("MAX_DIFF_CHARS", 120_000),
79            port=_get_int("PORT", 18002),
80        )

Настройки бота, прочитанные из окружения.

Config( gitea_url: str, gitea_token: str, gitea_bot_username: str, llm_base_url: str, llm_api_key: str, llm_model: str, llm_max_tokens: int, llm_temperature: float, webhook_secret: str, block_on_critical: bool, max_diff_chars: int, port: int)
gitea_url: str
gitea_token: str
gitea_bot_username: str
llm_base_url: str
llm_api_key: str
llm_model: str
llm_max_tokens: int
llm_temperature: float
webhook_secret: str
block_on_critical: bool
max_diff_chars: int
port: int
@classmethod
def from_env(cls) -> Config:
53    @classmethod
54    def from_env(cls) -> "Config":
55        """Собирает Config из переменных окружения, валидируя обязательные."""
56        gitea_token = os.getenv("GITEA_TOKEN", "")
57        llm_api_key = os.getenv("LLM_API_KEY", "")
58        if not gitea_token:
59            raise ValueError("GITEA_TOKEN обязателен")
60        if not llm_api_key:
61            raise ValueError("LLM_API_KEY обязателен")
62
63        temperature = float(os.getenv("LLM_TEMPERATURE", "0.1"))
64        if not (0.0 <= temperature <= 2.0):
65            raise ValueError(f"LLM_TEMPERATURE вне диапазона [0, 2]: {temperature}")
66
67        return cls(
68            gitea_url=os.getenv("GITEA_URL", "http://gitea:3000"),
69            gitea_token=gitea_token,
70            gitea_bot_username=os.getenv("GITEA_BOT_USERNAME", "code-review-bot"),
71            llm_base_url=os.getenv("LLM_BASE_URL", "http://192.168.0.251:1234/v1"),
72            llm_api_key=llm_api_key,
73            llm_model=os.getenv("LLM_MODEL", "openai/gpt-oss-20b"),
74            llm_max_tokens=_get_int("LLM_MAX_TOKENS", 8000),
75            llm_temperature=temperature,
76            webhook_secret=os.getenv("WEBHOOK_SECRET", ""),
77            block_on_critical=_get_bool("BLOCK_ON_CRITICAL", True),
78            max_diff_chars=_get_int("MAX_DIFF_CHARS", 120_000),
79            port=_get_int("PORT", 18002),
80        )

Собирает Config из переменных окружения, валидируя обязательные.