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)
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 из переменных окружения, валидируя обязательные.