mini-swe-agent 1.12.0__py3-none-any.whl → 1.13.2__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.12.0
3
+ Version: 1.13.2
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
@@ -65,6 +65,7 @@ Requires-Dist: mkdocstrings[python]>=0.18; extra == "dev"
65
65
  Requires-Dist: mike; extra == "dev"
66
66
  Requires-Dist: mkdocs-material; extra == "dev"
67
67
  Requires-Dist: mkdocs-glightbox; extra == "dev"
68
+ Requires-Dist: mkdocs-redirects; extra == "dev"
68
69
  Dynamic: license-file
69
70
 
70
71
  <div align="center">
@@ -1,5 +1,5 @@
1
- mini_swe_agent-1.12.0.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
2
- minisweagent/__init__.py,sha256=Pz8DDlFm1wJkbLSGvLgg4ejkc-R413muhKqEuhSN4r8,2014
1
+ mini_swe_agent-1.13.2.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
2
+ minisweagent/__init__.py,sha256=L9h6Hj8V819V2RbABXFOcXgPTwCdTacsJfSAG5X7tNE,2016
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
@@ -23,10 +23,11 @@ minisweagent/environments/singularity.py,sha256=HSwRTWef7cMCgBiGAh5DIrxW8HkZ9C9Z
23
23
  minisweagent/environments/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  minisweagent/environments/extra/bubblewrap.py,sha256=G12Dm63N30qByfLb1SKNsI4G4gLyKBfomnOIsPqRNZk,3662
25
25
  minisweagent/environments/extra/swerex_docker.py,sha256=WPYbohT_vqTHkde9cxpbV6chLXCpLl0PDAcgMbZsV0M,1707
26
- minisweagent/models/__init__.py,sha256=Boq3-r9eo4ptqnADMxAwtfevq9TmmE75mxSK5rS4ij0,4016
26
+ minisweagent/models/__init__.py,sha256=hMRTK5yfn2vymiXecZsSAGusxsTvrnQQVm8ih0y_PNM,4081
27
27
  minisweagent/models/anthropic.py,sha256=D8nHvvbgzPjla0He8p0O9kaXASPWg1Sai0pHsAj_Yn8,855
28
28
  minisweagent/models/litellm_model.py,sha256=RSvNzK6ksOOcTEY-kM36KJql9-iSH29kq0cKYXj2mxE,3034
29
29
  minisweagent/models/openrouter_model.py,sha256=ugI3322pyUQ8wBOuMysPoKlp0oRAmR38a6GcdjE6qHI,3748
30
+ minisweagent/models/portkey_model.py,sha256=IcFNz33AYeYgj0N9PGWV7QCn1fT8MfMWZx_OEzLaPsc,4548
30
31
  minisweagent/models/test_models.py,sha256=ItCA6ddntzkYA7dzSuUEaLMV-AE8TBuXBFP8CzpiO3U,1351
31
32
  minisweagent/models/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
33
  minisweagent/models/extra/roulette.py,sha256=SqLj_wz9Vkbxou7i9Ef4Uzmg_eheDouNySkkV7pm2Ys,2093
@@ -34,23 +35,23 @@ minisweagent/models/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
34
35
  minisweagent/models/utils/cache_control.py,sha256=mG9cE56HQaUwXfoqvXoH6LcbMV_G1vlEE1aBBpikXYg,1608
35
36
  minisweagent/models/utils/key_per_thread.py,sha256=Vlxt--rapNNYCgIHrMCu1WVAkuiVIhC_awbarkbnkZQ,644
36
37
  minisweagent/run/__init__.py,sha256=WIoYgHVl7iZF2YncrfV3IttupG6P5KogroKHKECka3A,38
37
- minisweagent/run/github_issue.py,sha256=LS4_7LktE0pitcFZJyBJADF74xZMBDgovUa4KwU9RN0,3073
38
+ minisweagent/run/github_issue.py,sha256=35mZoPLc4JV6XXJKRv55lnuKbXf5lDftd51N89-x9J0,3192
38
39
  minisweagent/run/hello_world.py,sha256=erLnEwNmPFLxq3-8zyv66Vy1kIqMqQf97vISX7LrQXg,959
