mini-swe-agent 1.4.1__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mini-swe-agent
3
- Version: 1.4.1
3
+ Version: 1.5.0
4
4
  Summary: Nano SWE Agent - A simple AI software engineering agent
5
5
  Author-email: Kilian Lieret <kilian.lieret@posteo.de>, "Carlos E. Jimenez" <carlosej@princeton.edu>
6
6
  License: MIT License
@@ -82,7 +82,7 @@ We now ask: **What if SWE-agent was 100x smaller, and still worked nearly as wel
82
82
 
83
83
  `mini` is for
84
84
 
85
- - **Researchers** who want to **benchmark, fine-tune or RL** without assumptions, bloat, or surprises
85
+ - **Researchers** who want to **[benchmark](https://swe-bench.com), [fine-tune](https://swesmith.com/) or RL** without assumptions, bloat, or surprises
86
86
  - **Developers** who like their tools like their scripts: **short, sharp, and readable**
87
87
  - **Engineers** who want something **trivial to sandbox & to deploy anywhere**
88
88
 
@@ -90,7 +90,7 @@ Here's some details:
90
90
 
91
91
  - **Minimal**: Just [100 lines of python](https://github.com/SWE-agent/mini-swe-agent/blob/main/src/minisweagent/agents/default.py) (+100 total for [env](https://github.com/SWE-agent/mini-swe-agent/blob/main/src/minisweagent/environments/local.py),
92
92
  [model](https://github.com/SWE-agent/mini-swe-agent/blob/main/src/minisweagent/models/litellm_model.py), [script](https://github.com/SWE-agent/mini-swe-agent/blob/main/src/minisweagent/run/hello_world.py)) — no fancy dependencies!
93
- - **Powerful:** Resolves 65% of GitHub issues in the [SWE-bench verified benchmark](https://www.swebench.com/) (with Claude Sonnet 4).
93
+ - **Powerful:** Resolves 65% of GitHub issues in the [SWE-bench verified benchmark](https://www.swebench.com/) ([leaderboard](https://swe-bench.com/)).
94
94
  - **Convenient:** Comes with UIs that turn this into your daily dev swiss army knife!
95
95
  - **Deployable:** In addition to local envs, you can use **docker**, **podman**, **singularity**, **apptainer**, and more
96
96
  - **Tested:** [![Codecov](https://img.shields.io/codecov/c/github/swe-agent/mini-swe-agent?style=flat-square)](https://codecov.io/gh/SWE-agent/mini-swe-agent)
@@ -116,6 +116,7 @@ In fact, mini-SWE-agent
116
116
 
117
117
  This makes it perfect as a baseline system and for a system that puts the language model (rather than
118
118
  the agent scaffold) in the middle of our attention.
119
+ You can see the result on the [SWE-bench (bash only)](https://www.swebench.com/) leaderboard, that evaluates the performance of different LMs with `mini`.
119
120
 
120
121
  </details>
121
122
 
@@ -274,7 +275,7 @@ Read more in our [documentation](https://mini-swe-agent.com/latest/):
274
275
  * [FAQ](https://mini-swe-agent.com/latest/faq/)
275
276
  * [Contribute!](https://mini-swe-agent.com/latest/contributing/)
276
277
 
277
- ## Bottom line
278
+ ## Attribution
278
279
 
279
280
  If you found this work helpful, please consider citing the [SWE-agent paper](https://arxiv.org/abs/2405.15793) in your work:
280
281
 
@@ -288,7 +289,7 @@ If you found this work helpful, please consider citing the [SWE-agent paper](htt
288
289
  }
289
290
  ```
290
291
 
291
- More agentic AI:
292
+ Our other projects:
292
293
 
293
294
  <div align="center">
294
295
  <a href="https://github.com/SWE-agent/SWE-agent"><img src="https://github.com/SWE-agent/mini-swe-agent/raw/main/docs/assets/sweagent_logo_text_below.svg" alt="SWE-agent" height="120px"></a>
@@ -1,16 +1,16 @@
1
- mini_swe_agent-1.4.1.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
2
- minisweagent/__init__.py,sha256=Fao4tu0VXB-yMYWz02SQVn7_hu-zj9am7sgP6swBWzs,1787
1
+ mini_swe_agent-1.5.0.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
2
+ minisweagent/__init__.py,sha256=psdAWbwaU43EuMjawhLNsRh0wDzqBW3_7EU2Psa2VHE,1787
3
3
  minisweagent/__main__.py,sha256=FIyAOiw--c3FQ2g240FOM1FdL0lk_PxSpixu0pQ7WFo,194
4
4
  minisweagent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  minisweagent/agents/__init__.py,sha256=cpjJLzg1IGxLM-tZpoMJV9S33ye13XtdBO0x7DU_Lrk,48
6
- minisweagent/agents/default.py,sha256=9LxgX3KWHFGkGE4FjGOZwK5oA8ykzg9KgmpgyqlEuoo,5460
6
+ minisweagent/agents/default.py,sha256=bqLMtEptn61zc_ptAIQkz_2fMI8hBoVpydVA84mPH8I,5471
7
7
  minisweagent/agents/interactive.py,sha256=7HW2cffaV5f66DIjxvtIbL8mo_S5aZSwgNLSmHp6VC0,7450
8
- minisweagent/agents/interactive_textual.py,sha256=-GrhQT3nh_bn4qyWFRMf6F7xEqMsRnQ-Ry5n3JJBjsc,16077
8
+ minisweagent/agents/interactive_textual.py,sha256=Ef2GTH2_9ujD95ukVf-hb7X6FCRVgvIdlJZvPvCAd-E,17629
9
9
  minisweagent/config/README.md,sha256=tPruhnQDhZ8ugc1FNPKk9tVMRltmmIjdYgvHCmN-3Hs,354
10
10
  minisweagent/config/__init__.py,sha256=UfORdQID1Ek_dduZlybUsIKJjihImkSqNU5tIjpw0hk,694
11
11
  minisweagent/config/default.yaml,sha256=AGhcIq6X6n5Fs71ufO3B6CtZ4PS877tCxkPkrWR5Ylg,4497
12
12
  minisweagent/config/github_issue.yaml,sha256=evvu3AJ52tXYSdami9_B8zfazOAE2r2XXkzVmScBoKc,4539
13
- minisweagent/config/mini.tcss,sha256=VgdZZqWElA5_nn4DJUFMpz8C7Gmi5s5XOtm7pfyM83Q,1122
13
+ minisweagent/config/mini.tcss,sha256=fmAP9cYAp2n7Ps2Dw3e-ZOGEF2E8JcwTgK1LDcis-x4,1141
14
14
  minisweagent/config/mini.yaml,sha256=WluQAx4AII9MFk3xDSzsJTosNJfgZti02niCYZWYq_A,5346
15
15
  minisweagent/config/extra/__init__.py,sha256=e1MoAlDn_wc9HnXNoncf1P-B4DQ-iRf6n7Q_txjZGRI,52
16
16
  minisweagent/config/extra/swebench.yaml,sha256=LNpTahpul6HL0HozgAAz-C6kpX3wZA7Tg8uE-ZmgrF4,7577
@@ -30,17 +30,17 @@ minisweagent/run/__init__.py,sha256=WIoYgHVl7iZF2YncrfV3IttupG6P5KogroKHKECka3A,
30
30
  minisweagent/run/github_issue.py,sha256=GWOkGM09jOYV93p6xIM_kKWmC1yP_d5lprafWlqoBN0,2748
31
31
  minisweagent/run/hello_world.py,sha256=erLnEwNmPFLxq3-8zyv66Vy1kIqMqQf97vISX7LrQXg,959
32
32
  minisweagent/run/inspector.py,sha256=QnY3oYzm-yq3w9Jzs112Lco2Rg84vSocAWrQRVz_1lc,7127
33
- minisweagent/run/mini.py,sha256=Q-B5LDFQtEoqxMC3cHpbQr8IW1qNpiTh_eaQpYHz954,4589
33
+ minisweagent/run/mini.py,sha256=Mz84iaJKg_5kvS84QQgrOXSmpMaKhIuFgeUbpyo_sSc,4750
34
34
  minisweagent/run/mini_extra.py,sha256=ecA1PnTWElpO60G9RktvVLtUOf3bZ_ESmnSttS6izhQ,1465
35
35
  minisweagent/run/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- minisweagent/run/extra/config.py,sha256=paMHfplhKsqNmzhCmozxhXWHvBzBCUlwUWD8N7ytCPc,3277
36
+ minisweagent/run/extra/config.py,sha256=ezUu8n2-h79cfphWXv-j9LQXfxzkxrF2aPlh7mObF7k,3545
37
37
  minisweagent/run/extra/swebench.py,sha256=m5_PZI4ojkUyCxzkkMtel_vlnYmjziWrXu73yHoZGFs,9688
38
38
  minisweagent/run/extra/swebench_single.py,sha256=L3Kk4G65o3MCPLMEwGNIs77-AFf6Lfc8o1oxrbN-ZWM,1991
39
39
  minisweagent/run/extra/utils/batch_progress.py,sha256=u__khJ-fipZLxTJu43LamGAtPUCqEZYEi8J7SfH7X6A,6211
40
40
  minisweagent/run/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- minisweagent/run/utils/save.py,sha256=q7omf7zYHg73k8-Iyp9w5YVSYvDAacRrh4X9L_4VhNM,942
42
- mini_swe_agent-1.4.1.dist-info/METADATA,sha256=wbuaTIHIypAE-u6bBYi2uXnmtXRv96vVmognFS6WG2U,13459
43
- mini_swe_agent-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- mini_swe_agent-1.4.1.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
45
- mini_swe_agent-1.4.1.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
46
- mini_swe_agent-1.4.1.dist-info/RECORD,,
41
+ minisweagent/run/utils/save.py,sha256=yI_hSU-GOaB7j8YeHBCc7Fhl4js9AyO9N5SC6p-nnu8,1606
42
+ mini_swe_agent-1.5.0.dist-info/METADATA,sha256=KOO2m4LFQ2LxvPC5_X1eX4y53EaJq43yoBR_GvUifJY,13684
43
+ mini_swe_agent-1.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ mini_swe_agent-1.5.0.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
45
+ mini_swe_agent-1.5.0.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
46
+ mini_swe_agent-1.5.0.dist-info/RECORD,,
minisweagent/__init__.py CHANGED
@@ -8,7 +8,7 @@ This file provides:
8
8
  unless you want the static type checking.
9
9
  """
10
10
 
11
- __version__ = "1.4.1"
11
+ __version__ = "1.5.0"
12
12
 
13
13
  import os
14
14
  from pathlib import Path
@@ -66,8 +66,8 @@ class DefaultAgent:
66
66
  cs = asdict(self.config) | asdict(self.env.config) | asdict(self.model.config) | platform.uname()._asdict()
67
67
  return Template(template).render(**kwargs, **cs, **os.environ)
68
68
 
69
- def add_message(self, role: str, content: str):
70
- self.messages.append({"role": role, "content": content})
69
+ def add_message(self, role: str, content: str, **kwargs):
70
+ self.messages.append({"role": role, "content": content, **kwargs})
71
71
 
72
72
  def run(self, task: str) -> tuple[str, str]:
73
73
  """Run step() until agent is finished. Return exit status & message"""
@@ -92,7 +92,7 @@ class DefaultAgent:
92
92
  if 0 < self.config.step_limit <= self.model.n_calls or 0 < self.config.cost_limit <= self.model.cost:
93
93
  raise LimitsExceeded()
94
94
  response = self.model.query(self.messages)
95
- self.add_message("assistant", response["content"])
95
+ self.add_message("assistant", **response)
96
96
  return response
97
97
 
98
98
  def get_observation(self, response: dict) -> dict:
@@ -9,17 +9,19 @@ import re
9
9
  import threading
10
10
  import time
11
11
  import traceback
12
+ from collections.abc import Iterable
12
13
  from dataclasses import dataclass, field
13
14
  from pathlib import Path
14
15
  from typing import Literal
15
16
 
16
17
  from rich.spinner import Spinner
17
18
  from rich.text import Text
18
- from textual.app import App, ComposeResult
19
+ from textual.app import App, ComposeResult, SystemCommand
19
20
  from textual.binding import Binding
20
21
  from textual.containers import Container, Vertical, VerticalScroll
21
22
  from textual.css.query import NoMatches
22
23
  from textual.events import Key
24
+ from textual.screen import Screen
23
25
  from textual.widgets import Footer, Header, Input, Static, TextArea
24
26
 
25
27
  from minisweagent.agents.default import AgentConfig, DefaultAgent, NonTerminatingException, Submitted
@@ -62,11 +64,12 @@ class TextualAgent(DefaultAgent):
62
64
  exit_status, result = super().run(task)
63
65
  except Exception as e:
64
66
  result = str(e)
67
+ self.app.call_from_thread(self.app.action_quit)
65
68
  print(traceback.format_exc())
66
- self.app.call_from_thread(self.app.on_agent_finished, "ERROR", result)
67
69
  return "ERROR", result
68
70
  else:
69
71
  self.app.call_from_thread(self.app.on_agent_finished, exit_status, result)
72
+ self.app.call_from_thread(self.app.action_quit)
70
73
  return exit_status, result
71
74
 
72
75
  def execute_action(self, action: dict) -> dict:
@@ -132,16 +135,10 @@ class SmartInputContainer(Container):
132
135
  self._input_event = threading.Event()
133
136
  self._input_result: str | None = None
134
137
 
135
- self._header_display = Static(
136
- "USER INPUT REQUESTED", id="input-header-display", classes="message-header input-request-header"
137
- )
138
- self._hint_text = Static(
139
- "[bold]Enter[/bold] to submit, [bold]Ctrl+T[/bold] to switch to multi-line input, [bold]Tab[/bold] to switch focus with other controls",
140
- classes="hint-text",
141
- )
138
+ self._header_display = Static(id="input-header-display", classes="message-header input-request-header")
139
+ self._hint_text = Static(classes="hint-text")
142
140
  self._single_input = Input(placeholder="Type your input...")
143
- self._multi_input = TextArea("", show_line_numbers=False, classes="multi-input")
144
-
141
+ self._multi_input = TextArea(show_line_numbers=False, classes="multi-input")
145
142
  self._input_elements_container = Vertical(
146
143
  self._header_display,
147
144
  self._hint_text,
@@ -180,7 +177,6 @@ class SmartInputContainer(Container):
180
177
  """Internal method to complete the input process."""
181
178
  self._input_result = input_text
182
179
  self.pending_prompt = None
183
- self._header_display.update("USER INPUT REQUESTED")
184
180
  self.display = False
185
181
  self._single_input.value = ""
186
182
  self._multi_input.text = ""
@@ -208,8 +204,13 @@ class SmartInputContainer(Container):
208
204
  self._multi_input.text = self._single_input.value
209
205
  self._single_input.display = False
210
206
  self._multi_input.display = True
211
-
207
+ self._hint_text.update(
208
+ "[reverse][bold][$accent] Ctrl+D [/][/][/] to submit, [reverse][bold][$accent] Tab [/][/][/] to switch focus with other controls"
209
+ )
212
210
  else:
211
+ self._hint_text.update(
212
+ "[reverse][bold][$accent] Enter [/][/][/] to submit, [reverse][bold][$accent] Ctrl+T [/][/][/] to switch to multi-line input, [reverse][bold][$accent] Tab [/][/][/] to switch focus with other controls",
213
+ )
213
214
  self._multi_input.display = False
214
215
  self._single_input.display = True
215
216
 
@@ -240,16 +241,22 @@ class SmartInputContainer(Container):
240
241
 
241
242
  class AgentApp(App):
242
243
  BINDINGS = [
243
- Binding("right,l", "next_step", "Step++"),
244
- Binding("left,h", "previous_step", "Step--"),
245
- Binding("0", "first_step", "Step=0"),
246
- Binding("$", "last_step", "Step=-1"),
247
- Binding("j,down", "scroll_down", "Scroll down"),
248
- Binding("k,up", "scroll_up", "Scroll up"),
249
- Binding("q", "quit", "Quit"),
250
- Binding("y", "yolo", "Switch to YOLO Mode"),
251
- Binding("c", "confirm", "Switch to Confirm Mode"),
252
- Binding("u", "human", "Switch to Human Mode"),
244
+ Binding("right,l", "next_step", "Step++", tooltip="Show next step of the agent"),
245
+ Binding("left,h", "previous_step", "Step--", tooltip="Show previous step of the agent"),
246
+ Binding("0", "first_step", "Step=0", tooltip="Show first step of the agent", show=False),
247
+ Binding("$", "last_step", "Step=-1", tooltip="Show last step of the agent", show=False),
248
+ Binding("j,down", "scroll_down", "Scroll down", show=False),
249
+ Binding("k,up", "scroll_up", "Scroll up", show=False),
250
+ Binding("q,ctrl+q", "quit", "Quit", tooltip="Quit the agent"),
251
+ Binding("y,ctrl+y", "yolo", "YOLO mode", tooltip="Switch to YOLO Mode (LM actions will execute immediately)"),
252
+ Binding(
253
+ "c",
254
+ "confirm",
255
+ "CONFIRM mode",
256
+ tooltip="Switch to Confirm Mode (LM proposes commands and you confirm/reject them)",
257
+ ),
258
+ Binding("u,ctrl+u", "human", "HUMAN mode", tooltip="Switch to Human Mode (you can now type commands directly)"),
259
+ Binding("f1,question_mark", "toggle_help_panel", "Help", tooltip="Show help"),
253
260
  ]
254
261
 
255
262
  def __init__(self, model, env, task: str, **kwargs):
@@ -369,6 +376,15 @@ class AgentApp(App):
369
376
  except NoMatches: # might be called when shutting down
370
377
  pass
371
378
 
379
+ # --- Other textual overrides ---
380
+
381
+ def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]:
382
+ # Add to palette
383
+ yield from super().get_system_commands(screen)
384
+ for binding in self.BINDINGS:
385
+ description = f"{binding.description} (shortcut {' OR '.join(binding.key.split(','))})" # type: ignore[attr-defined]
386
+ yield SystemCommand(description, binding.tooltip, binding.action) # type: ignore[attr-defined]
387
+
372
388
  # --- Textual bindings ---
373
389
 
374
390
  def action_yolo(self):
@@ -406,3 +422,9 @@ class AgentApp(App):
406
422
 
407
423
  def action_scroll_up(self) -> None:
408
424
  self._vscroll.scroll_to(y=self._vscroll.scroll_target_y - 15)
425
+
426
+ def action_toggle_help_panel(self) -> None:
427
+ if self.query("HelpPanel"):
428
+ self.action_hide_help_panel()
429
+ else:
430
+ self.action_show_help_panel()
@@ -40,6 +40,7 @@ Footer {
40
40
  }
41
41
 
42
42
  .hint-text{
43
+ margin-top: 1;
43
44
  margin-bottom: 1;
44
45
  padding: 0 1;
45
46
  color: white;
@@ -12,7 +12,7 @@ from dotenv import set_key, unset_key
12
12
  from prompt_toolkit import prompt
13
13
  from rich.console import Console
14
14
  from rich.rule import Rule
15
- from typer import Option, Typer
15
+ from typer import Argument, Typer
16
16
 
17
17
  from minisweagent import global_config_file
18
18
 
@@ -25,9 +25,7 @@ app = Typer(
25
25
  console = Console(highlight=False)
26
26
 
27
27
 
28
- _SETUP_HELP = """Welcome to Mini!
29
-
30
- To get started, we need to set up your global config file.
28
+ _SETUP_HELP = """To get started, we need to set up your global config file.
31
29
 
32
30
  You can edit it manually or use the [bold green]mini-extra config set[/bold green] or [bold green]mini-extra config edit[/bold green] commands.
33
31
 
@@ -39,6 +37,8 @@ Here's a few popular models and the required API keys:
39
37
  [bold green]o3[/bold green] ([bold green]OPENAI_API_KEY[/bold green])
40
38
 
41
39
  [bold yellow]You can leave any setting blank to skip it.[/bold yellow]
40
+
41
+ More information at https://mini-swe-agent.com/latest/quickstart/
42
42
  """
43
43
 
44
44
 
@@ -79,16 +79,22 @@ def setup():
79
79
 
80
80
  @app.command()
81
81
  def set(
82
- key: str = Option(..., help="The key to set", prompt=True),
83
- value: str = Option(..., help="The value to set", prompt=True),
82
+ key: str | None = Argument(None, help="The key to set"),
83
+ value: str | None = Argument(None, help="The value to set"),
84
84
  ):
85
85
  """Set a key in the global config file."""
86
+ if key is None:
87
+ key = prompt("Enter the key to set: ")
88
+ if value is None:
89
+ value = prompt(f"Enter the value for {key}: ")
86
90
  set_key(global_config_file, key, value)
87
91
 
88
92
 
89
93
  @app.command()
90
- def unset(key: str = Option(..., help="The key to unset")):
94
+ def unset(key: str | None = Argument(None, help="The key to unset")):
91
95
  """Unset a key in the global config file."""
96
+ if key is None:
97
+ key = prompt("Enter the key to unset: ")
92
98
  unset_key(global_config_file, key)
93
99
 
94
100
 
minisweagent/run/mini.py CHANGED
@@ -73,7 +73,12 @@ def run_textual(model: Model, env: Environment, agent_config: dict, task: str, o
73
73
 
74
74
  @app.command(help=_HELP_TEXT)
75
75
  def main(
76
- visual: bool = typer.Option(False, "-v", "--visual", help="Use visual (pager-style) UI (Textual)"),
76
+ visual: bool = typer.Option(
77
+ False,
78
+ "-v",
79
+ "--visual",
80
+ help="Toggle (pager-style) UI (Textual) depending on the MSWEA_VISUAL_MODE_DEFAULT environment setting",
81
+ ),
77
82
  model_name: str | None = typer.Option(
78
83
  None,
79
84
  "-m",
@@ -113,7 +118,7 @@ def main(
113
118
  model = get_model(model_name, config.get("model", {}))
114
119
  env = LocalEnvironment(**config.get("env", {}))
115
120
 
116
- if visual:
121
+ if visual and os.getenv("MSWEA_VISUAL_MODE_DEFAULT", "false") == "false":
117
122
  return run_textual(model, env, config["agent"], task, output) # type: ignore[arg-type]
118
123
  else:
119
124
  return run_interactive(model, env, config["agent"], task, output) # type: ignore[arg-type]
@@ -1,18 +1,31 @@
1
1
  import json
2
2
  from pathlib import Path
3
3
 
4
- from minisweagent import Agent
4
+ from minisweagent import Agent, __version__
5
5
 
6
6
 
7
7
  def save_traj(
8
8
  agent: Agent | None,
9
9
  path: Path,
10
10
  *,
11
+ print_path: bool = True,
11
12
  exit_status: str | None = None,
12
13
  result: str | None = None,
13
14
  extra_info: dict | None = None,
14
15
  **kwargs,
15
16
  ):
17
+ """Save the trajectory of the agent to a file.
18
+
19
+ Args:
20
+ agent: The agent to save the trajectory of.
21
+ path: The path to save the trajectory to.
22
+ print_path: Whether to print confirmation of path to the terminal.
23
+ exit_status: The exit status of the agent.
24
+ result: The result/submission of the agent.
25
+ extra_info: Extra information to save (will be merged into the info dict).
26
+ **kwargs: Additional information to save (will be merged into top level)
27
+
28
+ """
16
29
  data = {
17
30
  "info": {
18
31
  "exit_status": exit_status,
@@ -21,6 +34,7 @@ def save_traj(
21
34
  "instance_cost": 0.0,
22
35
  "api_calls": 0,
23
36
  },
37
+ "mini_version": __version__,
24
38
  },
25
39
  "messages": [],
26
40
  "trajectory_format": "mini-swe-agent-1",
@@ -34,3 +48,5 @@ def save_traj(
34
48
 
35
49
  path.parent.mkdir(parents=True, exist_ok=True)
36
50
  path.write_text(json.dumps(data, indent=2))
51
+ if print_path:
52
+ print(f"Saved trajectory to '{path}'")