Skip to content

v2.0 Migration Guide

Breaking Changes

mini-swe-agent v2.0 brings major improvements but requires migration. To stay with v1.x, pin your dependency: mini-swe-agent~=1.0. See the v1 documentation or the v1 branch on GitHub.

What's new

  • Tool calls: Native tool calling API support (now the default)
  • Multimodal input: Support for images and other content types

What do I need to change?

I only use the mini CLI with default configs

No changes needed.

I use custom configs

You might need to move some config keys. See Config changes.

I parse/analyze trajectories

Some metadata fields moved to extra. See Trajectory format.

I use the python bindings or built custom subclasses

You will need to refactor. An agent can refactor your code based on the instructions below.

Config changes

If you only changed system_template and instance_template, no changes needed.

Move from agent to model:

  • observation_template (renamed from action_observation_template)
  • format_error_template
  • action_regex (only for text-based parsing)

Removed:

  • timeout_template

Code block format changed:

Default action regex changed from ```bash to ```mswea_bash_command to avoid conflicts with bash examples in prompts.

Completion signal changed:

From echo MINI_SWE_AGENT_FINAL_OUTPUT to echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT.

New structure:

agent:
  system_template: "..."
  instance_template: "..."
  step_limit: 0
  cost_limit: 3.
environment:
  cwd: "..."
  timeout: 30
model:
  model_name: "..."
  observation_template: "..."
  format_error_template: "..."

CLI now supports multiple configs and key-value overrides:

mini -c mini.yaml -c model.model_kwargs.temperature=0.5
mini -c swebench.yaml -c agent.step_limit=100
mini -c mini.yaml -c /path/to/model.yaml

Warning

If you use -c, you will not load the full default config, so make sure to always specify your config file in addition to any overrides/updates.

Tool calling

v2.0 uses native tool calling by default (instead of regex-based text parsing).

What's the difference?

  • Tool calling (default): Model uses native tool calling API to invoke a "bash" tool
  • Text-based (legacy): Model outputs commands in markdown code blocks (or similar), regex extracts them

How to use it:

Tool calling is the default. The CLI uses mini.yaml and swebench.yaml which are configured for tool calling.

# Default (tool calling)
mini
python -m minisweagent.run.benchmarks.swebench
mini-extra swebench

# Text-based parsing
mini-extra swebench -c swebench_backticks.yaml
mini -c mini_textbased.yaml

For custom configs:

model:
  model_class: litellm  # tool calling (default)
  model_name: anthropic/claude-sonnet-4-5-20250929

# or for text-based:
model:
  model_class: litellm_textbased
  action_regex: "```mswea_bash_command\\s*\\n(.*?)\\n```"

Trajectory format

  • In v1, all messages in the trajectory's messages field were "hand-formatted", i.e., had a content: str and role: str field.
  • In v2, we use the model's native output as the basis for the message and only add the extra field to it.

If you use a model that uses the standard /completion endpoint, then you will still always have a content: str and role: str field. However, if you use the /response endpoint, then things might look different.

In other words: the exact message structure depends on the model you use.

  • Advantage: Any model message structure is supported
  • Disadvantage: If you parse trajectories, you might need to adapt to the message structure of the model you use.

Removed & renamed

Removed features:

  • "Visual" UI: The -v flag for the alternate, textual based mini -v CLI is no longer supported. This was a tough decision to make, but in the end the visual mode didn't see the adoption we wanted and is significantly more complex to maintain than the default interface.
  • Rotating API keys: ANTHROPIC_API_KEYS with :: separator no longer supported. Use single ANTHROPIC_API_KEY.
  • github_issue run script: The dedicated github_issue.py run script was removed. Use the mini CLI instead.
  • MSWEA_MODEL_API_KEY environment variable: No longer used to override API keys.

Removed model classes:

  • anthropic model class: Removed. Use litellm model class for Anthropic models (make sure that cache control is enabled).

Renamed model classes:

v1 name v2 name
litellm_response_api litellm_response
portkey_response_api portkey_response

New model classes:

Name Description
litellm_textbased Text-based parsing (regex) instead of tool calls
openrouter_textbased Text-based parsing for OpenRouter
openrouter_response OpenRouter with response API

New environment:

  • swerex_modal: Run environments on Modal (requires pip install mini-swe-agent[modal])

Architecture changes

  1. Responsibility shift: Models now parse actions and format observations. This enables switching between tool calls and text parsing by changing model classes. The Agent class is now a simpler coordinator.

  2. Stateless models: Cost tracking moved to Agent. The cost and n_calls attributes were removed from the Model protocol.

  3. Pydantic configs: AgentConfig (and other configs) changed from dataclass to Pydantic BaseModel. This requires pydantic >= 2.0.

  4. New protocol methods: All classes implement get_template_vars() and serialize() instead of requiring specific attributes.

Protocol changes

If you want to write a custom Model, Environment or Agent compatible with mini-swe-agent, you don't need to subclass anything. Rather, mini-swe-agent fully uses duck typing with protocols (tl;dr: as long as you implement the required methods, you can use any class as a Model, Environment or Agent). Config options like --config-class also take full import classes, so you can put your classes wherever you want.

Model protocol:

# Removed attributes
cost: float      # moved to Agent
n_calls: int     # moved to Agent

# New methods
def format_message(self, **kwargs) -> dict: ...
def format_observation_messages(self, message: dict, outputs: list[dict], template_vars: dict | None = None) -> list[dict]: ...
def serialize(self) -> dict: ...

Environment protocol:

# Changed signature
def execute(self, action: dict, cwd: str = "") -> dict[str, Any]: ...  # was: (command: str) -> dict[str, str]

# New method
def serialize(self) -> dict: ...

Agent protocol:

# Removed attributes (you don't need to implement these anymore, but you can)
model: Model
env: Environment
messages: list[dict]

# Changed return type
def run(self, task: str, **kwargs) -> dict: ...  # was: tuple[str, str]

# New method
def save(self, path: Path | None, *extra_dicts) -> dict: ...

Exception changes

All flow control exceptions now inherit from InterruptAgentFlow and moved to minisweagent.exceptions:

InterruptAgentFlow (base)
├── Submitted (task completed)
├── LimitsExceeded (cost/step limit)
├── FormatError (invalid model output)
└── UserInterruption (user cancelled)  # new

Removed exception classes:

  • NonTerminatingException - use InterruptAgentFlow base class
  • TerminatingException - use InterruptAgentFlow base class
  • ExecutionTimeoutError - removed (no longer used)
# Old
from minisweagent.agents.default import Submitted, FormatError

# New
from minisweagent.exceptions import Submitted, FormatError

Agent.run() return value

# Old (v1)
submission, exit_status = agent.run(task)  # tuple[str, str]

# New (v2)
result = agent.run(task)  # dict
submission = result["submission"]
exit_status = result["exit_status"]

The run() method returns the extra dict from the final exit message. For full trajectory data, use agent.save(path) or agent.serialize().