setumodi/devops-logs-analysis
0
1"""Central configuration for the Multi-Agent DevOps Incident Analyzer.2 3All settings are loaded from environment variables (optionally via a `.env`4file). Every external integration is optional: when its credentials are5absent the corresponding agent runs in a safe, degraded mode instead of6crashing. This keeps the project fully runnable for demos and CI.7"""8 9from __future__ import annotations10 11import os12from dataclasses import dataclass, field13from pathlib import Path14 15try:16 from dotenv import load_dotenv17 18 load_dotenv()19except Exception: # pragma: no cover - dotenv is optional at runtime20 pass21 22 23BASE_DIR = Path(__file__).resolve().parent24 25# Curated OpenRouter chat models the user can pick from in the UI.26# Any other OpenRouter model id can be entered manually via the "Custom" option.27OPENROUTER_MODEL_CHOICES = [28 "openai/gpt-4o-mini",29 "openai/gpt-4o",30 "anthropic/claude-3.5-sonnet",31 "google/gemini-2.0-flash-001",32 "meta-llama/llama-3.3-70b-instruct",33 "mistralai/mistral-large",34 "deepseek/deepseek-chat",35 "qwen/qwen-2.5-72b-instruct",36]37 38 39def _get(name: str, default: str = "") -> str:40 return os.getenv(name, default).strip()41 42 43@dataclass44class Settings:45 # --- LLM: OpenRouter (OpenAI-compatible, preferred) ---46 openrouter_api_key: str = field(default_factory=lambda: _get("OPENROUTER_API_KEY"))47 openrouter_base_url: str = field(48 default_factory=lambda: _get("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")49 )50 openrouter_model: str = field(51 default_factory=lambda: _get("OPENROUTER_MODEL", "openai/gpt-4o-mini")52 )53 54 # --- LLM: Hugging Face (fallback alternative) ---55 hf_token: str = field(default_factory=lambda: _get("HF_TOKEN"))56 hf_model: str = field(default_factory=lambda: _get("HF_MODEL", "Qwen/Qwen2.5-72B-Instruct"))57 llm_temperature: float = field(default_factory=lambda: float(_get("LLM_TEMPERATURE", "0.2") or 0.2))58 59 # --- Embeddings / Vector store ---60 embedding_model: str = field(61 default_factory=lambda: _get("EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2")62 )63 chroma_dir: str = field(default_factory=lambda: _get("CHROMA_DIR", "vectorstore/chroma_db"))64 65 # --- Slack ---66 slack_bot_token: str = field(default_factory=lambda: _get("SLACK_BOT_TOKEN"))67 slack_channel: str = field(default_factory=lambda: _get("SLACK_CHANNEL", "#incidents"))68 69 # --- Jira ---70 jira_server: str = field(default_factory=lambda: _get("JIRA_SERVER"))71 jira_email: str = field(default_factory=lambda: _get("JIRA_EMAIL"))72 jira_api_token: str = field(default_factory=lambda: _get("JIRA_API_TOKEN"))73 jira_project_key: str = field(default_factory=lambda: _get("JIRA_PROJECT_KEY", "OPS"))74 75 # --- Behaviour ---76 runbooks_dir: Path = field(default_factory=lambda: BASE_DIR / "data" / "runbooks")77 78 @property79 def llm_provider(self) -> str:80 """Which chat backend to use. OpenRouter takes priority when set."""81 if self.openrouter_api_key:82 return "openrouter"83 if self.hf_token:84 return "huggingface"85 return "none"86 87 @property88 def llm_enabled(self) -> bool:89 return self.llm_provider != "none"90 91 @property92 def active_model(self) -> str:93 if self.llm_provider == "openrouter":94 return self.openrouter_model95 if self.llm_provider == "huggingface":96 return self.hf_model97 return "rule-based fallback"98 99 @property100 def slack_enabled(self) -> bool:101 return bool(self.slack_bot_token)102 103 @property104 def jira_enabled(self) -> bool:105 return bool(self.jira_server and self.jira_email and self.jira_api_token)106 107 @property108 def chroma_path(self) -> Path:109 p = Path(self.chroma_dir)110 return p if p.is_absolute() else BASE_DIR / p111 112 113settings = Settings()114 