39
40
  minisweagent/run/inspector.py,sha256=QnY3oYzm-yq3w9Jzs112Lco2Rg84vSocAWrQRVz_1lc,7127
40
- minisweagent/run/mini.py,sha256=EMU-hXAaTsgbdtTmZRxPYZ2bjYRTD6NQDr5Eta5Ybe8,4814
41
+ minisweagent/run/mini.py,sha256=m916OtSd8Wj2bUjfLGedNN-d2axTwZirM4AYmymXip8,4933
41
42
  minisweagent/run/mini_extra.py,sha256=ecA1PnTWElpO60G9RktvVLtUOf3bZ_ESmnSttS6izhQ,1465
42
43
  minisweagent/run/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
44
  minisweagent/run/extra/config.py,sha256=L5Xe7VGX1HMBlPac9aBufPnHA9duJpZYrbqXVtr8O6w,3845
44
- minisweagent/run/extra/swebench.py,sha256=sheaYoTxFmw5NdRGiRHPd_epVHRdhYtfIgI-RMvTSQc,11650
45
- minisweagent/run/extra/swebench_single.py,sha256=BwalUCgQTDlg4WOFmhPmW8K0wzbNoktTdGWzPoAiryE,3607
45
+ minisweagent/run/extra/swebench.py,sha256=sO3LnjLXdU6Zbo409YhxVdizU8LaQcJUdcD8Tj6saMw,11741
46
+ minisweagent/run/extra/swebench_single.py,sha256=KmoUkD6UQ1P0MY_73-OtYuQAsNPmOLlZIZSYKZGs5MQ,3699
46
47
  minisweagent/run/extra/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
48
  minisweagent/run/extra/utils/batch_progress.py,sha256=xhJ7FmsaTBGz-yh8pzYl4yMoUGjn7GA24eYrP-nHj60,6804
48
49
  minisweagent/run/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
50
  minisweagent/run/utils/save.py,sha256=2xd-UnUzI7Fr_AUZ5KEJ53Aa4kpuuGYxkLwyUcvqyMM,2503
50
51
  minisweagent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
52
  minisweagent/utils/log.py,sha256=ruDMNKMrVC9NPvCeHwO3QYz5jsVNUGQB2dRAEAPAWp8,996
52
- mini_swe_agent-1.12.0.dist-info/METADATA,sha256=_Om37mtSfMAUudlH9y90u1qXd4JfNlaCPheYJIl4dOM,14102
53
- mini_swe_agent-1.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
- mini_swe_agent-1.12.0.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
55
- mini_swe_agent-1.12.0.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
56
- mini_swe_agent-1.12.0.dist-info/RECORD,,
53
+ mini_swe_agent-1.13.2.dist-info/METADATA,sha256=9kkyymOo_pfywhGT-wYZ0jeU84Hn8DBmwWTOjkgbdGw,14150
54
+ mini_swe_agent-1.13.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
+ mini_swe_agent-1.13.2.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
56
+ mini_swe_agent-1.13.2.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
57
+ mini_swe_agent-1.13.2.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.12.0"
11
+ __version__ = "1.13.2"
12
12
 
13
13
  import os
14
14
  from pathlib import Path
@@ -29,7 +29,7 @@ global_config_file = Path(global_config_dir) / ".env"
29
29
  if not os.getenv("MSWEA_SILENT_STARTUP"):
30
30
  Console().print(
31
31
  f"👋 This is [bold green]mini-swe-agent[/bold green] version [bold green]{__version__}[/bold green].\n"
32
- f"Your config is stored in [bold green]'{global_config_file}'[/bold green]"
32
+ f"Loading global config from [bold green]'{global_config_file}'[/bold green]"
33
33
  )
34
34
  dotenv.load_dotenv(dotenv_path=global_config_file)
