#!/usr/bin/env python3 """Run mini-SWE-agent in your local environment. This is the default executable `mini`.""" # Read this first: https://mini-swe-agent.com/latest/usage/mini/ (usage) import os from pathlib import Path from typing import Any import typer from rich.console import Console from minisweagent import global_config_dir from minisweagent.agents.interactive import InteractiveAgent, _multiline_prompt from minisweagent.config import builtin_config_dir, get_config_from_spec from minisweagent.environments.local import LocalEnvironment from minisweagent.models import get_model from minisweagent.run.utilities.config import configure_if_first_time from minisweagent.utils.serialize import UNSET, recursive_merge DEFAULT_CONFIG_FILE = Path(os.getenv("MSWEA_MINI_CONFIG_PATH", builtin_config_dir / "mini.yaml")) DEFAULT_OUTPUT_FILE = global_config_dir / "last_mini_run.traj.json" _HELP_TEXT = """Run mini-SWE-agent in your local environment. [not dim] More information about the usage: [bold green]https://mini-swe-agent.com/latest/usage/mini/[/bold green] [/not dim] """ _CONFIG_SPEC_HELP_TEXT = """Path to config files, filenames, or key-value pairs. [bold red]IMPORTANT:[/bold red] [red]If you set this option, the default config file will not be used.[/red] So you need to explicitly set it e.g., with [bold green]-c mini.yaml [/bold green] Multiple configs will be recursively merged. Examples: [bold red]-c model.model_kwargs.temperature=0[/bold red] [red]You forgot to add the default config file! See above.[/red] [bold green]-c mini.yaml -c model.model_kwargs.temperature=0.5[/bold green] [bold green]-c swebench.yaml agent.mode=yolo[/bold green] """ console = Console(highlight=False) app = typer.Typer(rich_markup_mode="rich") # fmt: off @app.command(help=_HELP_TEXT) def main( model_name: str | None = typer.Option(None, "-m", "--model", help="Model to use",), model_class: str | None = typer.Option(None, "--model-class", help="Model class to use (e.g., 'anthropic' or 'minisweagent.models.anthropic.AnthropicModel')", rich_help_panel="Advanced"), task: str | None = typer.Option(None, "-t", "--task", help="Task/problem statement", show_default=False), yolo: bool = typer.Option(False, "-y", "--yolo", help="Run without confirmation"), cost_limit: float | None = typer.Option(None, "-l", "--cost-limit", help="Cost limit. Set to 0 to disable."), config_spec: list[str] = typer.Option([str(DEFAULT_CONFIG_FILE)], "-c", "--config", help=_CONFIG_SPEC_HELP_TEXT), output: Path | None = typer.Option(DEFAULT_OUTPUT_FILE, "-o", "--output", help="Output trajectory file"), exit_immediately: bool = typer.Option(False, "--exit-immediately", help="Exit immediately when the agent wants to finish instead of prompting.", rich_help_panel="Advanced"), ) -> Any: # fmt: on configure_if_first_time() # Build the config from the command line arguments console.print(f"Building agent config from specs: [bold green]{config_spec}[/bold green]") configs = [get_config_from_spec(spec) for spec in config_spec] configs.append({ "agent": { "mode": "yolo" if yolo else UNSET, "cost_limit": cost_limit or UNSET, "confirm_exit": False if exit_immediately else UNSET, "output_path": output or UNSET, }, "model": { "model_class": model_class or UNSET, "model_name": model_name or UNSET, }, }) config = recursive_merge(*configs) if not task: console.print("[bold yellow]What do you want to do?") task = _multiline_prompt() console.print("[bold green]Got that, thanks![/bold green]") model = get_model(config=config.get("model", {})) env = LocalEnvironment(**config.get("environment", {})) agent = InteractiveAgent(model, env, **config.get("agent", {})) agent.run(task) # type: ignore[arg-type] if output: console.print(f"Saved trajectory to [bold green]'{output}'[/bold green]") return agent if __name__ == "__main__": app()