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 fromaction_observation_template)format_error_templateaction_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
messagesfield were "hand-formatted", i.e., had acontent: strandrole: strfield. - In v2, we use the model's native output as the basis for the message and only add the
extrafield 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
-vflag for the alternate, textual basedmini -vCLI 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_KEYSwith::separator no longer supported. Use singleANTHROPIC_API_KEY. github_issuerun script: The dedicatedgithub_issue.pyrun script was removed. Use theminiCLI instead.MSWEA_MODEL_API_KEYenvironment variable: No longer used to override API keys.
Removed model classes:
anthropicmodel class: Removed. Uselitellmmodel 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 (requirespip install mini-swe-agent[modal])
Architecture changes
-
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.
-
Stateless models: Cost tracking moved to Agent. The
costandn_callsattributes were removed from the Model protocol. -
Pydantic configs:
AgentConfig(and other configs) changed fromdataclassto PydanticBaseModel. This requirespydantic >= 2.0. -
New protocol methods: All classes implement
get_template_vars()andserialize()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- useInterruptAgentFlowbase classTerminatingException- useInterruptAgentFlowbase classExecutionTimeoutError- 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().