Spaces:
Sleeping
Sleeping
| """Runtime configuration for the content generation agent. | |
| All settings can come from environment variables (for Docker / server deploys) | |
| or be overridden at runtime from the Streamlit UI (so a user can paste their | |
| own API key and choose a model before running a command). | |
| """ | |
| from __future__ import annotations | |
| import os | |
| try: | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| except ImportError: | |
| pass | |
| from dataclasses import dataclass, field, asdict | |
| from typing import Optional | |
| # OpenAI-compatible chat-completion endpoints for the supported providers. | |
| PROVIDER_ENDPOINTS = { | |
| "groq": "https://api.groq.com/openai/v1/chat/completions", | |
| "openrouter": "https://openrouter.ai/api/v1/chat/completions", | |
| } | |
| # Sensible default models per provider. | |
| DEFAULT_MODELS = { | |
| "groq": "llama-3.3-70b-versatile", | |
| "openrouter": "meta-llama/llama-3.3-70b-instruct", | |
| } | |
| # A short menu the UI can present per provider. | |
| MODEL_CHOICES = { | |
| "groq": [ | |
| "llama-3.3-70b-versatile", | |
| "llama-3.1-8b-instant", | |
| "openai/gpt-oss-20b", | |
| ], | |
| "openrouter": [ | |
| "meta-llama/llama-3.3-70b-instruct", | |
| "openai/gpt-4o-mini", | |
| "anthropic/claude-3.5-sonnet", | |
| "google/gemini-flash-1.5", | |
| ], | |
| } | |
| def _env_bool(name: str, default: bool) -> bool: | |
| val = os.getenv(name) | |
| if val is None: | |
| return default | |
| return val.strip().lower() in {"1", "true", "yes", "on"} | |
| class Settings: | |
| """Resolved settings used by the pipeline.""" | |
| provider: str = field(default_factory=lambda: os.getenv("LLM_PROVIDER", "groq")) | |
| api_key: Optional[str] = field(default_factory=lambda: os.getenv("LLM_API_KEY")) | |
| model: Optional[str] = field(default_factory=lambda: os.getenv("LLM_MODEL")) | |
| # Mock mode lets the whole pipeline run with no network / no API key. | |
| mock_mode: bool = field(default_factory=lambda: _env_bool("MOCK_MODE", False)) | |
| temperature: float = field(default_factory=lambda: float(os.getenv("LLM_TEMPERATURE", "0.7"))) | |
| max_tokens: int = field(default_factory=lambda: int(os.getenv("LLM_MAX_TOKENS", "1200"))) | |
| # Crawl controls | |
| crawl_max_pages: int = field(default_factory=lambda: int(os.getenv("CRAWL_MAX_PAGES", "40"))) | |
| crawl_max_depth: int = field(default_factory=lambda: int(os.getenv("CRAWL_MAX_DEPTH", "2"))) | |
| crawl_timeout: int = field(default_factory=lambda: int(os.getenv("CRAWL_TIMEOUT", "10"))) | |
| def resolved_model(self) -> str: | |
| if self.model: | |
| return self.model | |
| return DEFAULT_MODELS.get(self.provider, DEFAULT_MODELS["groq"]) | |
| def endpoint(self) -> str: | |
| return PROVIDER_ENDPOINTS.get(self.provider, PROVIDER_ENDPOINTS["groq"]) | |
| def is_live(self) -> bool: | |
| """True when we should make real API calls.""" | |
| return (not self.mock_mode) and bool(self.api_key) | |
| def to_dict(self) -> dict: | |
| d = asdict(self) | |
| # Never echo the full key back. | |
| if d.get("api_key"): | |
| d["api_key"] = d["api_key"][:4] + "…" | |
| return d | |
| def get_settings(**overrides) -> Settings: | |
| """Build settings from env, then apply any UI overrides (non-None only).""" | |
| s = Settings() | |
| for k, v in overrides.items(): | |
| if v is not None and hasattr(s, k): | |
| setattr(s, k, v) | |
| return s | |