minima-cli 0.4.9__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.
- minima/__init__.py +5 -0
- minima/api/__init__.py +1 -0
- minima/api/auth.py +39 -0
- minima/api/errors.py +40 -0
- minima/api/routers/__init__.py +1 -0
- minima/api/routers/calibration.py +50 -0
- minima/api/routers/feedback.py +279 -0
- minima/api/routers/health.py +50 -0
- minima/api/routers/models.py +42 -0
- minima/api/routers/recommend.py +66 -0
- minima/api/routers/savings.py +55 -0
- minima/api/routers/strategies.py +33 -0
- minima/catalog/__init__.py +1 -0
- minima/catalog/data/capability_priors.json +210 -0
- minima/catalog/data/model_aliases.json +12 -0
- minima/catalog/merge.py +69 -0
- minima/catalog/refresh.py +54 -0
- minima/catalog/sources/__init__.py +1 -0
- minima/catalog/sources/litellm.py +19 -0
- minima/catalog/sources/openrouter.py +25 -0
- minima/catalog/store.py +86 -0
- minima/config.py +288 -0
- minima/deps.py +35 -0
- minima/llm/__init__.py +1 -0
- minima/llm/anthropic.py +106 -0
- minima/llm/base.py +196 -0
- minima/llm/gemini.py +124 -0
- minima/llm/registry.py +54 -0
- minima/logging.py +28 -0
- minima/main.py +109 -0
- minima/memory/__init__.py +1 -0
- minima/memory/adapter.py +572 -0
- minima/memory/keys.py +83 -0
- minima/memory/records.py +190 -0
- minima/memory/threadpool.py +41 -0
- minima/metrics/__init__.py +1 -0
- minima/metrics/calibration.py +415 -0
- minima/metrics/report.py +116 -0
- minima/metrics/savings.py +98 -0
- minima/recommender/__init__.py +1 -0
- minima/recommender/_pg_pool.py +38 -0
- minima/recommender/_redis_client.py +32 -0
- minima/recommender/aggregate.py +157 -0
- minima/recommender/classify.py +165 -0
- minima/recommender/decisionlog.py +505 -0
- minima/recommender/durablerefs.py +312 -0
- minima/recommender/engine.py +997 -0
- minima/recommender/escalation.py +83 -0
- minima/recommender/propensity.py +189 -0
- minima/recommender/recstore.py +368 -0
- minima/recommender/score.py +318 -0
- minima/recommender/types.py +166 -0
- minima/schemas/__init__.py +1 -0
- minima/schemas/common.py +73 -0
- minima/schemas/feedback.py +34 -0
- minima/schemas/models_catalog.py +36 -0
- minima/schemas/recommend.py +104 -0
- minima/schemas/savings.py +39 -0
- minima/schemas/strategies.py +57 -0
- minima/schemas/workflow.py +43 -0
- minima/seeding/__init__.py +1 -0
- minima/seeding/items.py +42 -0
- minima/seeding/llmrouterbench.py +232 -0
- minima/seeding/routerbench.py +141 -0
- minima/seeding/run_seed.py +56 -0
- minima/seeding/synthetic.py +70 -0
- minima/tenancy/__init__.py +8 -0
- minima/tenancy/context.py +37 -0
- minima/tenancy/passthrough.py +110 -0
- minima/version.py +3 -0
- minima_cli-0.4.9.dist-info/METADATA +275 -0
- minima_cli-0.4.9.dist-info/RECORD +161 -0
- minima_cli-0.4.9.dist-info/WHEEL +4 -0
- minima_cli-0.4.9.dist-info/entry_points.txt +5 -0
- minima_cli-0.4.9.dist-info/licenses/LICENSE +295 -0
- minima_client/__init__.py +19 -0
- minima_client/autocapture.py +101 -0
- minima_client/client.py +301 -0
- minima_client/errors.py +23 -0
- minima_harness/LICENSE_PI +32 -0
- minima_harness/__init__.py +16 -0
- minima_harness/agent/__init__.py +72 -0
- minima_harness/agent/agent.py +276 -0
- minima_harness/agent/events.py +124 -0
- minima_harness/agent/loop.py +311 -0
- minima_harness/agent/state.py +79 -0
- minima_harness/agent/tools.py +97 -0
- minima_harness/ai/__init__.py +66 -0
- minima_harness/ai/compat.py +71 -0
- minima_harness/ai/errors.py +96 -0
- minima_harness/ai/events.py +117 -0
- minima_harness/ai/openrouter_catalog.py +153 -0
- minima_harness/ai/provider_catalog.py +299 -0
- minima_harness/ai/provider_quirks.py +37 -0
- minima_harness/ai/providers/__init__.py +75 -0
- minima_harness/ai/providers/_common.py +48 -0
- minima_harness/ai/providers/anthropic.py +290 -0
- minima_harness/ai/providers/base.py +65 -0
- minima_harness/ai/providers/faux.py +173 -0
- minima_harness/ai/providers/google.py +221 -0
- minima_harness/ai/providers/openai_compat.py +278 -0
- minima_harness/ai/registry.py +184 -0
- minima_harness/ai/stream.py +82 -0
- minima_harness/ai/tools.py +51 -0
- minima_harness/ai/types.py +204 -0
- minima_harness/ai/usage.py +41 -0
- minima_harness/minima/__init__.py +40 -0
- minima_harness/minima/cache.py +102 -0
- minima_harness/minima/config.py +85 -0
- minima_harness/minima/goals.py +226 -0
- minima_harness/minima/judge.py +144 -0
- minima_harness/minima/mapping.py +147 -0
- minima_harness/minima/meter.py +143 -0
- minima_harness/minima/router.py +220 -0
- minima_harness/minima/runtime.py +544 -0
- minima_harness/minima/signals.py +195 -0
- minima_harness/session/__init__.py +14 -0
- minima_harness/session/format.py +35 -0
- minima_harness/session/store.py +236 -0
- minima_harness/tasks/__init__.py +17 -0
- minima_harness/tasks/task_set.py +78 -0
- minima_harness/tools/__init__.py +7 -0
- minima_harness/tools/_io.py +34 -0
- minima_harness/tools/bash.py +70 -0
- minima_harness/tools/builtin.py +23 -0
- minima_harness/tools/edit.py +50 -0
- minima_harness/tools/find.py +38 -0
- minima_harness/tools/grep.py +73 -0
- minima_harness/tools/ls.py +35 -0
- minima_harness/tools/read.py +38 -0
- minima_harness/tools/tasks.py +75 -0
- minima_harness/tools/write.py +36 -0
- minima_harness/tui/__init__.py +3 -0
- minima_harness/tui/analytics.py +111 -0
- minima_harness/tui/app.py +1927 -0
- minima_harness/tui/bridge.py +103 -0
- minima_harness/tui/cli.py +227 -0
- minima_harness/tui/clipboard.py +60 -0
- minima_harness/tui/commands.py +49 -0
- minima_harness/tui/compaction.py +17 -0
- minima_harness/tui/config_cli.py +141 -0
- minima_harness/tui/config_store.py +237 -0
- minima_harness/tui/context.py +93 -0
- minima_harness/tui/customize.py +95 -0
- minima_harness/tui/diff.py +53 -0
- minima_harness/tui/editor.py +43 -0
- minima_harness/tui/extensions.py +84 -0
- minima_harness/tui/extra_models.py +52 -0
- minima_harness/tui/history.py +71 -0
- minima_harness/tui/mubit.py +295 -0
- minima_harness/tui/overlays.py +593 -0
- minima_harness/tui/packages.py +59 -0
- minima_harness/tui/run_modes.py +66 -0
- minima_harness/tui/theme.py +77 -0
- minima_harness/tui/welcome.py +83 -0
- minima_harness/tui/widgets/__init__.py +3 -0
- minima_harness/tui/widgets/banner.py +38 -0
- minima_harness/tui/widgets/editor.py +83 -0
- minima_harness/tui/widgets/footer.py +73 -0
- minima_harness/tui/widgets/messages.py +151 -0
- minima_harness/tui/widgets/status.py +57 -0
minima_client/client.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Sync and async clients mirroring the Minima endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from minima.schemas.common import Constraints, OutcomeLabel, TaskInput
|
|
10
|
+
from minima.schemas.feedback import FeedbackRequest, FeedbackResponse
|
|
11
|
+
from minima.schemas.models_catalog import ModelsResponse
|
|
12
|
+
from minima.schemas.recommend import RecommendRequest, RecommendResponse
|
|
13
|
+
from minima.schemas.savings import CalibrationResponse, SavingsResponse
|
|
14
|
+
from minima.schemas.strategies import StrategiesResponse
|
|
15
|
+
from minima.schemas.workflow import WorkflowRequest, WorkflowResponse
|
|
16
|
+
from minima_client.errors import raise_for_status
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _report_params(
|
|
20
|
+
namespace: str | None, days: float | None, group_by: str | None = None
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
params: dict[str, Any] = {}
|
|
23
|
+
if namespace is not None:
|
|
24
|
+
params["namespace"] = namespace
|
|
25
|
+
if days is not None:
|
|
26
|
+
params["days"] = days
|
|
27
|
+
if group_by is not None:
|
|
28
|
+
params["group_by"] = group_by
|
|
29
|
+
return params
|
|
30
|
+
|
|
31
|
+
TaskLike = str | TaskInput | dict[str, Any]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _coerce_task(task: TaskLike) -> TaskInput:
|
|
35
|
+
if isinstance(task, TaskInput):
|
|
36
|
+
return task
|
|
37
|
+
if isinstance(task, str):
|
|
38
|
+
return TaskInput(task=task)
|
|
39
|
+
if isinstance(task, dict):
|
|
40
|
+
return TaskInput(**task)
|
|
41
|
+
raise TypeError(f"unsupported task type: {type(task)!r}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _headers(api_key: str | None) -> dict[str, str]:
|
|
45
|
+
return {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _feedback_request(
|
|
49
|
+
recommendation_id: str,
|
|
50
|
+
chosen_model_id: str,
|
|
51
|
+
outcome: OutcomeLabel | str,
|
|
52
|
+
**kwargs: Any,
|
|
53
|
+
) -> FeedbackRequest:
|
|
54
|
+
return FeedbackRequest(
|
|
55
|
+
recommendation_id=recommendation_id,
|
|
56
|
+
chosen_model_id=chosen_model_id,
|
|
57
|
+
outcome=OutcomeLabel(outcome),
|
|
58
|
+
**kwargs,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MinimaClient:
|
|
63
|
+
def __init__(self, base_url: str, api_key: str | None = None, timeout: float = 10.0):
|
|
64
|
+
self._client = httpx.Client(
|
|
65
|
+
base_url=base_url.rstrip("/"), headers=_headers(api_key), timeout=timeout
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def close(self) -> None:
|
|
69
|
+
self._client.close()
|
|
70
|
+
|
|
71
|
+
def __enter__(self) -> MinimaClient:
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def __exit__(self, *exc: object) -> None:
|
|
75
|
+
self.close()
|
|
76
|
+
|
|
77
|
+
def _post(self, path: str, model: Any) -> Any:
|
|
78
|
+
resp = self._client.post(path, json=model.model_dump(mode="json"))
|
|
79
|
+
raise_for_status(resp)
|
|
80
|
+
return resp.json()
|
|
81
|
+
|
|
82
|
+
def recommend(
|
|
83
|
+
self,
|
|
84
|
+
task: TaskLike,
|
|
85
|
+
*,
|
|
86
|
+
cost_quality_tradeoff: float = 5.0,
|
|
87
|
+
constraints: Constraints | None = None,
|
|
88
|
+
user_id: str | None = None,
|
|
89
|
+
namespace: str | None = None,
|
|
90
|
+
allow_llm_escalation: bool = True,
|
|
91
|
+
explain: bool = True,
|
|
92
|
+
baseline_model_id: str | None = None,
|
|
93
|
+
) -> RecommendResponse:
|
|
94
|
+
req = RecommendRequest(
|
|
95
|
+
task=_coerce_task(task),
|
|
96
|
+
cost_quality_tradeoff=cost_quality_tradeoff,
|
|
97
|
+
constraints=constraints or Constraints(),
|
|
98
|
+
user_id=user_id,
|
|
99
|
+
namespace=namespace,
|
|
100
|
+
allow_llm_escalation=allow_llm_escalation,
|
|
101
|
+
explain=explain,
|
|
102
|
+
baseline_model_id=baseline_model_id,
|
|
103
|
+
)
|
|
104
|
+
return RecommendResponse.model_validate(self._post("/v1/recommend", req))
|
|
105
|
+
|
|
106
|
+
def recommend_workflow(self, req: WorkflowRequest) -> WorkflowResponse:
|
|
107
|
+
return WorkflowResponse.model_validate(self._post("/v1/recommend/workflow", req))
|
|
108
|
+
|
|
109
|
+
def savings(
|
|
110
|
+
self,
|
|
111
|
+
namespace: str | None = None,
|
|
112
|
+
days: float | None = None,
|
|
113
|
+
group_by: str | None = None,
|
|
114
|
+
) -> SavingsResponse:
|
|
115
|
+
"""Counterfactual savings + routing health for your org (estimated AND realized)."""
|
|
116
|
+
resp = self._client.get("/v1/savings", params=_report_params(namespace, days, group_by))
|
|
117
|
+
raise_for_status(resp)
|
|
118
|
+
return SavingsResponse.model_validate(resp.json())
|
|
119
|
+
|
|
120
|
+
def calibration(
|
|
121
|
+
self,
|
|
122
|
+
namespace: str | None = None,
|
|
123
|
+
days: float | None = None,
|
|
124
|
+
) -> CalibrationResponse:
|
|
125
|
+
"""Is predicted_success telling the truth? ECE, reliability, and drift flags."""
|
|
126
|
+
resp = self._client.get("/v1/calibration", params=_report_params(namespace, days))
|
|
127
|
+
raise_for_status(resp)
|
|
128
|
+
return CalibrationResponse.model_validate(resp.json())
|
|
129
|
+
|
|
130
|
+
def feedback(
|
|
131
|
+
self,
|
|
132
|
+
recommendation_id: str,
|
|
133
|
+
chosen_model_id: str,
|
|
134
|
+
outcome: OutcomeLabel | str,
|
|
135
|
+
**kwargs: Any,
|
|
136
|
+
) -> FeedbackResponse:
|
|
137
|
+
req = _feedback_request(recommendation_id, chosen_model_id, outcome, **kwargs)
|
|
138
|
+
return FeedbackResponse.model_validate(self._post("/v1/feedback", req))
|
|
139
|
+
|
|
140
|
+
def models(
|
|
141
|
+
self,
|
|
142
|
+
provider: str | None = None,
|
|
143
|
+
task_type: str | None = None,
|
|
144
|
+
max_cost: float | None = None,
|
|
145
|
+
include_stale: bool = True,
|
|
146
|
+
) -> ModelsResponse:
|
|
147
|
+
params = {
|
|
148
|
+
k: v
|
|
149
|
+
for k, v in {
|
|
150
|
+
"provider": provider,
|
|
151
|
+
"task_type": task_type,
|
|
152
|
+
"max_cost": max_cost,
|
|
153
|
+
"include_stale": include_stale,
|
|
154
|
+
}.items()
|
|
155
|
+
if v is not None
|
|
156
|
+
}
|
|
157
|
+
resp = self._client.get("/v1/models", params=params)
|
|
158
|
+
raise_for_status(resp)
|
|
159
|
+
return ModelsResponse.model_validate(resp.json())
|
|
160
|
+
|
|
161
|
+
def strategies(
|
|
162
|
+
self,
|
|
163
|
+
namespace: str | None = None,
|
|
164
|
+
max_strategies: int = 5,
|
|
165
|
+
lesson_types: list[str] | None = None,
|
|
166
|
+
) -> StrategiesResponse:
|
|
167
|
+
params: dict[str, Any] = {"max_strategies": max_strategies}
|
|
168
|
+
if namespace is not None:
|
|
169
|
+
params["namespace"] = namespace
|
|
170
|
+
if lesson_types:
|
|
171
|
+
params["lesson_types"] = lesson_types
|
|
172
|
+
resp = self._client.get("/v1/strategies", params=params)
|
|
173
|
+
raise_for_status(resp)
|
|
174
|
+
return StrategiesResponse.model_validate(resp.json())
|
|
175
|
+
|
|
176
|
+
def health(self) -> dict[str, Any]:
|
|
177
|
+
resp = self._client.get("/v1/health")
|
|
178
|
+
raise_for_status(resp)
|
|
179
|
+
return resp.json()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class AsyncMinimaClient:
|
|
183
|
+
def __init__(self, base_url: str, api_key: str | None = None, timeout: float = 10.0):
|
|
184
|
+
self._client = httpx.AsyncClient(
|
|
185
|
+
base_url=base_url.rstrip("/"), headers=_headers(api_key), timeout=timeout
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
async def aclose(self) -> None:
|
|
189
|
+
await self._client.aclose()
|
|
190
|
+
|
|
191
|
+
async def __aenter__(self) -> AsyncMinimaClient:
|
|
192
|
+
return self
|
|
193
|
+
|
|
194
|
+
async def __aexit__(self, *exc: object) -> None:
|
|
195
|
+
await self.aclose()
|
|
196
|
+
|
|
197
|
+
async def _post(self, path: str, model: Any) -> Any:
|
|
198
|
+
resp = await self._client.post(path, json=model.model_dump(mode="json"))
|
|
199
|
+
raise_for_status(resp)
|
|
200
|
+
return resp.json()
|
|
201
|
+
|
|
202
|
+
async def recommend(
|
|
203
|
+
self,
|
|
204
|
+
task: TaskLike,
|
|
205
|
+
*,
|
|
206
|
+
cost_quality_tradeoff: float = 5.0,
|
|
207
|
+
constraints: Constraints | None = None,
|
|
208
|
+
user_id: str | None = None,
|
|
209
|
+
namespace: str | None = None,
|
|
210
|
+
allow_llm_escalation: bool = True,
|
|
211
|
+
explain: bool = True,
|
|
212
|
+
baseline_model_id: str | None = None,
|
|
213
|
+
) -> RecommendResponse:
|
|
214
|
+
req = RecommendRequest(
|
|
215
|
+
task=_coerce_task(task),
|
|
216
|
+
cost_quality_tradeoff=cost_quality_tradeoff,
|
|
217
|
+
constraints=constraints or Constraints(),
|
|
218
|
+
user_id=user_id,
|
|
219
|
+
namespace=namespace,
|
|
220
|
+
allow_llm_escalation=allow_llm_escalation,
|
|
221
|
+
explain=explain,
|
|
222
|
+
baseline_model_id=baseline_model_id,
|
|
223
|
+
)
|
|
224
|
+
return RecommendResponse.model_validate(await self._post("/v1/recommend", req))
|
|
225
|
+
|
|
226
|
+
async def recommend_workflow(self, req: WorkflowRequest) -> WorkflowResponse:
|
|
227
|
+
return WorkflowResponse.model_validate(await self._post("/v1/recommend/workflow", req))
|
|
228
|
+
|
|
229
|
+
async def savings(
|
|
230
|
+
self,
|
|
231
|
+
namespace: str | None = None,
|
|
232
|
+
days: float | None = None,
|
|
233
|
+
group_by: str | None = None,
|
|
234
|
+
) -> SavingsResponse:
|
|
235
|
+
"""Counterfactual savings + routing health for your org (estimated AND realized)."""
|
|
236
|
+
resp = await self._client.get(
|
|
237
|
+
"/v1/savings", params=_report_params(namespace, days, group_by)
|
|
238
|
+
)
|
|
239
|
+
raise_for_status(resp)
|
|
240
|
+
return SavingsResponse.model_validate(resp.json())
|
|
241
|
+
|
|
242
|
+
async def calibration(
|
|
243
|
+
self,
|
|
244
|
+
namespace: str | None = None,
|
|
245
|
+
days: float | None = None,
|
|
246
|
+
) -> CalibrationResponse:
|
|
247
|
+
"""Is predicted_success telling the truth? ECE, reliability, and drift flags."""
|
|
248
|
+
resp = await self._client.get("/v1/calibration", params=_report_params(namespace, days))
|
|
249
|
+
raise_for_status(resp)
|
|
250
|
+
return CalibrationResponse.model_validate(resp.json())
|
|
251
|
+
|
|
252
|
+
async def feedback(
|
|
253
|
+
self,
|
|
254
|
+
recommendation_id: str,
|
|
255
|
+
chosen_model_id: str,
|
|
256
|
+
outcome: OutcomeLabel | str,
|
|
257
|
+
**kwargs: Any,
|
|
258
|
+
) -> FeedbackResponse:
|
|
259
|
+
req = _feedback_request(recommendation_id, chosen_model_id, outcome, **kwargs)
|
|
260
|
+
return FeedbackResponse.model_validate(await self._post("/v1/feedback", req))
|
|
261
|
+
|
|
262
|
+
async def strategies(
|
|
263
|
+
self,
|
|
264
|
+
namespace: str | None = None,
|
|
265
|
+
max_strategies: int = 5,
|
|
266
|
+
lesson_types: list[str] | None = None,
|
|
267
|
+
) -> StrategiesResponse:
|
|
268
|
+
params: dict[str, Any] = {"max_strategies": max_strategies}
|
|
269
|
+
if namespace is not None:
|
|
270
|
+
params["namespace"] = namespace
|
|
271
|
+
if lesson_types:
|
|
272
|
+
params["lesson_types"] = lesson_types
|
|
273
|
+
resp = await self._client.get("/v1/strategies", params=params)
|
|
274
|
+
raise_for_status(resp)
|
|
275
|
+
return StrategiesResponse.model_validate(resp.json())
|
|
276
|
+
|
|
277
|
+
async def models(
|
|
278
|
+
self,
|
|
279
|
+
provider: str | None = None,
|
|
280
|
+
task_type: str | None = None,
|
|
281
|
+
max_cost: float | None = None,
|
|
282
|
+
include_stale: bool = True,
|
|
283
|
+
) -> ModelsResponse:
|
|
284
|
+
params = {
|
|
285
|
+
k: v
|
|
286
|
+
for k, v in {
|
|
287
|
+
"provider": provider,
|
|
288
|
+
"task_type": task_type,
|
|
289
|
+
"max_cost": max_cost,
|
|
290
|
+
"include_stale": include_stale,
|
|
291
|
+
}.items()
|
|
292
|
+
if v is not None
|
|
293
|
+
}
|
|
294
|
+
resp = await self._client.get("/v1/models", params=params)
|
|
295
|
+
raise_for_status(resp)
|
|
296
|
+
return ModelsResponse.model_validate(resp.json())
|
|
297
|
+
|
|
298
|
+
async def health(self) -> dict[str, Any]:
|
|
299
|
+
resp = await self._client.get("/v1/health")
|
|
300
|
+
raise_for_status(resp)
|
|
301
|
+
return resp.json()
|
minima_client/errors.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Client error type."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MinimaError(Exception):
|
|
9
|
+
def __init__(self, status: int, detail: str):
|
|
10
|
+
super().__init__(f"minima error {status}: {detail}")
|
|
11
|
+
self.status = status
|
|
12
|
+
self.detail = detail
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def raise_for_status(resp: httpx.Response) -> None:
|
|
16
|
+
if resp.status_code < 400:
|
|
17
|
+
return
|
|
18
|
+
try:
|
|
19
|
+
body = resp.json()
|
|
20
|
+
detail = body.get("detail") or body.get("title") or str(body)
|
|
21
|
+
except Exception: # noqa: BLE001
|
|
22
|
+
detail = resp.text
|
|
23
|
+
raise MinimaError(resp.status_code, detail)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
MIT License Attribution
|
|
2
|
+
=======================
|
|
3
|
+
|
|
4
|
+
Portions of this package (`src/minima_harness/`) are derived from the
|
|
5
|
+
`@earendil-works/pi <https://github.com/earendil-works/pi>`_ project, which is
|
|
6
|
+
licensed under the MIT License:
|
|
7
|
+
|
|
8
|
+
MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) the @earendil-works/pi contributors
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
|
|
30
|
+
This is a from-scratch Python port of design and APIs; no source code was copied
|
|
31
|
+
verbatim. The Minima integration layer (`src/minima_harness/minima/`) is original
|
|
32
|
+
work and is governed by the project's own license.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""minima_harness — a lean Python port of @earendil-works/pi's agent harness, made
|
|
2
|
+
Minima-native (routes each turn through Minima's recommender and feeds outcomes back).
|
|
3
|
+
|
|
4
|
+
Phase 0 ships the unified LLM API surface (``minima_harness.ai``), the task corpus
|
|
5
|
+
(``minima_harness.tasks``), and harness config (``minima_harness.minima.HarnessConfig``).
|
|
6
|
+
The ported agent runtime and the Minima router land in later phases.
|
|
7
|
+
|
|
8
|
+
Derived from the MIT-licensed @earendil-works/pi (see LICENSE_PI).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from minima_harness import ai, minima, tasks
|
|
12
|
+
from minima_harness.minima.config import HarnessConfig
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = ["HarnessConfig", "__version__", "ai", "minima", "tasks"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""minima_harness.agent — ported pi-agent-core: Agent + agent_loop + events + tools."""
|
|
2
|
+
|
|
3
|
+
from minima_harness.agent.agent import Agent
|
|
4
|
+
from minima_harness.agent.events import (
|
|
5
|
+
AgentEndEvent,
|
|
6
|
+
AgentEvent,
|
|
7
|
+
AgentStartEvent,
|
|
8
|
+
MessageEndEvent,
|
|
9
|
+
MessageStartEvent,
|
|
10
|
+
MessageUpdateEvent,
|
|
11
|
+
ToolExecutionEndEvent,
|
|
12
|
+
ToolExecutionStartEvent,
|
|
13
|
+
ToolExecutionUpdateEvent,
|
|
14
|
+
TurnEndEvent,
|
|
15
|
+
TurnStartEvent,
|
|
16
|
+
)
|
|
17
|
+
from minima_harness.agent.loop import agent_loop, agent_loop_continue
|
|
18
|
+
from minima_harness.agent.state import (
|
|
19
|
+
AgentLoopConfig,
|
|
20
|
+
AgentState,
|
|
21
|
+
ConvertToLlm,
|
|
22
|
+
TransformContext,
|
|
23
|
+
default_convert_to_llm,
|
|
24
|
+
)
|
|
25
|
+
from minima_harness.agent.tools import (
|
|
26
|
+
AfterToolCall,
|
|
27
|
+
AfterToolCallContext,
|
|
28
|
+
AfterToolCallResult,
|
|
29
|
+
AgentTool,
|
|
30
|
+
BeforeToolCall,
|
|
31
|
+
BeforeToolCallContext,
|
|
32
|
+
BeforeToolCallResult,
|
|
33
|
+
ThinkingLevel,
|
|
34
|
+
ToolExecutionMode,
|
|
35
|
+
ToolResult,
|
|
36
|
+
error_result,
|
|
37
|
+
find_agent_tool,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"AfterToolCall",
|
|
42
|
+
"AfterToolCallContext",
|
|
43
|
+
"AfterToolCallResult",
|
|
44
|
+
"Agent",
|
|
45
|
+
"AgentEndEvent",
|
|
46
|
+
"AgentEvent",
|
|
47
|
+
"AgentLoopConfig",
|
|
48
|
+
"AgentStartEvent",
|
|
49
|
+
"AgentState",
|
|
50
|
+
"AgentTool",
|
|
51
|
+
"BeforeToolCall",
|
|
52
|
+
"BeforeToolCallContext",
|
|
53
|
+
"BeforeToolCallResult",
|
|
54
|
+
"ConvertToLlm",
|
|
55
|
+
"MessageEndEvent",
|
|
56
|
+
"MessageStartEvent",
|
|
57
|
+
"MessageUpdateEvent",
|
|
58
|
+
"ThinkingLevel",
|
|
59
|
+
"ToolExecutionEndEvent",
|
|
60
|
+
"ToolExecutionMode",
|
|
61
|
+
"ToolExecutionStartEvent",
|
|
62
|
+
"ToolExecutionUpdateEvent",
|
|
63
|
+
"ToolResult",
|
|
64
|
+
"TransformContext",
|
|
65
|
+
"TurnEndEvent",
|
|
66
|
+
"TurnStartEvent",
|
|
67
|
+
"agent_loop",
|
|
68
|
+
"agent_loop_continue",
|
|
69
|
+
"default_convert_to_llm",
|
|
70
|
+
"error_result",
|
|
71
|
+
"find_agent_tool",
|
|
72
|
+
]
|