"""Configuration for the Cortex.0 / OpenHands fusion.""" from __future__ import annotations import os from dataclasses import dataclass, field from pathlib import Path import yaml DEFAULT_SYSTEM_PROMPT = Path(__file__).resolve().parent.parent / "prompts" / "cortex0_system.txt" @dataclass class Cortex0Settings: """Everything needed to reach Cortex.0 and configure the agent.""" # --- inference endpoint ------------------------------------------------- base_url: str = "http://127.0.0.1:8080/v1" api_key: str = "cortex0-local" # Name the endpoint actually serves. served_model_name: str = "cortex0" # litellm routes by provider prefix; any OpenAI-compatible server is # reached through the "openai/" prefix plus an explicit base_url. model: str = "openai/cortex0" # --- generation --------------------------------------------------------- max_output_tokens: int = 32768 temperature: float = 1.0 top_p: float = 0.95 top_k: int = 20 context_window: int = 262144 # --- robustness --------------------------------------------------------- num_retries: int = 5 timeout: float = 600.0 native_tool_calling: bool = True # --- agent -------------------------------------------------------------- tools: list[str] = field( default_factory=lambda: [ "terminal", "file_editor", "task_tracker", "glob", "grep", ] ) max_iterations_per_run: int = 500 condenser_max_size: int = 240 system_prompt_path: str = str(DEFAULT_SYSTEM_PROMPT) workspace: str = "workspace/project" @classmethod def load(cls, path: str | Path | None = None) -> "Cortex0Settings": """Load settings from YAML, then let environment variables win. Precedence: defaults < YAML file < CORTEX0_* environment variables. """ data: dict = {} candidate = Path(path) if path else Path("configs/cortex0.yaml") if candidate.is_file(): loaded = yaml.safe_load(candidate.read_text(encoding="utf-8")) or {} if not isinstance(loaded, dict): raise ValueError(f"{candidate} must contain a YAML mapping") data = loaded for key in cls.__dataclass_fields__: env_key = f"CORTEX0_{key.upper()}" if env_key in os.environ: raw = os.environ[env_key] data[key] = _coerce(raw, cls.__dataclass_fields__[key].default) known = {k: v for k, v in data.items() if k in cls.__dataclass_fields__} return cls(**known) def system_prompt(self) -> str: path = Path(self.system_prompt_path) if not path.is_file(): raise FileNotFoundError( f"System prompt not found: {path}. Set system_prompt_path or create the file." ) return path.read_text(encoding="utf-8") def _coerce(raw: str, default: object) -> object: """Turn an environment string into the type of the dataclass default.""" if isinstance(default, bool): return raw.strip().lower() in {"1", "true", "yes", "on"} if isinstance(default, int) and not isinstance(default, bool): return int(raw) if isinstance(default, float): return float(raw) if isinstance(default, list): return [item.strip() for item in raw.split(",") if item.strip()] return raw