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
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""The stateful Agent — a port of PI's ``pi-agent-core`` ``Agent`` class.
|
|
2
|
+
|
|
3
|
+
Wraps :func:`agent_loop` with: persistent state across prompts, ordered awaited
|
|
4
|
+
subscribers, abort via an anyio cancel scope, and steering/follow-up queues injected
|
|
5
|
+
between turns. ``prompt()`` awaits the full run inline (matching PI); for background use
|
|
6
|
+
launch it in a task and await :meth:`wait_for_idle`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import inspect
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
import anyio
|
|
17
|
+
|
|
18
|
+
from minima_harness.agent.events import AgentEvent
|
|
19
|
+
from minima_harness.agent.loop import agent_loop
|
|
20
|
+
from minima_harness.agent.state import (
|
|
21
|
+
AgentLoopConfig,
|
|
22
|
+
AgentState,
|
|
23
|
+
ConvertToLlm,
|
|
24
|
+
TransformContext,
|
|
25
|
+
default_convert_to_llm,
|
|
26
|
+
)
|
|
27
|
+
from minima_harness.agent.tools import (
|
|
28
|
+
AfterToolCall,
|
|
29
|
+
BeforeToolCall,
|
|
30
|
+
ThinkingLevel,
|
|
31
|
+
ToolExecutionMode,
|
|
32
|
+
)
|
|
33
|
+
from minima_harness.ai.types import ContentBlock, Message, Model
|
|
34
|
+
|
|
35
|
+
Listener = Callable[[AgentEvent], Any]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Agent:
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
*,
|
|
42
|
+
model: Model,
|
|
43
|
+
system_prompt: str | None = None,
|
|
44
|
+
tools: list | None = None,
|
|
45
|
+
messages: list[Message] | None = None,
|
|
46
|
+
thinking_level: ThinkingLevel = "off",
|
|
47
|
+
convert_to_llm: ConvertToLlm = default_convert_to_llm,
|
|
48
|
+
transform_context: TransformContext | None = None,
|
|
49
|
+
tool_execution: ToolExecutionMode = "parallel",
|
|
50
|
+
before_tool_call: BeforeToolCall | None = None,
|
|
51
|
+
after_tool_call: AfterToolCall | None = None,
|
|
52
|
+
thinking_budgets: dict[str, int] | None = None,
|
|
53
|
+
max_turns: int = 50,
|
|
54
|
+
session_id: str | None = None,
|
|
55
|
+
stream_options: dict[str, Any] | None = None,
|
|
56
|
+
steering_mode: str = "one-at-a-time",
|
|
57
|
+
follow_up_mode: str = "one-at-a-time",
|
|
58
|
+
should_stop_after_turn: Any = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
self._state = AgentState(
|
|
61
|
+
system_prompt=system_prompt,
|
|
62
|
+
model=model,
|
|
63
|
+
thinking_level=thinking_level,
|
|
64
|
+
tools=list(tools or []),
|
|
65
|
+
messages=list(messages or []),
|
|
66
|
+
steering_mode=steering_mode,
|
|
67
|
+
follow_up_mode=follow_up_mode,
|
|
68
|
+
)
|
|
69
|
+
self._convert_to_llm = convert_to_llm
|
|
70
|
+
self._transform_context = transform_context
|
|
71
|
+
self._tool_execution = tool_execution
|
|
72
|
+
self._before_tool_call = before_tool_call
|
|
73
|
+
self._after_tool_call = after_tool_call
|
|
74
|
+
self._thinking_budgets = thinking_budgets
|
|
75
|
+
self._max_turns = max_turns
|
|
76
|
+
self._session_id = session_id
|
|
77
|
+
self._stream_options = stream_options
|
|
78
|
+
self._should_stop_after_turn = should_stop_after_turn
|
|
79
|
+
self._listeners: list[Listener] = []
|
|
80
|
+
self._cancel_scope: anyio.CancelScope | None = None
|
|
81
|
+
self._idle = asyncio.Event()
|
|
82
|
+
self._idle.set()
|
|
83
|
+
|
|
84
|
+
# ----------------------------------------------------------------- state
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def state(self) -> AgentState:
|
|
88
|
+
return self._state
|
|
89
|
+
|
|
90
|
+
def reset(self) -> None:
|
|
91
|
+
"""Clear the conversation + error; keep model, tools, system prompt."""
|
|
92
|
+
self._state.messages = []
|
|
93
|
+
self._state.streaming_message = None
|
|
94
|
+
self._state.error_message = None
|
|
95
|
+
self._state.pending_tool_calls.clear()
|
|
96
|
+
|
|
97
|
+
# ----------------------------------------------- mutators (PI-style attrs)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def tool_execution(self) -> ToolExecutionMode:
|
|
101
|
+
return self._tool_execution
|
|
102
|
+
|
|
103
|
+
@tool_execution.setter
|
|
104
|
+
def tool_execution(self, value: ToolExecutionMode) -> None:
|
|
105
|
+
self._tool_execution = value
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def before_tool_call(self) -> BeforeToolCall | None:
|
|
109
|
+
return self._before_tool_call
|
|
110
|
+
|
|
111
|
+
@before_tool_call.setter
|
|
112
|
+
def before_tool_call(self, value: BeforeToolCall | None) -> None:
|
|
113
|
+
self._before_tool_call = value
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def after_tool_call(self) -> AfterToolCall | None:
|
|
117
|
+
return self._after_tool_call
|
|
118
|
+
|
|
119
|
+
@after_tool_call.setter
|
|
120
|
+
def after_tool_call(self, value: AfterToolCall | None) -> None:
|
|
121
|
+
self._after_tool_call = value
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def session_id(self) -> str | None:
|
|
125
|
+
return self._session_id
|
|
126
|
+
|
|
127
|
+
@session_id.setter
|
|
128
|
+
def session_id(self, value: str | None) -> None:
|
|
129
|
+
self._session_id = value
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def thinking_budgets(self) -> dict[str, int] | None:
|
|
133
|
+
return self._thinking_budgets
|
|
134
|
+
|
|
135
|
+
@thinking_budgets.setter
|
|
136
|
+
def thinking_budgets(self, value: dict[str, int] | None) -> None:
|
|
137
|
+
self._thinking_budgets = value
|
|
138
|
+
|
|
139
|
+
# ------------------------------------------------------------- subscribe
|
|
140
|
+
|
|
141
|
+
def subscribe(self, listener: Listener) -> Callable[[], None]:
|
|
142
|
+
"""Register a listener (sync or async). Returns an unsubscribe callable."""
|
|
143
|
+
self._listeners.append(listener)
|
|
144
|
+
|
|
145
|
+
def _unsubscribe() -> None:
|
|
146
|
+
try:
|
|
147
|
+
self._listeners.remove(listener)
|
|
148
|
+
except ValueError:
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
return _unsubscribe
|
|
152
|
+
|
|
153
|
+
async def _dispatch(self, event: AgentEvent) -> None:
|
|
154
|
+
for listener in list(self._listeners):
|
|
155
|
+
result = listener(event)
|
|
156
|
+
if inspect.isawaitable(result):
|
|
157
|
+
await result
|
|
158
|
+
|
|
159
|
+
# ----------------------------------------------------------- prompting
|
|
160
|
+
|
|
161
|
+
async def prompt(self, content: str | list[ContentBlock] | Message | list[Any]) -> None:
|
|
162
|
+
"""Run the loop with ``content`` appended as a user turn. Awaits completion."""
|
|
163
|
+
await self._run(self._coerce_prompts(content))
|
|
164
|
+
|
|
165
|
+
async def continue_(self) -> None:
|
|
166
|
+
"""Resume from current context without a new user message."""
|
|
167
|
+
if self._state.messages and self._state.messages[-1].role == "assistant":
|
|
168
|
+
raise ValueError("continue_() requires the last message to be user or toolResult")
|
|
169
|
+
await self._run([])
|
|
170
|
+
|
|
171
|
+
async def _run(self, prompts: list[Message]) -> None:
|
|
172
|
+
if self._state.is_streaming:
|
|
173
|
+
raise RuntimeError("agent is already running")
|
|
174
|
+
self._idle.clear()
|
|
175
|
+
self._state.is_streaming = True
|
|
176
|
+
self._state.error_message = None
|
|
177
|
+
scope = anyio.CancelScope()
|
|
178
|
+
try:
|
|
179
|
+
with scope:
|
|
180
|
+
self._cancel_scope = scope
|
|
181
|
+
config = self._build_config()
|
|
182
|
+
async for event in agent_loop(prompts, self._state, config):
|
|
183
|
+
await self._dispatch(event)
|
|
184
|
+
if scope.cancelled_caught:
|
|
185
|
+
if self._state.error_message is None:
|
|
186
|
+
self._state.error_message = "aborted"
|
|
187
|
+
finally:
|
|
188
|
+
self._cancel_scope = None
|
|
189
|
+
self._state.is_streaming = False
|
|
190
|
+
self._state.streaming_message = None
|
|
191
|
+
self._idle.set()
|
|
192
|
+
|
|
193
|
+
def abort(self) -> None:
|
|
194
|
+
"""Cancel the in-flight run (if any). No-op when idle."""
|
|
195
|
+
if self._cancel_scope is not None:
|
|
196
|
+
self._cancel_scope.cancel()
|
|
197
|
+
|
|
198
|
+
async def wait_for_idle(self) -> None:
|
|
199
|
+
"""Await the current run's completion (for background-task usage)."""
|
|
200
|
+
await self._idle.wait()
|
|
201
|
+
|
|
202
|
+
# ----------------------------------------------------- steering/follow-up
|
|
203
|
+
|
|
204
|
+
def steer(self, message: Message | str) -> None:
|
|
205
|
+
self._state.steering.append(self._as_message(message))
|
|
206
|
+
|
|
207
|
+
def follow_up(self, message: Message | str) -> None:
|
|
208
|
+
self._state.follow_up.append(self._as_message(message))
|
|
209
|
+
|
|
210
|
+
def clear_steering_queue(self) -> None:
|
|
211
|
+
self._state.steering.clear()
|
|
212
|
+
|
|
213
|
+
def clear_follow_up_queue(self) -> None:
|
|
214
|
+
self._state.follow_up.clear()
|
|
215
|
+
|
|
216
|
+
def clear_all_queues(self) -> None:
|
|
217
|
+
self.clear_steering_queue()
|
|
218
|
+
self.clear_follow_up_queue()
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def steering_mode(self) -> str:
|
|
222
|
+
return self._state.steering_mode
|
|
223
|
+
|
|
224
|
+
@steering_mode.setter
|
|
225
|
+
def steering_mode(self, value: str) -> None:
|
|
226
|
+
self._state.steering_mode = value
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def follow_up_mode(self) -> str:
|
|
230
|
+
return self._state.follow_up_mode
|
|
231
|
+
|
|
232
|
+
@follow_up_mode.setter
|
|
233
|
+
def follow_up_mode(self, value: str) -> None:
|
|
234
|
+
self._state.follow_up_mode = value
|
|
235
|
+
|
|
236
|
+
# --------------------------------------------------------------- internals
|
|
237
|
+
|
|
238
|
+
def _build_config(self) -> AgentLoopConfig:
|
|
239
|
+
assert self._state.model is not None
|
|
240
|
+
return AgentLoopConfig(
|
|
241
|
+
model=self._state.model,
|
|
242
|
+
convert_to_llm=self._convert_to_llm,
|
|
243
|
+
tool_execution=self._tool_execution,
|
|
244
|
+
before_tool_call=self._before_tool_call,
|
|
245
|
+
after_tool_call=self._after_tool_call,
|
|
246
|
+
transform_context=self._transform_context,
|
|
247
|
+
should_stop_after_turn=self._should_stop_after_turn,
|
|
248
|
+
thinking_budgets=self._thinking_budgets,
|
|
249
|
+
thinking_level=self._state.thinking_level,
|
|
250
|
+
max_turns=self._max_turns,
|
|
251
|
+
session_id=self._session_id,
|
|
252
|
+
stream_options=self._stream_options,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def _as_message(message: Message | str) -> Message:
|
|
257
|
+
if isinstance(message, Message):
|
|
258
|
+
return message
|
|
259
|
+
return Message(role="user", content=message)
|
|
260
|
+
|
|
261
|
+
def _coerce_prompts(self, content: Any) -> list[Message]:
|
|
262
|
+
if isinstance(content, Message):
|
|
263
|
+
return [content]
|
|
264
|
+
if isinstance(content, str):
|
|
265
|
+
return [Message(role="user", content=content)]
|
|
266
|
+
if isinstance(content, list):
|
|
267
|
+
prompts: list[Message] = []
|
|
268
|
+
for item in content:
|
|
269
|
+
if isinstance(item, Message):
|
|
270
|
+
prompts.append(item)
|
|
271
|
+
elif isinstance(item, str):
|
|
272
|
+
prompts.append(Message(role="user", content=item))
|
|
273
|
+
else:
|
|
274
|
+
prompts.append(Message(role="user", content=item))
|
|
275
|
+
return prompts
|
|
276
|
+
return [Message(role="user", content=content)]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Agent runtime events — a port of PI's ``pi-agent-core`` event taxonomy.
|
|
2
|
+
|
|
3
|
+
Emitted by :func:`minima_harness.agent.loop.agent_loop` in a strict order per turn::
|
|
4
|
+
|
|
5
|
+
agent_start
|
|
6
|
+
(per turn)
|
|
7
|
+
turn_start
|
|
8
|
+
message_start {user or toolResult}
|
|
9
|
+
message_end {...}
|
|
10
|
+
message_start {assistant}
|
|
11
|
+
message_update {assistant_message_event: a provider StreamEvent}
|
|
12
|
+
message_end {assistant}
|
|
13
|
+
tool_execution_start / tool_execution_update / tool_execution_end (if toolUse)
|
|
14
|
+
message_start {toolResult} / message_end
|
|
15
|
+
turn_end
|
|
16
|
+
agent_end
|
|
17
|
+
|
|
18
|
+
Events are immutable dataclasses so they can be fanned out to many subscribers safely.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
25
|
+
|
|
26
|
+
from minima_harness.ai.events import Event as StreamEvent
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class AgentStartEvent:
|
|
34
|
+
type: Literal["agent_start"] = "agent_start"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class AgentEndEvent:
|
|
39
|
+
type: Literal["agent_end"] = "agent_end"
|
|
40
|
+
messages: list = field(default_factory=list) # list[Message]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True, slots=True)
|
|
44
|
+
class TurnStartEvent:
|
|
45
|
+
type: Literal["turn_start"] = "turn_start"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class TurnEndEvent:
|
|
50
|
+
type: Literal["turn_end"] = "turn_end"
|
|
51
|
+
message: Any = None # AssistantMessage | None
|
|
52
|
+
tool_results: list = field(default_factory=list) # list[ToolResult]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, slots=True)
|
|
56
|
+
class MessageStartEvent:
|
|
57
|
+
type: Literal["message_start"] = "message_start"
|
|
58
|
+
message: Any = None # Message | None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True, slots=True)
|
|
62
|
+
class MessageUpdateEvent:
|
|
63
|
+
"""Assistant-only. Wraps a provider streaming event (text/thinking/toolcall delta)."""
|
|
64
|
+
|
|
65
|
+
type: Literal["message_update"] = "message_update"
|
|
66
|
+
assistant_message_event: StreamEvent | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True)
|
|
70
|
+
class MessageEndEvent:
|
|
71
|
+
type: Literal["message_end"] = "message_end"
|
|
72
|
+
message: Any = None # Message | None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True, slots=True)
|
|
76
|
+
class ToolExecutionStartEvent:
|
|
77
|
+
type: Literal["tool_execution_start"] = "tool_execution_start"
|
|
78
|
+
tool_call_id: str = ""
|
|
79
|
+
tool_name: str = ""
|
|
80
|
+
args: Any = None # validated params (pydantic model) | None when blocked/invalid
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True, slots=True)
|
|
84
|
+
class ToolExecutionUpdateEvent:
|
|
85
|
+
type: Literal["tool_execution_update"] = "tool_execution_update"
|
|
86
|
+
tool_call_id: str = ""
|
|
87
|
+
partial: Any = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(frozen=True, slots=True)
|
|
91
|
+
class ToolExecutionEndEvent:
|
|
92
|
+
type: Literal["tool_execution_end"] = "tool_execution_end"
|
|
93
|
+
tool_call_id: str = ""
|
|
94
|
+
result: Any = None # ToolResult | None
|
|
95
|
+
is_error: bool = False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
AgentEvent = (
|
|
99
|
+
AgentStartEvent
|
|
100
|
+
| AgentEndEvent
|
|
101
|
+
| TurnStartEvent
|
|
102
|
+
| TurnEndEvent
|
|
103
|
+
| MessageStartEvent
|
|
104
|
+
| MessageUpdateEvent
|
|
105
|
+
| MessageEndEvent
|
|
106
|
+
| ToolExecutionStartEvent
|
|
107
|
+
| ToolExecutionUpdateEvent
|
|
108
|
+
| ToolExecutionEndEvent
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
__all__ = [
|
|
113
|
+
"AgentEndEvent",
|
|
114
|
+
"AgentEvent",
|
|
115
|
+
"AgentStartEvent",
|
|
116
|
+
"MessageEndEvent",
|
|
117
|
+
"MessageStartEvent",
|
|
118
|
+
"MessageUpdateEvent",
|
|
119
|
+
"ToolExecutionEndEvent",
|
|
120
|
+
"ToolExecutionStartEvent",
|
|
121
|
+
"ToolExecutionUpdateEvent",
|
|
122
|
+
"TurnEndEvent",
|
|
123
|
+
"TurnStartEvent",
|
|
124
|
+
]
|