mini-swe-agent 1.13.3__py3-none-any.whl → 1.14.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.
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/METADATA +2 -2
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/RECORD +18 -18
- minisweagent/__init__.py +1 -1
- minisweagent/agents/interactive_textual.py +2 -1
- minisweagent/config/extra/swebench.yaml +1 -1
- minisweagent/config/extra/swebench_roulette.yaml +1 -1
- minisweagent/models/__init__.py +7 -5
- minisweagent/models/anthropic.py +22 -6
- minisweagent/models/litellm_model.py +8 -3
- minisweagent/models/openrouter_model.py +6 -1
- minisweagent/models/portkey_model.py +6 -1
- minisweagent/models/utils/cache_control.py +39 -9
- minisweagent/models/utils/key_per_thread.py +2 -0
- minisweagent/run/extra/config.py +3 -2
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/WHEEL +0 -0
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/entry_points.txt +0 -0
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/licenses/LICENSE.md +0 -0
- {mini_swe_agent-1.13.3.dist-info → mini_swe_agent-1.14.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mini-swe-agent
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.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
|
|
@@ -94,7 +94,7 @@ Here's some details:
|
|
|
94
94
|
|
|
95
95
|
- **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),
|
|
96
96
|
[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!
|
|
97
|
-
- **Powerful:** Resolves
|
|
97
|
+
- **Powerful:** Resolves >70% of GitHub issues in the [SWE-bench verified benchmark](https://www.swebench.com/) ([leaderboard](https://swe-bench.com/)).
|
|
98
98
|
- **Convenient:** Comes with UIs that turn this into your daily dev swiss army knife!
|
|
99
99
|
- **Deployable:** In addition to local envs, you can use **docker**, **podman**, **singularity**, **apptainer**, and more
|
|
100
100
|
- **Tested:** [](https://codecov.io/gh/SWE-agent/mini-swe-agent)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
mini_swe_agent-1.
|
|
2
|
-
minisweagent/__init__.py,sha256=
|
|
1
|
+
mini_swe_agent-1.14.0.dist-info/licenses/LICENSE.md,sha256=D3luWPkdHAe7LBsdD4vzqDAXw6Xewb3G-uczss0uh1s,1094
|
|
2
|
+
minisweagent/__init__.py,sha256=x76NxcXdWcajgoVsrPVO4EaR79r1Bnk3bF6wifAmcYo,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
|
|
6
6
|
minisweagent/agents/default.py,sha256=9YG6s8Ea6RLq2cAv7hjERCR8B2vtuwh94EcmWezDyZw,5693
|
|
7
7
|
minisweagent/agents/interactive.py,sha256=NBeNamRuqww9ZRhOg1q8xPO9ziUw2gpAVV6hCPbpBxU,7470
|
|
8
|
-
minisweagent/agents/interactive_textual.py,sha256=
|
|
8
|
+
minisweagent/agents/interactive_textual.py,sha256=yUDMkuvhhnZAP8LtiBWmt5J5WzfWBeR0zNlJbdbEGa0,18153
|
|
9
9
|
minisweagent/config/README.md,sha256=ABd9anA4aRWtx7Oh37z36Wv6ARvcxD2w9lPUE24R2mY,435
|
|
10
10
|
minisweagent/config/__init__.py,sha256=0KzHaaIqWgRy2zbwIzhrg6BJPDzOvYi3jb4eBNY4sAU,823
|
|
11
11
|
minisweagent/config/default.yaml,sha256=iVNFs-FHrjc81RAiaTjGk5435G6V7OPjbXECu6RxJPU,5129
|
|
@@ -14,8 +14,8 @@ minisweagent/config/mini.tcss,sha256=fmAP9cYAp2n7Ps2Dw3e-ZOGEF2E8JcwTgK1LDcis-x4
|
|
|
14
14
|
minisweagent/config/mini.yaml,sha256=-3c4eKeCysFAfKJX3whUuBI6wbQgt8vrlcTFp_pcdyY,5145
|
|
15
15
|
minisweagent/config/mini_no_temp.yaml,sha256=g1Y5goNTYZlDcSuBgKWJUdMkoK09w_5vheASZg1yYYI,5190
|
|
16
16
|
minisweagent/config/extra/__init__.py,sha256=e1MoAlDn_wc9HnXNoncf1P-B4DQ-iRf6n7Q_txjZGRI,52
|
|
17
|
-
minisweagent/config/extra/swebench.yaml,sha256=
|
|
18
|
-
minisweagent/config/extra/swebench_roulette.yaml,sha256=
|
|
17
|
+
minisweagent/config/extra/swebench.yaml,sha256=opFzxJPeMYY6oIpB6oUViXiax3ei7UTOlP0Lz1LbFss,7750
|
|
18
|
+
minisweagent/config/extra/swebench_roulette.yaml,sha256=8O7PvO8tPGJN-mYuBGhWUlAzsjMPnbf3_i6Sn5v7RQ4,7813
|
|
19
19
|
minisweagent/environments/__init__.py,sha256=x80Ulx0UK21GAwg5jSTkOFeiZ7CQsGBP8cI_5BhazAo,1266
|
|
20
20
|
minisweagent/environments/docker.py,sha256=Gq-0asaNPpEDtKtzddScPkRVgBQCdR-qhifRbzbFHSA,4323
|
|
21
21
|
minisweagent/environments/local.py,sha256=sOM-8Hc-bmGW6NEMebKz47vFR2Nb0xqvm1Daj6A_mPY,1278
|
|
@@ -23,17 +23,17 @@ 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=
|
|
27
|
-
minisweagent/models/anthropic.py,sha256=
|
|
28
|
-
minisweagent/models/litellm_model.py,sha256=
|
|
29
|
-
minisweagent/models/openrouter_model.py,sha256=
|
|
30
|
-
minisweagent/models/portkey_model.py,sha256=
|
|
26
|
+
minisweagent/models/__init__.py,sha256=Ol95N3oEDlojz7IBw10A7xf5rKfxtjESyJuEWOwZyzo,4171
|
|
27
|
+
minisweagent/models/anthropic.py,sha256=4p-LxQ_RYQUX1rBsffAj3T1bBb2uMRhA4IyKfDcMpgo,1517
|
|
28
|
+
minisweagent/models/litellm_model.py,sha256=qAYsFBzZGPRGGQfbNCJXKH9eRpzmJKZ9ORTJHsOdhL0,3419
|
|
29
|
+
minisweagent/models/openrouter_model.py,sha256=7SwS4Raw-i_gAM9AGJ8EWoTrnCP8MfHqWf7UZ0sb4ds,4095
|
|
30
|
+
minisweagent/models/portkey_model.py,sha256=lARromtKwiXJtxxXScOkp9PCl3oS76QMQh_6DeehoiI,4895
|
|
31
31
|
minisweagent/models/test_models.py,sha256=ItCA6ddntzkYA7dzSuUEaLMV-AE8TBuXBFP8CzpiO3U,1351
|
|
32
32
|
minisweagent/models/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
minisweagent/models/extra/roulette.py,sha256=SqLj_wz9Vkbxou7i9Ef4Uzmg_eheDouNySkkV7pm2Ys,2093
|
|
34
34
|
minisweagent/models/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
minisweagent/models/utils/cache_control.py,sha256=
|
|
36
|
-
minisweagent/models/utils/key_per_thread.py,sha256=
|
|
35
|
+
minisweagent/models/utils/cache_control.py,sha256=rAjW_FPWxG7bnuNe94CARAq9LKaTgFtSSp9zMQvssRs,2535
|
|
36
|
+
minisweagent/models/utils/key_per_thread.py,sha256=4YZXATIw-Fozi7M-1i-wyjIBf-GtQM71kkOHxSPkwrE,748
|
|
37
37
|
minisweagent/run/__init__.py,sha256=WIoYgHVl7iZF2YncrfV3IttupG6P5KogroKHKECka3A,38
|
|
38
38
|
minisweagent/run/github_issue.py,sha256=35mZoPLc4JV6XXJKRv55lnuKbXf5lDftd51N89-x9J0,3192
|
|
39
39
|
minisweagent/run/hello_world.py,sha256=erLnEwNmPFLxq3-8zyv66Vy1kIqMqQf97vISX7LrQXg,959
|
|
@@ -41,7 +41,7 @@ minisweagent/run/inspector.py,sha256=QnY3oYzm-yq3w9Jzs112Lco2Rg84vSocAWrQRVz_1lc
|
|
|
41
41
|
minisweagent/run/mini.py,sha256=m916OtSd8Wj2bUjfLGedNN-d2axTwZirM4AYmymXip8,4933
|
|
42
42
|
minisweagent/run/mini_extra.py,sha256=ecA1PnTWElpO60G9RktvVLtUOf3bZ_ESmnSttS6izhQ,1465
|
|
43
43
|
minisweagent/run/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
-
minisweagent/run/extra/config.py,sha256=
|
|
44
|
+
minisweagent/run/extra/config.py,sha256=5s0ZjjE4QTphOqQzCJqHD2APq5jCdod5jFjGo3sXDms,3868
|
|
45
45
|
minisweagent/run/extra/swebench.py,sha256=sO3LnjLXdU6Zbo409YhxVdizU8LaQcJUdcD8Tj6saMw,11741
|
|
46
46
|
minisweagent/run/extra/swebench_single.py,sha256=KmoUkD6UQ1P0MY_73-OtYuQAsNPmOLlZIZSYKZGs5MQ,3699
|
|
47
47
|
minisweagent/run/extra/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -50,8 +50,8 @@ minisweagent/run/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
50
50
|
minisweagent/run/utils/save.py,sha256=2xd-UnUzI7Fr_AUZ5KEJ53Aa4kpuuGYxkLwyUcvqyMM,2503
|
|
51
51
|
minisweagent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
minisweagent/utils/log.py,sha256=ruDMNKMrVC9NPvCeHwO3QYz5jsVNUGQB2dRAEAPAWp8,996
|
|
53
|
-
mini_swe_agent-1.
|
|
54
|
-
mini_swe_agent-1.
|
|
55
|
-
mini_swe_agent-1.
|
|
56
|
-
mini_swe_agent-1.
|
|
57
|
-
mini_swe_agent-1.
|
|
53
|
+
mini_swe_agent-1.14.0.dist-info/METADATA,sha256=_quEJiibMIZ98LVaarGvBumSvBXWihTaeelQMGF7Wf8,14151
|
|
54
|
+
mini_swe_agent-1.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
55
|
+
mini_swe_agent-1.14.0.dist-info/entry_points.txt,sha256=d1_yRbTaGjs1UXHa6JQK0sKDGBIVGm8oeW0k2kfbJgQ,182
|
|
56
|
+
mini_swe_agent-1.14.0.dist-info/top_level.txt,sha256=zKF4t8bFpV87fdVABZt2Da-vnb4Vkh_CxkwQx5YT4Ew,13
|
|
57
|
+
mini_swe_agent-1.14.0.dist-info/RECORD,,
|
minisweagent/__init__.py
CHANGED
|
@@ -402,7 +402,8 @@ class TextualAgent(App):
|
|
|
402
402
|
yield from super().get_system_commands(screen)
|
|
403
403
|
for binding in self.BINDINGS:
|
|
404
404
|
description = f"{binding.description} (shortcut {' OR '.join(binding.key.split(','))})" # type: ignore[attr-defined]
|
|
405
|
-
|
|
405
|
+
action_method = getattr(self, f"action_{binding.action}") # type: ignore[attr-defined]
|
|
406
|
+
yield SystemCommand(description, binding.tooltip, action_method) # type: ignore[attr-defined]
|
|
406
407
|
|
|
407
408
|
# --- Textual bindings ---
|
|
408
409
|
|
|
@@ -227,7 +227,7 @@ model:
|
|
|
227
227
|
model_name: "roulette"
|
|
228
228
|
model_class: "minisweagent.models.extra.roulette.RouletteModel"
|
|
229
229
|
model_kwargs:
|
|
230
|
-
- model_name: "claude-sonnet-4-
|
|
230
|
+
- model_name: "anthropic/claude-sonnet-4-5-20250929"
|
|
231
231
|
model_kwargs:
|
|
232
232
|
temperature: 0.
|
|
233
233
|
- model_name: "gpt-5"
|
minisweagent/models/__init__.py
CHANGED
|
@@ -55,6 +55,13 @@ def get_model(input_model_name: str | None = None, config: dict | None = None) -
|
|
|
55
55
|
if (from_env := os.getenv("MSWEA_MODEL_API_KEY")) and not str(type(model_class)).endswith("DeterministicModel"):
|
|
56
56
|
config.setdefault("model_kwargs", {})["api_key"] = from_env
|
|
57
57
|
|
|
58
|
+
if (
|
|
59
|
+
any(s in resolved_model_name.lower() for s in ["anthropic", "sonnet", "opus", "claude"])
|
|
60
|
+
and "set_cache_control" not in config
|
|
61
|
+
):
|
|
62
|
+
# Select cache control for Anthropic models by default
|
|
63
|
+
config["set_cache_control"] = "default_end"
|
|
64
|
+
|
|
58
65
|
return model_class(**config)
|
|
59
66
|
|
|
60
67
|
|
|
@@ -98,11 +105,6 @@ def get_model_class(model_name: str, model_class: str = "") -> type:
|
|
|
98
105
|
msg = f"Unknown model class: {model_class} (resolved to {full_path}, available: {_MODEL_CLASS_MAPPING})"
|
|
99
106
|
raise ValueError(msg)
|
|
100
107
|
|
|
101
|
-
if any(s in model_name.lower() for s in ["anthropic", "sonnet", "opus", "claude"]):
|
|
102
|
-
from minisweagent.models.anthropic import AnthropicModel
|
|
103
|
-
|
|
104
|
-
return AnthropicModel
|
|
105
|
-
|
|
106
108
|
# Default to LitellmModel
|
|
107
109
|
from minisweagent.models.litellm_model import LitellmModel
|
|
108
110
|
|
minisweagent/models/anthropic.py
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Literal
|
|
2
4
|
|
|
3
|
-
from minisweagent.models.litellm_model import LitellmModel
|
|
5
|
+
from minisweagent.models.litellm_model import LitellmModel, LitellmModelConfig
|
|
4
6
|
from minisweagent.models.utils.cache_control import set_cache_control
|
|
5
7
|
from minisweagent.models.utils.key_per_thread import get_key_per_thread
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
class AnthropicModelConfig(LitellmModelConfig):
|
|
11
|
+
set_cache_control: Literal["default_end"] | None = "default_end"
|
|
12
|
+
"""Set explicit cache control markers, for example for Anthropic models"""
|
|
13
|
+
|
|
14
|
+
|
|
8
15
|
class AnthropicModel(LitellmModel):
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if running with multiple agents in parallel threads.
|
|
16
|
+
"""This class is now only a thin wrapper around the LitellmModel class.
|
|
17
|
+
It is largely kept for backwards compatibility.
|
|
18
|
+
It will not be selected by `get_model` and `get_model_class` unless explicitly specified.
|
|
13
19
|
"""
|
|
14
20
|
|
|
21
|
+
def __init__(self, *, config_class: type = AnthropicModelConfig, **kwargs):
|
|
22
|
+
super().__init__(config_class=config_class, **kwargs)
|
|
23
|
+
|
|
15
24
|
def query(self, messages: list[dict], **kwargs) -> dict:
|
|
16
25
|
api_key = None
|
|
26
|
+
# Legacy only
|
|
17
27
|
if rotating_keys := os.getenv("ANTHROPIC_API_KEYS"):
|
|
28
|
+
warnings.warn(
|
|
29
|
+
"ANTHROPIC_API_KEYS is deprecated and will be removed in the future. "
|
|
30
|
+
"Simply use the ANTHROPIC_API_KEY environment variable instead. "
|
|
31
|
+
"Key rotation is no longer required."
|
|
32
|
+
)
|
|
18
33
|
api_key = get_key_per_thread(rotating_keys.split("::"))
|
|
19
|
-
|
|
34
|
+
messages = set_cache_control(messages, mode="default_end")
|
|
35
|
+
return super().query(messages, api_key=api_key, **kwargs)
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import asdict, dataclass, field
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Literal
|
|
7
7
|
|
|
8
8
|
import litellm
|
|
9
9
|
from tenacity import (
|
|
@@ -15,6 +15,7 @@ from tenacity import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
from minisweagent.models import GLOBAL_MODEL_STATS
|
|
18
|
+
from minisweagent.models.utils.cache_control import set_cache_control
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger("litellm_model")
|
|
20
21
|
|
|
@@ -24,11 +25,13 @@ class LitellmModelConfig:
|
|
|
24
25
|
model_name: str
|
|
25
26
|
model_kwargs: dict[str, Any] = field(default_factory=dict)
|
|
26
27
|
litellm_model_registry: Path | str | None = os.getenv("LITELLM_MODEL_REGISTRY_PATH")
|
|
28
|
+
set_cache_control: Literal["default_end"] | None = None
|
|
29
|
+
"""Set explicit cache control markers, for example for Anthropic models"""
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
class LitellmModel:
|
|
30
|
-
def __init__(self, **kwargs):
|
|
31
|
-
self.config =
|
|
33
|
+
def __init__(self, *, config_class: type = LitellmModelConfig, **kwargs):
|
|
34
|
+
self.config = config_class(**kwargs)
|
|
32
35
|
self.cost = 0.0
|
|
33
36
|
self.n_calls = 0
|
|
34
37
|
if self.config.litellm_model_registry and Path(self.config.litellm_model_registry).is_file():
|
|
@@ -60,6 +63,8 @@ class LitellmModel:
|
|
|
60
63
|
raise e
|
|
61
64
|
|
|
62
65
|
def query(self, messages: list[dict[str, str]], **kwargs) -> dict:
|
|
66
|
+
if self.config.set_cache_control:
|
|
67
|
+
messages = set_cache_control(messages, mode=self.config.set_cache_control)
|
|
63
68
|
response = self._query(messages, **kwargs)
|
|
64
69
|
try:
|
|
65
70
|
cost = litellm.cost_calculator.completion_cost(response)
|
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import asdict, dataclass, field
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, Literal
|
|
6
6
|
|
|
7
7
|
import requests
|
|
8
8
|
from tenacity import (
|
|
@@ -14,6 +14,7 @@ from tenacity import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
from minisweagent.models import GLOBAL_MODEL_STATS
|
|
17
|
+
from minisweagent.models.utils.cache_control import set_cache_control
|
|
17
18
|
|
|
18
19
|
logger = logging.getLogger("openrouter_model")
|
|
19
20
|
|
|
@@ -22,6 +23,8 @@ logger = logging.getLogger("openrouter_model")
|
|
|
22
23
|
class OpenRouterModelConfig:
|
|
23
24
|
model_name: str
|
|
24
25
|
model_kwargs: dict[str, Any] = field(default_factory=dict)
|
|
26
|
+
set_cache_control: Literal["default_end"] | None = None
|
|
27
|
+
"""Set explicit cache control markers, for example for Anthropic models"""
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class OpenRouterAPIError(Exception):
|
|
@@ -90,6 +93,8 @@ class OpenRouterModel:
|
|
|
90
93
|
raise OpenRouterAPIError(f"Request failed: {e}") from e
|
|
91
94
|
|
|
92
95
|
def query(self, messages: list[dict[str, str]], **kwargs) -> dict:
|
|
96
|
+
if self.config.set_cache_control:
|
|
97
|
+
messages = set_cache_control(messages, mode=self.config.set_cache_control)
|
|
93
98
|
response = self._query(messages, **kwargs)
|
|
94
99
|
|
|
95
100
|
# Extract cost from usage information
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import asdict, dataclass, field
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Literal
|
|
7
7
|
|
|
8
8
|
import litellm
|
|
9
9
|
from tenacity import (
|
|
@@ -15,6 +15,7 @@ from tenacity import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
from minisweagent.models import GLOBAL_MODEL_STATS
|
|
18
|
+
from minisweagent.models.utils.cache_control import set_cache_control
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger("portkey_model")
|
|
20
21
|
|
|
@@ -37,6 +38,8 @@ class PortkeyModelConfig:
|
|
|
37
38
|
doesn't match the Portkey model name.
|
|
38
39
|
Note that this might change if we get better support for Portkey and change how we calculate costs.
|
|
39
40
|
"""
|
|
41
|
+
set_cache_control: Literal["default_end"] | None = None
|
|
42
|
+
"""Set explicit cache control markers, for example for Anthropic models"""
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
class PortkeyModel:
|
|
@@ -85,6 +88,8 @@ class PortkeyModel:
|
|
|
85
88
|
)
|
|
86
89
|
|
|
87
90
|
def query(self, messages: list[dict[str, str]], **kwargs) -> dict:
|
|
91
|
+
if self.config.set_cache_control:
|
|
92
|
+
messages = set_cache_control(messages, mode=self.config.set_cache_control)
|
|
88
93
|
response = self._query(messages, **kwargs)
|
|
89
94
|
response_for_cost_calc = response.model_copy()
|
|
90
95
|
if self.config.litellm_model_name_override:
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
def _get_content_text(entry: dict) -> str:
|
|
2
7
|
if isinstance(entry["content"], str):
|
|
3
8
|
return entry["content"]
|
|
@@ -29,14 +34,39 @@ def _set_cache_control(entry: dict) -> None:
|
|
|
29
34
|
entry["cache_control"] = {"type": "ephemeral"}
|
|
30
35
|
|
|
31
36
|
|
|
32
|
-
def set_cache_control(
|
|
37
|
+
def set_cache_control(
|
|
38
|
+
messages: list[dict], *, mode: Literal["default_end"] | None = "default_end", last_n_messages_offset: int = 0
|
|
39
|
+
) -> list[dict]:
|
|
33
40
|
"""This messages processor adds manual cache control marks to the messages."""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
if mode != "default_end":
|
|
42
|
+
raise ValueError(f"Invalid mode: {mode}")
|
|
43
|
+
if last_n_messages_offset:
|
|
44
|
+
warnings.warn("last_n_messages_offset is deprecated and will be removed in the future")
|
|
45
|
+
messages = copy.deepcopy(messages)
|
|
46
|
+
|
|
47
|
+
# Find all user messages
|
|
48
|
+
user_message_indices = []
|
|
49
|
+
for i, msg in enumerate(messages):
|
|
50
|
+
if msg["role"] == "user":
|
|
51
|
+
user_message_indices.append(i)
|
|
52
|
+
|
|
53
|
+
# Clear cache control from all messages first
|
|
54
|
+
for entry in messages:
|
|
37
55
|
_clear_cache_control(entry)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
56
|
+
|
|
57
|
+
# Add cache control to user messages, respecting offset
|
|
58
|
+
if user_message_indices:
|
|
59
|
+
# If offset is specified, exclude the last N messages from getting cache control
|
|
60
|
+
messages_to_cache = user_message_indices
|
|
61
|
+
if last_n_messages_offset > 0:
|
|
62
|
+
messages_to_cache = (
|
|
63
|
+
user_message_indices[:-last_n_messages_offset]
|
|
64
|
+
if last_n_messages_offset < len(user_message_indices)
|
|
65
|
+
else []
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Add cache control to the selected user messages
|
|
69
|
+
for idx in messages_to_cache:
|
|
70
|
+
_set_cache_control(messages[idx])
|
|
71
|
+
|
|
72
|
+
return messages
|
|
@@ -3,6 +3,7 @@ agents to not mess up prompt caching.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import threading
|
|
6
|
+
import warnings
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
_THREADS_THAT_USED_API_KEYS: list[Any] = []
|
|
@@ -10,6 +11,7 @@ _THREADS_THAT_USED_API_KEYS: list[Any] = []
|
|
|
10
11
|
|
|
11
12
|
def get_key_per_thread(api_keys: list[Any]) -> Any:
|
|
12
13
|
"""Choose key based on thread name. Returns None if no keys are available."""
|
|
14
|
+
warnings.warn("get_key_per_thread is deprecated and will be removed in the future")
|
|
13
15
|
thread_name = threading.current_thread().name
|
|
14
16
|
if thread_name not in _THREADS_THAT_USED_API_KEYS:
|
|
15
17
|
_THREADS_THAT_USED_API_KEYS.append(thread_name)
|
minisweagent/run/extra/config.py
CHANGED
|
@@ -33,7 +33,7 @@ This setup will ask you for your model and an API key.
|
|
|
33
33
|
|
|
34
34
|
Here's a few popular models and the required API keys:
|
|
35
35
|
|
|
36
|
-
[bold green]anthropic/claude-sonnet-4-
|
|
36
|
+
[bold green]anthropic/claude-sonnet-4-5-20250929[/bold green] ([bold green]ANTHROPIC_API_KEY[/bold green])
|
|
37
37
|
[bold green]openai/gpt-5[/bold green] or [bold green]openai/gpt-5-mini[/bold green] ([bold green]OPENAI_API_KEY[/bold green])
|
|
38
38
|
[bold green]gemini/gemini-2.5-pro[/bold green] ([bold green]GEMINI_API_KEY[/bold green])
|
|
39
39
|
|
|
@@ -58,7 +58,8 @@ def setup():
|
|
|
58
58
|
"""Setup the global config file."""
|
|
59
59
|
console.print(_SETUP_HELP.format(global_config_file=global_config_file))
|
|
60
60
|
default_model = prompt(
|
|
61
|
-
"Enter your default model (e.g., claude-sonnet-4-
|
|
61
|
+
"Enter your default model (e.g., anthropic/claude-sonnet-4-5-20250929): ",
|
|
62
|
+
default=os.getenv("MSWEA_MODEL_NAME", ""),
|
|
62
63
|
).strip()
|
|
63
64
|
if default_model:
|
|
64
65
|
set_key(global_config_file, "MSWEA_MODEL_NAME", default_model)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|