35
35
 
@@ -75,6 +75,7 @@ _MODEL_CLASS_MAPPING = {
75
75
  "anthropic": "minisweagent.models.anthropic.AnthropicModel",
76
76
  "litellm": "minisweagent.models.litellm_model.LitellmModel",
77
77
  "openrouter": "minisweagent.models.openrouter_model.OpenRouterModel",
78
+ "portkey": "minisweagent.models.portkey_model.PortkeyModel",
78
79
  "deterministic": "minisweagent.models.test_models.DeterministicModel",
79
80
  }
80
81
 
@@ -0,0 +1,118 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import asdict, dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import litellm
9
+ from tenacity import (
10
+ before_sleep_log,
11
+ retry,
12
+ retry_if_not_exception_type,
13
+ stop_after_attempt,
14
+ wait_exponential,
15
+ )
16
+
17
+ from minisweagent.models import GLOBAL_MODEL_STATS
18
+
19
+ logger = logging.getLogger("portkey_model")
20
+
21
+ try:
22
+ from portkey_ai import Portkey
23
+ except ImportError:
24
+ Portkey = None
25
+
26
+
27
+ @dataclass
28
+ class PortkeyModelConfig:
29
+ model_name: str
30
+ model_kwargs: dict[str, Any] = field(default_factory=dict)
31
+ litellm_model_registry: Path | str | None = os.getenv("LITELLM_MODEL_REGISTRY_PATH")
32
+ """We currently use litellm to calculate costs. Here you can register additional models to litellm's model registry.
33
+ Note that this might change if we get better support for Portkey and change how we calculate costs.
34
+ """
35
+ litellm_model_name_override: str = ""
36
+ """We currently use litellm to calculate costs. Here you can override the model name to use for litellm in case it
37
+ doesn't match the Portkey model name.
38
+ Note that this might change if we get better support for Portkey and change how we calculate costs.
39
+ """
40
+
41
+
42
+ class PortkeyModel:
43
+ def __init__(self, **kwargs):
44
+ if Portkey is None:
45
+ raise ImportError(
46
+ "The portkey-ai package is required to use PortkeyModel. Please install it with: pip install portkey-ai"
47
+ )
48
+ self.config = PortkeyModelConfig(**kwargs)
49
+ self.cost = 0.0
50
+ self.n_calls = 0
51
+ if self.config.litellm_model_registry and Path(self.config.litellm_model_registry).is_file():
52
+ litellm.utils.register_model(json.loads(Path(self.config.litellm_model_registry).read_text()))
53
+
54
+ # Get API key from environment or raise error
55
+ self._api_key = os.getenv("PORTKEY_API_KEY")
56
+ if not self._api_key:
57
+ raise ValueError(
58
+ "Portkey API key is required. Set it via the "
59
+ "PORTKEY_API_KEY environment variable. You can permanently set it with "
60
+ "`mini-extra config set PORTKEY_API_KEY YOUR_KEY`."
61
+ )
62
+
63
+ # Get virtual key from environment
64
+ virtual_key = os.getenv("PORTKEY_VIRTUAL_KEY")
65
+
66
+ # Initialize Portkey client
67
+ client_kwargs = {"api_key": self._api_key}
68
+ if virtual_key:
69
+ client_kwargs["virtual_key"] = virtual_key
70
+
71
+ self.client = Portkey(**client_kwargs)
72
+
73
+ @retry(
74
+ stop=stop_after_attempt(10),
75
+ wait=wait_exponential(multiplier=1, min=4, max=60),
76
+ before_sleep=before_sleep_log(logger, logging.WARNING),
77
+ retry=retry_if_not_exception_type((KeyboardInterrupt, TypeError, ValueError)),
78
+ )
79
+ def _query(self, messages: list[dict[str, str]], **kwargs):
80
+ # return self.client.with_options(metadata={"request_id": request_id}).chat.completions.create(
81
+ return self.client.chat.completions.create(
82
+ model=self.config.model_name,
83
+ messages=messages,
84
+ **(self.config.model_kwargs | kwargs),
85
+ )
86
+
87
+ def query(self, messages: list[dict[str, str]], **kwargs) -> dict:
88
+ response = self._query(messages, **kwargs)
89
+ response_for_cost_calc = response.model_copy()
90
+ if self.config.litellm_model_name_override:
91
+ if response_for_cost_calc.model:
92
+ response_for_cost_calc.model = self.config.litellm_model_name_override
93
+ try:
94
+ cost = litellm.cost_calculator.completion_cost(
95
+ response_for_cost_calc, model=self.config.litellm_model_name_override or None
96
+ )
97
+ except Exception as e:
98
+ logger.critical(
99
+ f"Error calculating cost for model {self.config.model_name} based on {response_for_cost_calc.model_dump()}: {e}. "
100
+ "Please check the 'Updating the model registry' section in the documentation at "
101
+ "https://klieret.short.gy/litellm-model-registry Still stuck? Please open a github issue for help!"
102
+ )
103
+ raise
104
+
105
+ self.n_calls += 1
106
+ self.cost += cost
107
+ GLOBAL_MODEL_STATS.add(cost)
108
+
109
+ return {
110
+ "content": response.choices[0].message.content or "",
111
+ "extra": {
112
+ "response": response.model_dump(),
113
+ "cost": cost,
114
+ },
115
+ }
116
+
117
+ def get_template_vars(self) -> dict[str, Any]:
118
+ return asdict(self.config) | {"n_model_calls": self.n_calls, "model_cost": self.cost}
@@ -221,8 +221,9 @@ def main(
221
221
  instances = [instance for instance in instances if instance["instance_id"] not in existing_instances]
222
222
  logger.info(f"Running on {len(instances)} instances...")
223
223
 
224
-
225
- config = yaml.safe_load(get_config_path(config_spec).read_text())
224
+ config_path = get_config_path(config_spec)
225
+ logger.info(f"Loading agent config from '{config_path}'")
226
+ config = yaml.safe_load(config_path.read_text())
226
227
  if environment_class is not None:
227
228
  config.setdefault("environment", {})["environment_class"] = environment_class
228
229
  if model is not None:
@@ -48,7 +48,9 @@ def main(
48
48
  instance_spec = sorted(instances.keys())[int(instance_spec)]
49
49
  instance: dict = instances[instance_spec] # type: ignore
50
50
 
51
- config = yaml.safe_load(get_config_path(config_path).read_text())
51
+ config_path = get_config_path(config_path)
52
+ logger.info(f"Loading agent config from '{config_path}'")
53
+ config = yaml.safe_load(config_path.read_text())
52
54
  if environment_class is not None:
53
55
  config.setdefault("environment", {})["environment_class"] = environment_class
54
56
  if model_class is not None:
@@ -50,7 +50,9 @@ def main(
50
50
  """Run mini-SWE-agent on a GitHub issue"""
51
51
  configure_if_first_time()
52
52
 
53
- _config = yaml.safe_load(get_config_path(config).read_text())
53
+ config_path = get_config_path(config)
54
+ console.print(f"Loading agent config from [bold green]'{config_path}'[/bold green]")
55
+ _config = yaml.safe_load(config_path.read_text())
54
56
  _agent_config = _config.setdefault("agent", {})
55
57
  if yolo:
56
58
  _agent_config["mode"] = "yolo"
minisweagent/run/mini.py CHANGED
@@ -58,7 +58,9 @@ def main(
58
58
  ) -> Any:
59
59
  # fmt: on
60
60
  configure_if_first_time()
61
- config = yaml.safe_load(get_config_path(config_spec).read_text())
61
+ config_path = get_config_path(config_spec)
62
+ console.print(f"Loading agent config from [bold green]'{config_path}'[/bold green]")
63
+ config = yaml.safe_load(config_path.read_text())
62
64
 
63
65
  if not task:
64
66
  console.print("[bold yellow]What do you want to do?")