import os from dataclasses import dataclass, field from dotenv import load_dotenv load_dotenv() @dataclass class Config: # ═══════════════════════════════════════════════════════════════ # BLOCK: Telegram # ═══════════════════════════════════════════════════════════════ BOT_TOKEN: str = os.getenv("BOT_TOKEN", "") OWNER_ID: int = int(os.getenv("OWNER_ID", "0") or "0") WEBHOOK_PATH: str = "/webhook" # ═══════════════════════════════════════════════════════════════ # BLOCK: Database (Neon PostgreSQL) # ═══════════════════════════════════════════════════════════════ DATABASE_URL: str = os.getenv("DATABASE_URL", "") # ═══════════════════════════════════════════════════════════════ # BLOCK: AI — Primary Model (GLM via NVIDIA) # ═══════════════════════════════════════════════════════════════ NVIDIA_API_KEY: str = os.getenv("NVIDIA_API_KEY", "") NVIDIA_BASE_URL: str = "https://integrate.api.nvidia.com/v1" PRIMARY_MODEL: str = os.getenv("MODEL_NAME", "z-ai/glm-5.1") # ═══════════════════════════════════════════════════════════════ # BLOCK: AI — Fallback Model # ═══════════════════════════════════════════════════════════════ FALLBACK_MODEL: str = os.getenv("FALLBACK_MODEL", "") FALLBACK_ENABLED: bool = os.getenv("FALLBACK_ENABLED", "false").lower() == "true" # ═══════════════════════════════════════════════════════════════ # BLOCK: AI Parameters — Optimized for GLM-5.1 # ═══════════════════════════════════════════════════════════════ # GLM-4.5 рекомендации через NVIDIA NIM: # - temperature: 0.3–0.8 (ниже = точнее, выше = креативнее) # - top_p: 0.7–0.95 # - max_tokens: зависит от задачи, 4096–8192 для длинных ответов GLM_TEMPERATURE: float = float(os.getenv("GLM_TEMPERATURE", "0.7")) GLM_TOP_P: float = float(os.getenv("GLM_TOP_P", "0.9")) GLM_FREQUENCY_PENALTY: float = float(os.getenv("GLM_FREQUENCY_PENALTY", "0.1")) GLM_PRESENCE_PENALTY: float = float(os.getenv("GLM_PRESENCE_PENALTY", "0.05")) GLM_MAX_TOKENS: int = int(os.getenv("GLM_MAX_TOKENS", "4096")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Timeouts — Granular for NVIDIA NIM # ═══════════════════════════════════════════════════════════════ # NVIDIA NIM может отвечать 2–3 минуты на сложные запросы. # connect: время установления TCP-соединения # read: время ожидания первого байта ответа # write: время на отправку запроса TIMEOUT_CONNECT: float = float(os.getenv("TIMEOUT_CONNECT", "10.0")) TIMEOUT_READ: float = float(os.getenv("TIMEOUT_READ", "180.0")) # 3 минуты TIMEOUT_WRITE: float = float(os.getenv("TIMEOUT_WRITE", "10.0")) TIMEOUT_POOL: float = float(os.getenv("TIMEOUT_POOL", "5.0")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Retry Configuration # ═══════════════════════════════════════════════════════════════ MAX_RETRIES: int = int(os.getenv("MAX_RETRIES", "3")) RETRY_BASE_DELAY: float = float(os.getenv("RETRY_BASE_DELAY", "2.0")) RETRY_MAX_DELAY: float = float(os.getenv("RETRY_MAX_DELAY", "60.0")) RETRY_EXPONENTIAL_BASE: float = float(os.getenv("RETRY_EXPONENTIAL_BASE", "2.0")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Streaming # ═══════════════════════════════════════════════════════════════ STREAMING_ENABLED: bool = os.getenv("STREAMING_ENABLED", "true").lower() == "true" STREAMING_CHUNK_SIZE: int = int(os.getenv("STREAMING_CHUNK_SIZE", "100")) STREAMING_UPDATE_INTERVAL: float = float(os.getenv("STREAMING_UPDATE_INTERVAL", "1.5")) # ═══════════════════════════════════════════════════════════════ # BLOCK: History & Memory # ═══════════════════════════════════════════════════════════════ MAX_HISTORY: int = int(os.getenv("MAX_HISTORY", "20")) # сообщений в контексте MAX_CONTEXT_TOKENS: int = int(os.getenv("MAX_CONTEXT_TOKENS", "6000")) # примерно токенов SUMMARIZE_THRESHOLD: int = int(os.getenv("SUMMARIZE_THRESHOLD", "30")) SUMMARY_MAX_TOKENS: int = int(os.getenv("SUMMARY_MAX_TOKENS", "512")) KEEP_RECENT_MESSAGES: int = int(os.getenv("KEEP_RECENT_MESSAGES", "10")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Telegram Limits # ═══════════════════════════════════════════════════════════════ MAX_MESSAGE_LENGTH: int = 4096 TYPING_ACTION_INTERVAL: float = 4.5 # Telegram typing action expires after ~5s # ═══════════════════════════════════════════════════════════════ # BLOCK: Rate Limiting # ═══════════════════════════════════════════════════════════════ RATE_LIMIT_ENABLED: bool = os.getenv("RATE_LIMIT_ENABLED", "true").lower() == "true" RATE_LIMIT_REQUESTS_PER_MINUTE: int = int(os.getenv("RATE_LIMIT_REQUESTS_PER_MINUTE", "10")) RATE_LIMIT_BURST: int = int(os.getenv("RATE_LIMIT_BURST", "3")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Cache # ═══════════════════════════════════════════════════════════════ CACHE_ENABLED: bool = os.getenv("CACHE_ENABLED", "true").lower() == "true" CACHE_TTL_SECONDS: int = int(os.getenv("CACHE_TTL_SECONDS", "300")) # 5 минут CACHE_MAX_SIZE: int = int(os.getenv("CACHE_MAX_SIZE", "1000")) # ═══════════════════════════════════════════════════════════════ # BLOCK: Monitoring # ═══════════════════════════════════════════════════════════════ LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") METRICS_ENABLED: bool = os.getenv("METRICS_ENABLED", "true").lower() == "true" def __post_init__(self) -> None: if self.DATABASE_URL.startswith("postgres://"): self.DATABASE_URL = self.DATABASE_URL.replace( "postgres://", "postgresql://", 1 ) def validate(self) -> None: required = [ ("BOT_TOKEN", self.BOT_TOKEN), ("OWNER_ID", self.OWNER_ID), ("DATABASE_URL", self.DATABASE_URL), ("NVIDIA_API_KEY", self.NVIDIA_API_KEY), ] for name, value in required: if not value: raise ValueError(f"{name} is required") config = Config() config.validate()