From b05f282e3a4fe8f5a337eb1042e566c1642679e4 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Tue, 26 Aug 2025 11:32:55 +0500 Subject: [PATCH] feat: remove `models.yaml` --- README.md | 6 ++-- models.yaml | 4 --- src/cli.py | 29 ++-------------- src/instance_runner/api.py | 2 -- src/instance_runner/runner.py | 32 ++---------------- src/orchestration/orchestrator.py | 10 +----- src/utils/model_mapping.py | 55 ------------------------------- 7 files changed, 9 insertions(+), 129 deletions(-) delete mode 100644 models.yaml delete mode 100644 src/utils/model_mapping.py diff --git a/README.md b/README.md index 31ffd32..8118190 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ The CLI is designed to be discoverable and production-ready. Run `pitaya --help` Highlights - Strategy: `--strategy ` (use `-S key=value` for strategy params) -- Model: `--model ` (aliases resolved via `models.yaml` when applicable) +- Model: `--model ` - Plugin: `--plugin ` - Parallel runs: `--runs ` - TUI controls: `--no-tui`, `--output ` @@ -195,7 +195,7 @@ CLI overrides config; `-S` only affects the selected strategy. ## Models -Some plugins (e.g., Claude Code) validate model aliases via `models.yaml`. If an alias isn’t defined, Pitaya warns and passes the string through to the plugin. +Plugins accept model identifiers as provided. Claude Code commonly uses `sonnet`, `haiku`, or `opus`; OpenAI‑compatible providers accept their own model IDs. No `models.yaml` mapping is used. ## Docker & Plugins @@ -234,7 +234,7 @@ Some plugins (e.g., Claude Code) validate model aliases via `models.yaml`. If an - Cannot connect to Docker: start Docker Desktop / system service; run `docker info` - Missing credentials: set `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY` (Claude), or `OPENAI_API_KEY` (Codex) -- Model alias not found: add it to `models.yaml` or pass a direct model ID +- Model not recognized by the agent: pass a valid model ID for your provider - Slow or flaky network: use `--parallel conservative` or `--max-parallel ` - Clean stale state: `pitaya --prune` and `pitaya --clean-containers ` diff --git a/models.yaml b/models.yaml deleted file mode 100644 index ad2195f..0000000 --- a/models.yaml +++ /dev/null @@ -1,4 +0,0 @@ -models: - sonnet: sonnet - haiku: haiku - opus: opus diff --git a/src/cli.py b/src/cli.py index 77e375a..44707a5 100644 --- a/src/cli.py +++ b/src/cli.py @@ -144,7 +144,7 @@ def create_parser(cls) -> argparse.ArgumentParser: "--model", type=str, default="sonnet", - help="Model name/alias (plugin-agnostic; see models.yaml)", + help="Model name (plugin-agnostic)", ) g_model.add_argument( "--plugin", @@ -1753,23 +1753,7 @@ def _red(k, v): except Exception: pass - # Optional models.yaml drift note: only when alias missing/mismatch - try: - from .utils.model_mapping import load_model_mapping - - mapping, checksum = load_model_mapping() - alias = full_config.get("model") - if alias: - if alias not in mapping: - self.console.print( - f"[yellow]models.yaml: alias '{alias}' not found (checksum {checksum[:8]}). Using alias as-is.[/yellow]" - ) - elif mapping.get(alias) != alias: - self.console.print( - f"[dim]models.yaml drift: checksum {checksum[:8]} • {alias} -> {mapping.get(alias)}[/dim]" - ) - except Exception: - pass + # models.yaml alias mapping removed; no drift checks # Validate merged configuration; render compact error table on invalid if not self._validate_full_config(full_config, args): @@ -2803,14 +2787,7 @@ def info_line(title: str, msg: str): ) except Exception: pass - # models.yaml - try: - from .utils.model_mapping import load_model_mapping - - mapping, cs = load_model_mapping() - pass_line("models.yaml", f"checksum {cs[:8]}") - except Exception as e: - info_line("models.yaml", f"warn: {e}") + # models.yaml checks removed # SELinux / WSL2 hints try: import platform diff --git a/src/instance_runner/api.py b/src/instance_runner/api.py index fb4cf5f..9cd67c9 100644 --- a/src/instance_runner/api.py +++ b/src/instance_runner/api.py @@ -51,7 +51,6 @@ async def run_instance( session_group_key: Optional[str] = None, network_egress: Optional[str] = None, max_turns: Optional[int] = None, - model_mapping_checksum: Optional[str] = None, allow_overwrite_protected_refs: bool = False, allow_global_session_volume: bool = False, agent_cli_args: Optional[list[str]] = None, @@ -130,7 +129,6 @@ async def run_instance( skip_empty_import=skip_empty_import, network_egress=network_egress, max_turns=max_turns, - model_mapping_checksum=model_mapping_checksum, allow_overwrite_protected_refs=allow_overwrite_protected_refs, allow_global_session_volume=allow_global_session_volume, agent_cli_args=agent_cli_args, diff --git a/src/instance_runner/runner.py b/src/instance_runner/runner.py index dd3da5e..3787266 100644 --- a/src/instance_runner/runner.py +++ b/src/instance_runner/runner.py @@ -100,7 +100,6 @@ async def run_instance( skip_empty_import: bool = True, network_egress: Optional[str] = None, max_turns: Optional[int] = None, - model_mapping_checksum: Optional[str] = None, allow_overwrite_protected_refs: bool = False, allow_global_session_volume: bool = False, agent_cli_args: Optional[list[str]] = None, @@ -161,36 +160,9 @@ async def run_instance( # Use plugin's default image if not specified if docker_image is None: docker_image = plugin.docker_image - # Defensive model validation and resolution (via models.yaml mapping) - # For non-validated plugins, allow passthrough model ids to the plugin. + # Model IDs are passed through as provided; no models.yaml enforcement. + # Plugins can interpret model identifiers as they see fit. resolved_model_id = model - try: - from ..utils.model_mapping import load_model_mapping - - mapping, _checksum = load_model_mapping() - # Optional handshake: ensure checksum matches orchestrator's view - if model_mapping_checksum and _checksum != model_mapping_checksum: - raise ValidationError( - "models.yaml checksum mismatch between orchestration and runner" - ) - allowed_models = set(mapping.keys()) - if model in allowed_models: - resolved_model_id = mapping.get(model, model) - else: - # Only enforce strict validation for plugins that require mapping - if getattr(plugin, "name", "claude-code") == "claude-code": - raise ValidationError( - f"Unknown model: {model}. Allowed: {sorted(allowed_models)}" - ) - # For other plugins, keep original model (passthrough) - except Exception: - # Fallback strictness only for plugins that require mapping - if getattr(plugin, "name", "claude-code") == "claude-code": - allowed_models = {"sonnet", "opus", "haiku"} - if model not in allowed_models: - raise ValidationError( - f"Unknown model: {model}. Allowed: {sorted(allowed_models)}" - ) # Validate plugin environment # Convert AuthConfig to dict for plugin diff --git a/src/orchestration/orchestrator.py b/src/orchestration/orchestrator.py index f327b31..21f2d52 100644 --- a/src/orchestration/orchestrator.py +++ b/src/orchestration/orchestrator.py @@ -110,14 +110,7 @@ def __init__( self.force_commit: bool = bool(force_commit) # Whether operator explicitly set max_parallel (override guards/policies) self._explicit_max_parallel = bool(explicit_max_parallel) - # Load model mapping checksum for handshake (single-process still validates equality) - try: - from ..utils.model_mapping import load_model_mapping - - _map, _cs = load_model_mapping() - self._models_checksum = _cs - except Exception: - self._models_checksum = None + # models.yaml mapping removed; no checksum handshake # Log auth config for debugging if self.auth_config: @@ -1763,7 +1756,6 @@ def _write_last_active(): reuse_container=bool( (info.metadata or {}).get("reuse_container", True) ), - model_mapping_checksum=self._models_checksum, allow_overwrite_protected_refs=self.allow_overwrite_protected_refs, allow_global_session_volume=self.allow_global_session_volume, agent_cli_args=(info.metadata or {}).get("agent_cli_args"), diff --git a/src/utils/model_mapping.py b/src/utils/model_mapping.py deleted file mode 100644 index fe0685d..0000000 --- a/src/utils/model_mapping.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Model mapping loader with checksum support. - -Loads models.yaml with friendly->concrete model IDs and computes a checksum for handshake. -Falls back to a small identity mapping when the file is absent. -""" - -from pathlib import Path -from typing import Dict, Tuple, Optional -import hashlib -import json -import yaml - - -def _compute_checksum(obj: dict) -> str: - enc = json.dumps(obj, ensure_ascii=False, separators=(",", ":"), sort_keys=True) - return hashlib.sha256(enc.encode("utf-8")).hexdigest() - - -def load_model_mapping( - models_path: Optional[Path] = None, -) -> Tuple[Dict[str, str], str]: - """ - Load model mapping from models.yaml. - - Returns: - (mapping, checksum) - """ - if models_path is None: - models_path = Path("models.yaml") - - if not models_path.exists(): - # Fallback identity mapping used when no mapping file is present - mapping = {"sonnet": "sonnet", "haiku": "haiku", "opus": "opus"} - return mapping, _compute_checksum({"models": mapping}) - - with open(models_path, "r") as fh: - data = yaml.safe_load(fh) or {} - if ( - not isinstance(data, dict) - or "models" not in data - or not isinstance(data["models"], dict) - ): - raise ValueError("Invalid models.yaml: expected top-level 'models' mapping") - mapping = {str(k): str(v) for k, v in (data.get("models") or {}).items()} - checksum = _compute_checksum({"models": mapping}) - return mapping, checksum - - -def resolve_model(name: str, mapping: Optional[Dict[str, str]] = None) -> str: - if mapping is None: - mapping, _ = load_model_mapping() - if name not in mapping: - raise KeyError(f"Unknown model alias: {name}") - return mapping[name]