mini-swe-agent 1.17.5__py3-none-any.whl → 2.0.0a1__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.
- {mini_swe_agent-1.17.5.dist-info → mini_swe_agent-2.0.0a1.dist-info}/METADATA +36 -52
- mini_swe_agent-2.0.0a1.dist-info/RECORD +70 -0
- mini_swe_agent-2.0.0a1.dist-info/entry_points.txt +5 -0
- minisweagent/__init__.py +19 -26
- minisweagent/agents/default.py +128 -113
- minisweagent/agents/interactive.py +119 -58
- minisweagent/config/README.md +3 -4
- minisweagent/config/__init__.py +36 -1
- minisweagent/config/benchmarks/swebench.yaml +156 -0
- minisweagent/config/{extra/swebench.yaml → benchmarks/swebench_backticks.yaml} +69 -64
- minisweagent/config/benchmarks/swebench_modal.yaml +47 -0
- minisweagent/config/{extra → benchmarks}/swebench_xml.yaml +73 -70
- minisweagent/config/default.yaml +24 -21
- minisweagent/config/inspector.tcss +42 -0
- minisweagent/config/mini.yaml +53 -71
- minisweagent/config/{github_issue.yaml → mini_textbased.yaml} +43 -29
- minisweagent/environments/__init__.py +1 -0
- minisweagent/environments/docker.py +67 -20
- minisweagent/environments/extra/bubblewrap.py +86 -47
- minisweagent/environments/extra/swerex_docker.py +53 -20
- minisweagent/environments/extra/swerex_modal.py +90 -0
- minisweagent/environments/local.py +62 -21
- minisweagent/environments/singularity.py +59 -18
- minisweagent/exceptions.py +22 -0
- minisweagent/models/__init__.py +6 -7
- minisweagent/models/extra/roulette.py +20 -17
- minisweagent/models/litellm_model.py +90 -44
- minisweagent/models/litellm_response_model.py +80 -0
- minisweagent/models/litellm_textbased_model.py +45 -0
- minisweagent/models/openrouter_model.py +87 -45
- minisweagent/models/openrouter_response_model.py +123 -0
- minisweagent/models/openrouter_textbased_model.py +76 -0
- minisweagent/models/portkey_model.py +84 -42
- minisweagent/models/portkey_response_model.py +163 -0
- minisweagent/models/requesty_model.py +91 -41
- minisweagent/models/test_models.py +246 -19
- minisweagent/models/utils/actions_text.py +60 -0
- minisweagent/models/utils/actions_toolcall.py +102 -0
- minisweagent/models/utils/actions_toolcall_response.py +110 -0
- minisweagent/models/utils/anthropic_utils.py +28 -0
- minisweagent/models/utils/cache_control.py +15 -2
- minisweagent/models/utils/content_string.py +74 -0
- minisweagent/models/utils/openai_multimodal.py +50 -0
- minisweagent/models/utils/retry.py +25 -0
- minisweagent/run/benchmarks/__init__.py +1 -0
- minisweagent/run/{extra → benchmarks}/swebench.py +56 -35
- minisweagent/run/{extra → benchmarks}/swebench_single.py +36 -26
- minisweagent/run/{extra → benchmarks}/utils/batch_progress.py +1 -1
- minisweagent/run/hello_world.py +6 -0
- minisweagent/run/mini.py +54 -63
- minisweagent/run/utilities/__init__.py +1 -0
- minisweagent/run/{extra → utilities}/config.py +2 -0
- minisweagent/run/{inspector.py → utilities/inspector.py} +90 -11
- minisweagent/run/{mini_extra.py → utilities/mini_extra.py} +9 -5
- minisweagent/utils/serialize.py +26 -0
- mini_swe_agent-1.17.5.dist-info/RECORD +0 -61
- mini_swe_agent-1.17.5.dist-info/entry_points.txt +0 -5
- minisweagent/agents/interactive_textual.py +0 -450
- minisweagent/config/extra/swebench_roulette.yaml +0 -233
- minisweagent/config/mini.tcss +0 -86
- minisweagent/models/anthropic.py +0 -35
- minisweagent/models/litellm_response_api_model.py +0 -82
- minisweagent/models/portkey_response_api_model.py +0 -75
- minisweagent/models/utils/key_per_thread.py +0 -20
- minisweagent/models/utils/openai_utils.py +0 -41
- minisweagent/run/github_issue.py +0 -87
- minisweagent/run/utils/__init__.py +0 -0
- minisweagent/run/utils/save.py +0 -78
- {mini_swe_agent-1.17.5.dist-info → mini_swe_agent-2.0.0a1.dist-info}/WHEEL +0 -0
- {mini_swe_agent-1.17.5.dist-info → mini_swe_agent-2.0.0a1.dist-info}/licenses/LICENSE.md +0 -0
- {mini_swe_agent-1.17.5.dist-info → mini_swe_agent-2.0.0a1.dist-info}/top_level.txt +0 -0
- /minisweagent/config/{extra → benchmarks}/__init__.py +0 -0
- /minisweagent/run/{extra → benchmarks}/utils/__init__.py +0 -0
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
"""Run on a single SWE-Bench instance."""
|
|
2
2
|
|
|
3
|
-
import traceback
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
|
-
import yaml
|
|
8
6
|
from datasets import load_dataset
|
|
9
7
|
|
|
10
8
|
from minisweagent import global_config_dir
|
|
11
9
|
from minisweagent.agents.interactive import InteractiveAgent
|
|
12
|
-
from minisweagent.config import builtin_config_dir,
|
|
10
|
+
from minisweagent.config import builtin_config_dir, get_config_from_spec
|
|
13
11
|
from minisweagent.models import get_model
|
|
14
|
-
from minisweagent.run.
|
|
12
|
+
from minisweagent.run.benchmarks.swebench import (
|
|
15
13
|
DATASET_MAPPING,
|
|
16
14
|
get_sb_environment,
|
|
17
15
|
)
|
|
18
|
-
from minisweagent.run.utils.save import save_traj
|
|
19
16
|
from minisweagent.utils.log import logger
|
|
17
|
+
from minisweagent.utils.serialize import recursive_merge
|
|
18
|
+
|
|
19
|
+
DEFAULT_OUTPUT_FILE = global_config_dir / "last_swebench_single_run.traj.json"
|
|
20
|
+
DEFAULT_CONFIG_FILE = builtin_config_dir / "benchmarks" / "swebench.yaml"
|
|
20
21
|
|
|
21
22
|
app = typer.Typer(add_completion=False)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
_CONFIG_SPEC_HELP_TEXT = """Path to config files, filenames, or key-value pairs.
|
|
25
|
+
|
|
26
|
+
[bold red]IMPORTANT:[/bold red] [red]If you set this option, the default config file will not be used.[/red]
|
|
27
|
+
So you need to explicitly set it e.g., with [bold green]-c swebench.yaml <other options>[/bold green]
|
|
28
|
+
|
|
29
|
+
Multiple configs will be recursively merged.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
|
|
33
|
+
[bold red]-c model.model_kwargs.temperature=0[/bold red] [red]You forgot to add the default config file! See above.[/red]
|
|
34
|
+
|
|
35
|
+
[bold green]-c swebench.yaml -c model.model_kwargs.temperature=0.5[/bold green]
|
|
36
|
+
|
|
37
|
+
[bold green]-c swebench.yaml -c agent.mode=yolo[/bold green]
|
|
38
|
+
"""
|
|
24
39
|
|
|
25
40
|
|
|
26
41
|
# fmt: off
|
|
@@ -31,10 +46,10 @@ def main(
|
|
|
31
46
|
instance_spec: str = typer.Option(0, "-i", "--instance", help="SWE-Bench instance ID or index", rich_help_panel="Data selection"),
|
|
32
47
|
model_name: str | None = typer.Option(None, "-m", "--model", help="Model to use", rich_help_panel="Basic"),
|
|
33
48
|
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"),
|
|
34
|
-
|
|
49
|
+
config_spec: list[str] = typer.Option([str(DEFAULT_CONFIG_FILE)], "-c", "--config", help=_CONFIG_SPEC_HELP_TEXT, rich_help_panel="Basic"),
|
|
35
50
|
environment_class: str | None = typer.Option(None, "--environment-class", rich_help_panel="Advanced"),
|
|
36
51
|
exit_immediately: bool = typer.Option( False, "--exit-immediately", help="Exit immediately when the agent wants to finish instead of prompting.", rich_help_panel="Basic"),
|
|
37
|
-
output: Path = typer.Option(
|
|
52
|
+
output: Path = typer.Option(DEFAULT_OUTPUT_FILE, "-o", "--output", help="Output trajectory file", rich_help_panel="Basic"),
|
|
38
53
|
) -> None:
|
|
39
54
|
# fmt: on
|
|
40
55
|
"""Run on a single SWE-Bench instance."""
|
|
@@ -48,31 +63,26 @@ def main(
|
|
|
48
63
|
instance_spec = sorted(instances.keys())[int(instance_spec)]
|
|
49
64
|
instance: dict = instances[instance_spec] # type: ignore
|
|
50
65
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
logger.info(f"Building agent config from specs: {config_spec}")
|
|
67
|
+
configs = [get_config_from_spec(spec) for spec in config_spec]
|
|
68
|
+
configs.append({"agent": {"mode": "yolo"}})
|
|
54
69
|
if environment_class is not None:
|
|
55
|
-
|
|
70
|
+
configs.append({"environment": {"environment_class": environment_class}})
|
|
56
71
|
if model_class is not None:
|
|
57
|
-
|
|
72
|
+
configs.append({"model": {"model_class": model_class}})
|
|
73
|
+
if model_name is not None:
|
|
74
|
+
configs.append({"model": {"model_name": model_name}})
|
|
58
75
|
if exit_immediately:
|
|
59
|
-
|
|
76
|
+
configs.append({"agent": {"confirm_exit": False}})
|
|
77
|
+
config = recursive_merge(*configs)
|
|
78
|
+
|
|
60
79
|
env = get_sb_environment(config, instance)
|
|
61
80
|
agent = InteractiveAgent(
|
|
62
|
-
get_model(
|
|
81
|
+
get_model(config=config.get("model", {})),
|
|
63
82
|
env,
|
|
64
|
-
**
|
|
83
|
+
**config.get("agent", {}),
|
|
65
84
|
)
|
|
66
|
-
|
|
67
|
-
exit_status, result, extra_info = None, None, None
|
|
68
|
-
try:
|
|
69
|
-
exit_status, result = agent.run(instance["problem_statement"]) # type: ignore[arg-type]
|
|
70
|
-
except Exception as e:
|
|
71
|
-
logger.error(f"Error processing instance {instance_spec}: {e}", exc_info=True)
|
|
72
|
-
exit_status, result = type(e).__name__, str(e)
|
|
73
|
-
extra_info = {"traceback": traceback.format_exc()}
|
|
74
|
-
finally:
|
|
75
|
-
save_traj(agent, output, exit_status=exit_status, result=result, extra_info=extra_info) # type: ignore[arg-type]
|
|
85
|
+
agent.run(instance["problem_statement"])
|
|
76
86
|
|
|
77
87
|
|
|
78
88
|
if __name__ == "__main__":
|
|
@@ -143,8 +143,8 @@ class RunBatchProgressManager:
|
|
|
143
143
|
)
|
|
144
144
|
|
|
145
145
|
def on_instance_end(self, instance_id: str, exit_status: str | None) -> None:
|
|
146
|
-
self._instances_by_exit_status[exit_status].append(instance_id)
|
|
147
146
|
with self._lock:
|
|
147
|
+
self._instances_by_exit_status[exit_status].append(instance_id)
|
|
148
148
|
try:
|
|
149
149
|
self._task_progress_bar.remove_task(self._spinner_tasks[instance_id])
|
|
150
150
|
except KeyError:
|
minisweagent/run/hello_world.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""This is the simplest possible example of how to use mini-SWE-agent with python bindings.
|
|
2
|
+
For a more complete example, see mini.py
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
1
6
|
import os
|
|
2
7
|
from pathlib import Path
|
|
3
8
|
|
|
@@ -23,6 +28,7 @@ def main(
|
|
|
23
28
|
prompt="What model do you want to use?",
|
|
24
29
|
),
|
|
25
30
|
) -> DefaultAgent:
|
|
31
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
26
32
|
agent = DefaultAgent(
|
|
27
33
|
LitellmModel(model_name=model_name),
|
|
28
34
|
LocalEnvironment(),
|
minisweagent/run/mini.py
CHANGED
|
@@ -4,103 +4,94 @@
|
|
|
4
4
|
# Read this first: https://mini-swe-agent.com/latest/usage/mini/ (usage)
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
|
-
import traceback
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import Any
|
|
10
9
|
|
|
11
10
|
import typer
|
|
12
|
-
import yaml
|
|
13
|
-
from prompt_toolkit.formatted_text import HTML
|
|
14
|
-
from prompt_toolkit.history import FileHistory
|
|
15
|
-
from prompt_toolkit.shortcuts import PromptSession
|
|
16
11
|
from rich.console import Console
|
|
17
12
|
|
|
18
13
|
from minisweagent import global_config_dir
|
|
19
|
-
from minisweagent.agents.interactive import InteractiveAgent
|
|
20
|
-
from minisweagent.
|
|
21
|
-
from minisweagent.config import builtin_config_dir, get_config_path
|
|
14
|
+
from minisweagent.agents.interactive import InteractiveAgent, _multiline_prompt
|
|
15
|
+
from minisweagent.config import builtin_config_dir, get_config_from_spec
|
|
22
16
|
from minisweagent.environments.local import LocalEnvironment
|
|
23
17
|
from minisweagent.models import get_model
|
|
24
|
-
from minisweagent.run.
|
|
25
|
-
from minisweagent.
|
|
26
|
-
from minisweagent.utils.log import logger
|
|
18
|
+
from minisweagent.run.utilities.config import configure_if_first_time
|
|
19
|
+
from minisweagent.utils.serialize import UNSET, recursive_merge
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console = Console(highlight=False)
|
|
31
|
-
app = typer.Typer(rich_markup_mode="rich")
|
|
32
|
-
prompt_session = PromptSession(history=FileHistory(global_config_dir / "mini_task_history.txt"))
|
|
33
|
-
_HELP_TEXT = """Run mini-SWE-agent in your local environment.
|
|
21
|
+
DEFAULT_CONFIG_FILE = Path(os.getenv("MSWEA_MINI_CONFIG_PATH", builtin_config_dir / "mini.yaml"))
|
|
22
|
+
DEFAULT_OUTPUT_FILE = global_config_dir / "last_mini_run.traj.json"
|
|
34
23
|
|
|
35
|
-
[not dim]
|
|
36
|
-
There are two different user interfaces:
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
[bold green]mini -v[/bold green] Pager-style interface (Textual)
|
|
25
|
+
_HELP_TEXT = """Run mini-SWE-agent in your local environment.
|
|
40
26
|
|
|
27
|
+
[not dim]
|
|
41
28
|
More information about the usage: [bold green]https://mini-swe-agent.com/latest/usage/mini/[/bold green]
|
|
42
29
|
[/not dim]
|
|
43
30
|
"""
|
|
44
31
|
|
|
32
|
+
_CONFIG_SPEC_HELP_TEXT = """Path to config files, filenames, or key-value pairs.
|
|
33
|
+
|
|
34
|
+
[bold red]IMPORTANT:[/bold red] [red]If you set this option, the default config file will not be used.[/red]
|
|
35
|
+
So you need to explicitly set it e.g., with [bold green]-c mini.yaml <other options>[/bold green]
|
|
36
|
+
|
|
37
|
+
Multiple configs will be recursively merged.
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
|
|
41
|
+
[bold red]-c model.model_kwargs.temperature=0[/bold red] [red]You forgot to add the default config file! See above.[/red]
|
|
42
|
+
|
|
43
|
+
[bold green]-c mini.yaml -c model.model_kwargs.temperature=0.5[/bold green]
|
|
44
|
+
|
|
45
|
+
[bold green]-c swebench.yaml agent.mode=yolo[/bold green]
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
console = Console(highlight=False)
|
|
49
|
+
app = typer.Typer(rich_markup_mode="rich")
|
|
50
|
+
|
|
45
51
|
|
|
46
52
|
# fmt: off
|
|
47
53
|
@app.command(help=_HELP_TEXT)
|
|
48
54
|
def main(
|
|
49
|
-
|
|
50
|
-
model_name: str | None = typer.Option( None, "-m", "--model", help="Model to use",),
|
|
55
|
+
model_name: str | None = typer.Option(None, "-m", "--model", help="Model to use",),
|
|
51
56
|
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"),
|
|
52
57
|
task: str | None = typer.Option(None, "-t", "--task", help="Task/problem statement", show_default=False),
|
|
53
58
|
yolo: bool = typer.Option(False, "-y", "--yolo", help="Run without confirmation"),
|
|
54
59
|
cost_limit: float | None = typer.Option(None, "-l", "--cost-limit", help="Cost limit. Set to 0 to disable."),
|
|
55
|
-
config_spec:
|
|
56
|
-
output: Path | None = typer.Option(
|
|
57
|
-
exit_immediately: bool = typer.Option(
|
|
60
|
+
config_spec: list[str] = typer.Option([str(DEFAULT_CONFIG_FILE)], "-c", "--config", help=_CONFIG_SPEC_HELP_TEXT),
|
|
61
|
+
output: Path | None = typer.Option(DEFAULT_OUTPUT_FILE, "-o", "--output", help="Output trajectory file"),
|
|
62
|
+
exit_immediately: bool = typer.Option(False, "--exit-immediately", help="Exit immediately when the agent wants to finish instead of prompting.", rich_help_panel="Advanced"),
|
|
58
63
|
) -> Any:
|
|
59
64
|
# fmt: on
|
|
60
65
|
configure_if_first_time()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
config
|
|
66
|
+
|
|
67
|
+
# Build the config from the command line arguments
|
|
68
|
+
console.print(f"Building agent config from specs: [bold green]{config_spec}[/bold green]")
|
|
69
|
+
configs = [get_config_from_spec(spec) for spec in config_spec]
|
|
70
|
+
configs.append({
|
|
71
|
+
"agent": {
|
|
72
|
+
"mode": "yolo" if yolo else UNSET,
|
|
73
|
+
"cost_limit": cost_limit or UNSET,
|
|
74
|
+
"confirm_exit": False if exit_immediately else UNSET,
|
|
75
|
+
"output_path": output or UNSET,
|
|
76
|
+
},
|
|
77
|
+
"model": {
|
|
78
|
+
"model_class": model_class or UNSET,
|
|
79
|
+
"model_name": model_name or UNSET,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
config = recursive_merge(*configs)
|
|
64
83
|
|
|
65
84
|
if not task:
|
|
66
85
|
console.print("[bold yellow]What do you want to do?")
|
|
67
|
-
task =
|
|
68
|
-
"",
|
|
69
|
-
multiline=True,
|
|
70
|
-
bottom_toolbar=HTML(
|
|
71
|
-
"Submit task: <b fg='yellow' bg='black'>Esc+Enter</b> | "
|
|
72
|
-
"Navigate history: <b fg='yellow' bg='black'>Arrow Up/Down</b> | "
|
|
73
|
-
"Search history: <b fg='yellow' bg='black'>Ctrl+R</b>"
|
|
74
|
-
),
|
|
75
|
-
)
|
|
86
|
+
task = _multiline_prompt()
|
|
76
87
|
console.print("[bold green]Got that, thanks![/bold green]")
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
config.setdefault("agent", {})["mode"] = "yolo"
|
|
80
|
-
if cost_limit is not None:
|
|
81
|
-
config.setdefault("agent", {})["cost_limit"] = cost_limit
|
|
82
|
-
if exit_immediately:
|
|
83
|
-
config.setdefault("agent", {})["confirm_exit"] = False
|
|
84
|
-
if model_class is not None:
|
|
85
|
-
config.setdefault("model", {})["model_class"] = model_class
|
|
86
|
-
model = get_model(model_name, config.get("model", {}))
|
|
89
|
+
model = get_model(config=config.get("model", {}))
|
|
87
90
|
env = LocalEnvironment(**config.get("environment", {}))
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
agent_class = TextualAgent
|
|
93
|
-
|
|
94
|
-
agent = agent_class(model, env, **config.get("agent", {}))
|
|
95
|
-
exit_status, result, extra_info = None, None, None
|
|
96
|
-
try:
|
|
97
|
-
exit_status, result = agent.run(task) # type: ignore[arg-type]
|
|
98
|
-
except Exception as e:
|
|
99
|
-
logger.error(f"Error running agent: {e}", exc_info=True)
|
|
100
|
-
exit_status, result = type(e).__name__, str(e)
|
|
101
|
-
extra_info = {"traceback": traceback.format_exc()}
|
|
102
|
-
finally:
|
|
103
|
-
save_traj(agent, output, exit_status=exit_status, result=result, extra_info=extra_info) # type: ignore[arg-type]
|
|
91
|
+
agent = InteractiveAgent(model, env, **config.get("agent", {}))
|
|
92
|
+
agent.run(task) # type: ignore[arg-type]
|
|
93
|
+
if output:
|
|
94
|
+
console.print(f"Saved trajectory to [bold green]'{output}'[/bold green]")
|
|
104
95
|
return agent
|
|
105
96
|
|
|
106
97
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utility modules for mini-SWE-agent (config management, inspector, etc.)."""
|
|
@@ -7,36 +7,92 @@ More information about the usage: [bold green] https://mini-swe-agent.com/latest
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import tempfile
|
|
10
12
|
from pathlib import Path
|
|
11
13
|
|
|
12
14
|
import typer
|
|
13
15
|
from rich.text import Text
|
|
14
16
|
from textual.app import App, ComposeResult
|
|
15
17
|
from textual.binding import Binding
|
|
18
|
+
from textual.command import DiscoveryHit, Hit, Hits, Provider
|
|
16
19
|
from textual.containers import Container, Vertical, VerticalScroll
|
|
17
20
|
from textual.widgets import Footer, Header, Static
|
|
18
21
|
|
|
19
|
-
from minisweagent.
|
|
22
|
+
from minisweagent.models.utils.content_string import get_content_string
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _messages_to_steps(messages: list[dict]) -> list[list[dict]]:
|
|
26
|
+
"""Group messages into "pages" as shown by the UI."""
|
|
27
|
+
steps = []
|
|
28
|
+
current_step = []
|
|
29
|
+
for message in messages:
|
|
30
|
+
# Start new step with new tool uses
|
|
31
|
+
if message.get("extra", {}).get("actions") or message.get("role") == "assistant":
|
|
32
|
+
steps.append(current_step)
|
|
33
|
+
current_step = [message]
|
|
34
|
+
else:
|
|
35
|
+
current_step.append(message)
|
|
36
|
+
if current_step:
|
|
37
|
+
steps.append(current_step)
|
|
38
|
+
return steps
|
|
39
|
+
|
|
20
40
|
|
|
21
41
|
app = typer.Typer(rich_markup_mode="rich", add_completion=False)
|
|
22
42
|
|
|
23
43
|
|
|
44
|
+
class BindingCommandProvider(Provider):
|
|
45
|
+
"""Provide bindings as commands in the palette."""
|
|
46
|
+
|
|
47
|
+
COMMAND_DESCRIPTIONS = {
|
|
48
|
+
"next_step": "Next step in the current trajectory",
|
|
49
|
+
"previous_step": "Previous step in the current trajectory",
|
|
50
|
+
"first_step": "First step in the current trajectory",
|
|
51
|
+
"last_step": "Last step in the current trajectory",
|
|
52
|
+
"scroll_down": "Scroll down",
|
|
53
|
+
"scroll_up": "Scroll up",
|
|
54
|
+
"next_trajectory": "Next trajectory",
|
|
55
|
+
"previous_trajectory": "Previous trajectory",
|
|
56
|
+
"open_in_jless": "Open the current step in jless",
|
|
57
|
+
"open_in_jless_all": "Open the entire trajectory in jless",
|
|
58
|
+
"quit": "Quit the inspector",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async def discover(self) -> Hits:
|
|
62
|
+
app = self.app
|
|
63
|
+
for binding in app.BINDINGS:
|
|
64
|
+
desc = self.COMMAND_DESCRIPTIONS.get(binding.action, binding.description)
|
|
65
|
+
yield DiscoveryHit(desc, lambda b=binding: app.run_action(b.action))
|
|
66
|
+
|
|
67
|
+
async def search(self, query: str) -> Hits:
|
|
68
|
+
matcher = self.matcher(query)
|
|
69
|
+
app = self.app
|
|
70
|
+
for binding in app.BINDINGS:
|
|
71
|
+
desc = self.COMMAND_DESCRIPTIONS.get(binding.action, binding.description)
|
|
72
|
+
score = matcher.match(desc)
|
|
73
|
+
if score > 0:
|
|
74
|
+
yield Hit(score, matcher.highlight(desc), lambda b=binding: app.run_action(b.action))
|
|
75
|
+
|
|
76
|
+
|
|
24
77
|
class TrajectoryInspector(App):
|
|
78
|
+
COMMANDS = {BindingCommandProvider}
|
|
25
79
|
BINDINGS = [
|
|
26
80
|
Binding("right,l", "next_step", "Step++"),
|
|
27
81
|
Binding("left,h", "previous_step", "Step--"),
|
|
28
82
|
Binding("0", "first_step", "Step=0"),
|
|
29
83
|
Binding("$", "last_step", "Step=-1"),
|
|
30
|
-
Binding("j,down", "scroll_down", "
|
|
31
|
-
Binding("k,up", "scroll_up", "
|
|
32
|
-
Binding("L", "next_trajectory", "
|
|
33
|
-
Binding("H", "previous_trajectory", "
|
|
84
|
+
Binding("j,down", "scroll_down", "↓"),
|
|
85
|
+
Binding("k,up", "scroll_up", "↑"),
|
|
86
|
+
Binding("L", "next_trajectory", "Traj++"),
|
|
87
|
+
Binding("H", "previous_trajectory", "Traj--"),
|
|
88
|
+
Binding("e", "open_in_jless", "Jless"),
|
|
89
|
+
Binding("E", "open_in_jless_all", "Jless (all)"),
|
|
34
90
|
Binding("q", "quit", "Quit"),
|
|
35
91
|
]
|
|
36
92
|
|
|
37
93
|
def __init__(self, trajectory_files: list[Path]):
|
|
38
94
|
css_path = os.environ.get(
|
|
39
|
-
"MSWEA_INSPECTOR_STYLE_PATH", str(Path(__file__).parent.parent / "config" / "
|
|
95
|
+
"MSWEA_INSPECTOR_STYLE_PATH", str(Path(__file__).parent.parent.parent / "config" / "inspector.tcss")
|
|
40
96
|
)
|
|
41
97
|
self.__class__.CSS = Path(css_path).read_text()
|
|
42
98
|
|
|
@@ -142,13 +198,10 @@ class TrajectoryInspector(App):
|
|
|
142
198
|
return
|
|
143
199
|
|
|
144
200
|
for message in self.steps[self.i_step]:
|
|
145
|
-
|
|
146
|
-
content_str = "\n".join([item["text"] for item in message["content"]])
|
|
147
|
-
else:
|
|
148
|
-
content_str = str(message["content"])
|
|
201
|
+
content_str = get_content_string(message)
|
|
149
202
|
message_container = Vertical(classes="message-container")
|
|
150
203
|
container.mount(message_container)
|
|
151
|
-
role = message
|
|
204
|
+
role = message.get("role") or message.get("type") or "unknown"
|
|
152
205
|
message_container.mount(Static(role.upper(), classes="message-header"))
|
|
153
206
|
message_container.mount(Static(Text(content_str, no_wrap=False), classes="message-content"))
|
|
154
207
|
|
|
@@ -186,6 +239,32 @@ class TrajectoryInspector(App):
|
|
|
186
239
|
vs = self.query_one(VerticalScroll)
|
|
187
240
|
vs.scroll_to(y=vs.scroll_target_y - 15)
|
|
188
241
|
|
|
242
|
+
def _open_in_jless(self, path: Path) -> None:
|
|
243
|
+
"""Open file in jless."""
|
|
244
|
+
with self.suspend():
|
|
245
|
+
try:
|
|
246
|
+
subprocess.run(["jless", path])
|
|
247
|
+
except FileNotFoundError:
|
|
248
|
+
self.notify("jless not found. Install with: `brew install jless`", severity="error")
|
|
249
|
+
|
|
250
|
+
def action_open_in_jless(self) -> None:
|
|
251
|
+
"""Open the current step's messages in jless."""
|
|
252
|
+
if not self.steps:
|
|
253
|
+
self.notify("No messages to display", severity="warning")
|
|
254
|
+
return
|
|
255
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
256
|
+
json.dump(self.steps[self.i_step], f, indent=2)
|
|
257
|
+
temp_path = Path(f.name)
|
|
258
|
+
self._open_in_jless(temp_path)
|
|
259
|
+
temp_path.unlink()
|
|
260
|
+
|
|
261
|
+
def action_open_in_jless_all(self) -> None:
|
|
262
|
+
"""Open the entire trajectory in jless."""
|
|
263
|
+
if not self.trajectory_files:
|
|
264
|
+
self.notify("No trajectory to display", severity="warning")
|
|
265
|
+
return
|
|
266
|
+
self._open_in_jless(self.trajectory_files[self.i_trajectory])
|
|
267
|
+
|
|
189
268
|
|
|
190
269
|
@app.command(help=__doc__)
|
|
191
270
|
def main(
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
+
"""This is the central entry point to the mini-extra script. Use subcommands
|
|
4
|
+
to invoke other command line utilities like running on benchmarks, editing config,
|
|
5
|
+
inspecting trajectories, etc.
|
|
6
|
+
"""
|
|
7
|
+
|
|
3
8
|
import sys
|
|
4
9
|
from importlib import import_module
|
|
5
10
|
|
|
6
11
|
from rich.console import Console
|
|
7
12
|
|
|
8
13
|
subcommands = [
|
|
9
|
-
("minisweagent.run.
|
|
10
|
-
("minisweagent.run.inspector", ["inspect", "i", "inspector"], "Run inspector (browse trajectories)"),
|
|
11
|
-
("minisweagent.run.
|
|
12
|
-
("minisweagent.run.
|
|
13
|
-
("minisweagent.run.extra.swebench_single", ["swebench-single"], "Evaluate on SWE-bench (single instance)"),
|
|
14
|
+
("minisweagent.run.utilities.config", ["config"], "Manage the global config file"),
|
|
15
|
+
("minisweagent.run.utilities.inspector", ["inspect", "i", "inspector"], "Run inspector (browse trajectories)"),
|
|
16
|
+
("minisweagent.run.benchmarks.swebench", ["swebench"], "Evaluate on SWE-bench (batch mode)"),
|
|
17
|
+
("minisweagent.run.benchmarks.swebench_single", ["swebench-single"], "Evaluate on SWE-bench (single instance)"),
|
|
14
18
|
]
|
|
15
19
|
|
|
16
20
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
UNSET = object()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def recursive_merge(*dictionaries: dict | None) -> dict:
|
|
7
|
+
"""Merge multiple dictionaries recursively.
|
|
8
|
+
|
|
9
|
+
Later dictionaries take precedence over earlier ones.
|
|
10
|
+
Nested dictionaries are merged recursively.
|
|
11
|
+
UNSET values are skipped.
|
|
12
|
+
"""
|
|
13
|
+
if not dictionaries:
|
|
14
|
+
return {}
|
|
15
|
+
result: dict[str, Any] = {}
|
|
16
|
+
for d in dictionaries:
|
|
17
|
+
if d is None:
|
|
18
|
+
continue
|
|
19
|
+
for key, value in d.items():
|
|
20
|
+
if value is UNSET:
|
|
21
|
+
continue
|
|
22
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
23
|
+
result[key] = recursive_merge(result[key], value)
|
|
24
|
+
else:
|
|
25
|
+
result[key] = value
|
|
26
|
+
return result
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
mini_swe_agent-1.17.5.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
|
|
2
|
-
minisweagent/__init__.py,sha256=mS0JbWq4g29gBFL9_t60714xVzV15INGgXquluOK4RE,2621
|
|
3
|
-
minisweagent/__main__.py,sha256=FIyAOiw--c3FQ2g240FOM1FdL0lk_PxSpixu0pQ7WFo,194
|
|
4
|
-
minisweagent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
minisweagent/agents/__init__.py,sha256=cpjJLzg1IGxLM-tZpoMJV9S33ye13XtdBO0x7DU_Lrk,48
|
|
6
|
-
minisweagent/agents/default.py,sha256=No2GmsgUuWzqxUswFKICJNViVDx3GSjecAgK6NP0Tu4,6176
|
|
7
|
-
minisweagent/agents/interactive.py,sha256=NBeNamRuqww9ZRhOg1q8xPO9ziUw2gpAVV6hCPbpBxU,7470
|
|
8
|
-
minisweagent/agents/interactive_textual.py,sha256=yUDMkuvhhnZAP8LtiBWmt5J5WzfWBeR0zNlJbdbEGa0,18153
|
|
9
|
-
minisweagent/config/README.md,sha256=ZGH5KbFHpkwYOwoZgwP1dHOikuvU11dIG_TqyI73dik,355
|
|
10
|
-
minisweagent/config/__init__.py,sha256=0KzHaaIqWgRy2zbwIzhrg6BJPDzOvYi3jb4eBNY4sAU,823
|
|
11
|
-
minisweagent/config/default.yaml,sha256=z1q91vFq_EeEb2fAuRiIwU7ZabiWP_-29M4GgaBxpgA,5108
|
|
12
|
-
minisweagent/config/github_issue.yaml,sha256=Ws1IHO_lGA7JfLzXvDUzpSasf-4hQzXG7EjJqHORtho,4548
|
|
13
|
-
minisweagent/config/mini.tcss,sha256=fmAP9cYAp2n7Ps2Dw3e-ZOGEF2E8JcwTgK1LDcis-x4,1141
|
|
14
|
-
minisweagent/config/mini.yaml,sha256=eeOO6VkmiQcdU5dddESXUpGKFcSI2gBXsavNni4Xmig,5124
|
|
15
|
-
minisweagent/config/extra/__init__.py,sha256=e1MoAlDn_wc9HnXNoncf1P-B4DQ-iRf6n7Q_txjZGRI,52
|
|
16
|
-
minisweagent/config/extra/swebench.yaml,sha256=opFzxJPeMYY6oIpB6oUViXiax3ei7UTOlP0Lz1LbFss,7750
|
|
17
|
-
minisweagent/config/extra/swebench_roulette.yaml,sha256=ZYkh_ji0e7TuLcWXMrDhUQMPLiYH__EI0C4Skj3sK_Y,7871
|
|
18
|
-
minisweagent/config/extra/swebench_xml.yaml,sha256=dWXAqzXgw167hgiKqoqOryPHwAgDV2JbgoiDABdEznY,7827
|
|
19
|
-
minisweagent/environments/__init__.py,sha256=x80Ulx0UK21GAwg5jSTkOFeiZ7CQsGBP8cI_5BhazAo,1266
|
|
20
|
-
minisweagent/environments/docker.py,sha256=hsKOPGAP2kjgEwA_2HQz_nCrr25qmR4fB8u5Ob6UbzY,4370
|
|
21
|
-
minisweagent/environments/local.py,sha256=sOM-8Hc-bmGW6NEMebKz47vFR2Nb0xqvm1Daj6A_mPY,1278
|
|
22
|
-
minisweagent/environments/singularity.py,sha256=HSwRTWef7cMCgBiGAh5DIrxW8HkZ9C9ZGtwn0ktD_cw,3675
|
|
23
|
-
minisweagent/environments/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
minisweagent/environments/extra/bubblewrap.py,sha256=G12Dm63N30qByfLb1SKNsI4G4gLyKBfomnOIsPqRNZk,3662
|
|
25
|
-
minisweagent/environments/extra/swerex_docker.py,sha256=WPYbohT_vqTHkde9cxpbV6chLXCpLl0PDAcgMbZsV0M,1707
|
|
26
|
-
minisweagent/models/__init__.py,sha256=RGJgMPeF8W2Ix8_xwvHjjDCD9I6ygirz4v8Ps1KG6dI,4435
|
|
27
|
-
minisweagent/models/anthropic.py,sha256=4p-LxQ_RYQUX1rBsffAj3T1bBb2uMRhA4IyKfDcMpgo,1517
|
|
28
|
-
minisweagent/models/litellm_model.py,sha256=0HBnb53GdeqSAwNcJuu8hXrd3XqqIm4BHS1WkwK3ZGE,4394
|
|
29
|
-
minisweagent/models/litellm_response_api_model.py,sha256=Niq4HepQm_dlCkGdXsg9_7zNNRxJlnwWK-JJE0lP9Qg,3150
|
|
30
|
-
minisweagent/models/openrouter_model.py,sha256=YgML8_p9gHMjoBcxszmgyrIr5DLRX2vZiBwHC47N5mg,4796
|
|
31
|
-
minisweagent/models/portkey_model.py,sha256=_eK8maRm2kwombWYbYtH7MXj8rhd18ZE7tGer2YXvws,7307
|
|
32
|
-
minisweagent/models/portkey_response_api_model.py,sha256=JcODu40mYsqWnwWntTSjRUMTzifLUZZpFL5sF3FUk3s,2960
|
|
33
|
-
minisweagent/models/requesty_model.py,sha256=t4iQD7mH61UlldQmydcGYmoj-dAG8K_2347DBjl-4vo,3865
|
|
34
|
-
minisweagent/models/test_models.py,sha256=ItCA6ddntzkYA7dzSuUEaLMV-AE8TBuXBFP8CzpiO3U,1351
|
|
35
|
-
minisweagent/models/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
minisweagent/models/extra/roulette.py,sha256=idteU0pGvmmipNr0s-42GAbVkmKE20hY2LTFxbkAgoI,2048
|
|
37
|
-
minisweagent/models/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
minisweagent/models/utils/cache_control.py,sha256=zgi9e_72Y1pc0qNirW8_rMyi8RcLonGJNtfavqGX5_I,1917
|
|
39
|
-
minisweagent/models/utils/key_per_thread.py,sha256=4YZXATIw-Fozi7M-1i-wyjIBf-GtQM71kkOHxSPkwrE,748
|
|
40
|
-
minisweagent/models/utils/openai_utils.py,sha256=3OEOR65gFeVCTpcLJyMkzbFL_B-k8ftmcgvPK1EvqAw,1314
|
|
41
|
-
minisweagent/run/__init__.py,sha256=WIoYgHVl7iZF2YncrfV3IttupG6P5KogroKHKECka3A,38
|
|
42
|
-
minisweagent/run/github_issue.py,sha256=35mZoPLc4JV6XXJKRv55lnuKbXf5lDftd51N89-x9J0,3192
|
|
43
|
-
minisweagent/run/hello_world.py,sha256=erLnEwNmPFLxq3-8zyv66Vy1kIqMqQf97vISX7LrQXg,959
|
|
44
|
-
minisweagent/run/inspector.py,sha256=P86kOmzySWdK4tx0DHAOfSF1h-s1vHboSsaRD3_0OKQ,7109
|
|
45
|
-
minisweagent/run/mini.py,sha256=08uKeYc_tcAgbi1nbvqmVto-_RhG0NIp5pycvZsk_DM,4930
|
|
46
|
-
minisweagent/run/mini_extra.py,sha256=ecA1PnTWElpO60G9RktvVLtUOf3bZ_ESmnSttS6izhQ,1465
|
|
47
|
-
minisweagent/run/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
minisweagent/run/extra/config.py,sha256=KDMwg6eQCxbwI6P1phosCwaLQhJQXB4ti65M_HoxU-g,3892
|
|
49
|
-
minisweagent/run/extra/swebench.py,sha256=kqLazI4ZOX54ubFE0Md1THOtcdYdW1-7MCBu_z0lza8,11735
|
|
50
|
-
minisweagent/run/extra/swebench_single.py,sha256=JHIZkF60_AXfp2LC6CdEqABisEch2XB_udr9bjEO-yM,3693
|
|
51
|
-
minisweagent/run/extra/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
-
minisweagent/run/extra/utils/batch_progress.py,sha256=URgnm5MpUA6liESNFqDIbzELM869PbXro7jKzvdbiv0,6804
|
|
53
|
-
minisweagent/run/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
-
minisweagent/run/utils/save.py,sha256=bokvblZ1SaIvCXimkRQqgvERKmVM0jn8SF7UoHBeerQ,2546
|
|
55
|
-
minisweagent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
-
minisweagent/utils/log.py,sha256=ruDMNKMrVC9NPvCeHwO3QYz5jsVNUGQB2dRAEAPAWp8,996
|
|
57
|
-
mini_swe_agent-1.17.5.dist-info/METADATA,sha256=G47BDX4x97YUXUpe3ccX2-VfbsqFGUZO5xnL6dgyOCM,14836
|
|
58
|
-
mini_swe_agent-1.17.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
59
|
-
mini_swe_agent-1.17.5.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
|
|
60
|
-
mini_swe_agent-1.17.5.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
|
|
61
|
-
mini_swe_agent-1.17.5.dist-info/RECORD,,
|