ellements 0.2.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.
- ellements/__init__.py +57 -0
- ellements/agents/__init__.py +45 -0
- ellements/agents/backend.py +100 -0
- ellements/agents/builder.py +303 -0
- ellements/agents/claude_backend.py +200 -0
- ellements/agents/controller.py +237 -0
- ellements/agents/openai_backend.py +187 -0
- ellements/agents/py.typed +0 -0
- ellements/agents/runner.py +358 -0
- ellements/agents/tools.py +30 -0
- ellements/benchmarking/__init__.py +52 -0
- ellements/benchmarking/harness.py +342 -0
- ellements/benchmarking/py.typed +0 -0
- ellements/benchmarking/results.py +173 -0
- ellements/cli/__init__.py +80 -0
- ellements/cli/adapters.py +73 -0
- ellements/cli/agent_tui.py +1178 -0
- ellements/cli/components.py +411 -0
- ellements/cli/printer.py +229 -0
- ellements/cli/py.typed +0 -0
- ellements/core/__init__.py +112 -0
- ellements/core/async_utils.py +42 -0
- ellements/core/budgeting/__init__.py +43 -0
- ellements/core/budgeting/client.py +276 -0
- ellements/core/budgeting/protocol.py +51 -0
- ellements/core/budgeting/trackers.py +177 -0
- ellements/core/caching/__init__.py +51 -0
- ellements/core/caching/cache.py +73 -0
- ellements/core/caching/client.py +300 -0
- ellements/core/caching/disk.py +128 -0
- ellements/core/caching/keys.py +97 -0
- ellements/core/caching/memory.py +104 -0
- ellements/core/chunking.py +262 -0
- ellements/core/config.py +180 -0
- ellements/core/exceptions.py +145 -0
- ellements/core/llm/__init__.py +46 -0
- ellements/core/llm/client.py +1124 -0
- ellements/core/llm/images.py +226 -0
- ellements/core/llm/messages.py +202 -0
- ellements/core/llm/model_params.py +66 -0
- ellements/core/llm/protocol.py +146 -0
- ellements/core/llm/requests.py +100 -0
- ellements/core/llm/structured.py +91 -0
- ellements/core/llm/wrapper.py +79 -0
- ellements/core/observability/__init__.py +39 -0
- ellements/core/observability/events.py +169 -0
- ellements/core/observability/jsonl_logger.py +244 -0
- ellements/core/observability/markdown_formatter.py +197 -0
- ellements/core/observability/observer.py +56 -0
- ellements/core/prompting/__init__.py +14 -0
- ellements/core/prompting/context.py +185 -0
- ellements/core/prompting/guideline.py +133 -0
- ellements/core/prompting/persona.py +267 -0
- ellements/core/prompting/sources.py +92 -0
- ellements/core/py.typed +0 -0
- ellements/core/rate_limit/__init__.py +44 -0
- ellements/core/rate_limit/bucket.py +85 -0
- ellements/core/rate_limit/client.py +216 -0
- ellements/core/rate_limit/protocol.py +27 -0
- ellements/core/templating.py +126 -0
- ellements/core/tools/__init__.py +51 -0
- ellements/core/tools/dialects.py +136 -0
- ellements/core/tools/executor.py +48 -0
- ellements/core/tools/protocol.py +36 -0
- ellements/core/tools/records.py +28 -0
- ellements/core/tools/registry.py +205 -0
- ellements/core/tools/schemas.py +80 -0
- ellements/core/tools/simple.py +119 -0
- ellements/core/tools/spec.py +33 -0
- ellements/domain_specific/__init__.py +7 -0
- ellements/domain_specific/finance/__init__.py +188 -0
- ellements/domain_specific/finance/calculations.py +837 -0
- ellements/domain_specific/finance/charts.py +426 -0
- ellements/domain_specific/finance/portfolio.py +129 -0
- ellements/domain_specific/finance/quant_analysis.py +279 -0
- ellements/domain_specific/finance/risk.py +362 -0
- ellements/domain_specific/finance/technical_indicators.py +241 -0
- ellements/domain_specific/finance/tools.py +483 -0
- ellements/domain_specific/finance/valuation.py +312 -0
- ellements/domain_specific/finance/yahoo_finance.py +1523 -0
- ellements/domain_specific/finance/yahoo_finance_models.py +321 -0
- ellements/domain_specific/py.typed +0 -0
- ellements/execution/__init__.py +56 -0
- ellements/execution/callbacks.py +149 -0
- ellements/execution/catalog.py +70 -0
- ellements/execution/collaborative.py +191 -0
- ellements/execution/config.py +135 -0
- ellements/execution/py.typed +0 -0
- ellements/execution/reflection.py +156 -0
- ellements/execution/self_consistency.py +189 -0
- ellements/execution/single_call.py +48 -0
- ellements/execution/strategies.py +237 -0
- ellements/execution/tree_of_thought.py +541 -0
- ellements/fslm/__init__.py +108 -0
- ellements/fslm/builtins.py +103 -0
- ellements/fslm/cli.py +199 -0
- ellements/fslm/context.py +50 -0
- ellements/fslm/definition.py +163 -0
- ellements/fslm/det.py +173 -0
- ellements/fslm/dsl.py +458 -0
- ellements/fslm/errors.py +38 -0
- ellements/fslm/evaluators.py +141 -0
- ellements/fslm/kernel.py +603 -0
- ellements/fslm/loading.py +123 -0
- ellements/fslm/models.py +623 -0
- ellements/fslm/nl.py +87 -0
- ellements/fslm/observers.py +107 -0
- ellements/fslm/persistence.py +72 -0
- ellements/fslm/py.typed +0 -0
- ellements/fslm/rendering.py +551 -0
- ellements/fslm/visualization.py +25 -0
- ellements/reporting/__init__.py +12 -0
- ellements/reporting/charts.py +364 -0
- ellements/reporting/html_generation.py +132 -0
- ellements/reporting/py.typed +0 -0
- ellements/reporting/visualization.py +73 -0
- ellements/standard_tools/__init__.py +22 -0
- ellements/standard_tools/py.typed +0 -0
- ellements/standard_tools/terminal.py +205 -0
- ellements/standard_tools/web/__init__.py +37 -0
- ellements/standard_tools/web/browser_viewer.py +214 -0
- ellements/standard_tools/web/crawler.py +454 -0
- ellements/standard_tools/web/search.py +399 -0
- ellements/standard_tools/web/youtube.py +1297 -0
- ellements-0.2.0.dist-info/METADATA +368 -0
- ellements-0.2.0.dist-info/RECORD +130 -0
- ellements-0.2.0.dist-info/WHEEL +5 -0
- ellements-0.2.0.dist-info/entry_points.txt +2 -0
- ellements-0.2.0.dist-info/licenses/LICENSE +42 -0
- ellements-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Run agents through any :class:`AgentBackend` with progress + stats.
|
|
2
|
+
|
|
3
|
+
The runner is backend-agnostic: it consumes the uniform
|
|
4
|
+
:class:`~ellements.agents.AgentEvent` vocabulary emitted by
|
|
5
|
+
:meth:`AgentBackend.stream_run`. Stats are kept in a clean dict-backed
|
|
6
|
+
:class:`AgentRunStats` model — there are no legacy ``searches`` /
|
|
7
|
+
``results_examined`` aliases. Callers that want them increment the
|
|
8
|
+
metric explicitly via :meth:`AgentRunStats.increment_metric`.
|
|
9
|
+
|
|
10
|
+
The single entry point is :func:`run_agent_with_progress`.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import json
|
|
17
|
+
from collections.abc import Awaitable, Callable
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from .backend import AgentBackend, AgentEvent
|
|
22
|
+
|
|
23
|
+
DEFAULT_MAX_TURNS = 200
|
|
24
|
+
|
|
25
|
+
ProgressCallback = Callable[[str], Awaitable[None] | None]
|
|
26
|
+
StatsCallback = Callable[["AgentRunStats"], Awaitable[None] | None]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ── Stats ──────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class AgentRunStats:
|
|
34
|
+
"""Statistics gathered during one agent run."""
|
|
35
|
+
|
|
36
|
+
tool_calls: int = 0
|
|
37
|
+
tool_outputs: int = 0
|
|
38
|
+
observed_items: int = 0
|
|
39
|
+
tool_names: dict[str, int] = field(default_factory=dict)
|
|
40
|
+
metrics: dict[str, int] = field(default_factory=dict)
|
|
41
|
+
|
|
42
|
+
def increment_metric(self, name: str, amount: int = 1) -> None:
|
|
43
|
+
self.metrics[name] = self.metrics.get(name, 0) + amount
|
|
44
|
+
|
|
45
|
+
def increment_tool_call(self, tool_name: str) -> None:
|
|
46
|
+
self.tool_calls += 1
|
|
47
|
+
clean = (tool_name or "unknown").strip() or "unknown"
|
|
48
|
+
self.tool_names[clean] = self.tool_names.get(clean, 0) + 1
|
|
49
|
+
|
|
50
|
+
def increment_tool_output(self, amount: int = 1) -> None:
|
|
51
|
+
self.tool_outputs += amount
|
|
52
|
+
|
|
53
|
+
def observe_items(self, count: int) -> None:
|
|
54
|
+
if count > 0:
|
|
55
|
+
self.observed_items += count
|
|
56
|
+
|
|
57
|
+
def merge(self, other: AgentRunStats) -> None:
|
|
58
|
+
self.tool_calls += other.tool_calls
|
|
59
|
+
self.tool_outputs += other.tool_outputs
|
|
60
|
+
self.observed_items += other.observed_items
|
|
61
|
+
for name, count in other.tool_names.items():
|
|
62
|
+
self.tool_names[name] = self.tool_names.get(name, 0) + count
|
|
63
|
+
for name, count in other.metrics.items():
|
|
64
|
+
self.metrics[name] = self.metrics.get(name, 0) + count
|
|
65
|
+
|
|
66
|
+
def to_dict(self) -> dict[str, Any]:
|
|
67
|
+
return {
|
|
68
|
+
"tool_calls": self.tool_calls,
|
|
69
|
+
"tool_outputs": self.tool_outputs,
|
|
70
|
+
"observed_items": self.observed_items,
|
|
71
|
+
"tool_names": dict(self.tool_names),
|
|
72
|
+
"metrics": dict(self.metrics),
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class AgentRunResult:
|
|
78
|
+
"""Wrapper exposing the backend-native result plus collected stats.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
result: The raw backend-native result object. Inspect this
|
|
82
|
+
directly to access provider-specific data (e.g. token
|
|
83
|
+
usage, raw items, tool-call traces).
|
|
84
|
+
stats: Uniform :class:`AgentRunStats` collected during the
|
|
85
|
+
run, regardless of which backend produced *result*.
|
|
86
|
+
|
|
87
|
+
The ``final_output`` property is the canonical, backend-agnostic
|
|
88
|
+
accessor for the assistant's final text response. Anything else
|
|
89
|
+
must go through ``result.result.<native_attr>`` — the wrapper
|
|
90
|
+
intentionally does **not** proxy unknown attributes to keep the
|
|
91
|
+
public surface explicit and discoverable.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
result: Any
|
|
95
|
+
stats: AgentRunStats
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def final_output(self) -> str:
|
|
99
|
+
"""Return the assistant's final text reply (``""`` if missing)."""
|
|
100
|
+
return getattr(self.result, "final_output", "")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ── Public entry point ─────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async def run_agent_with_progress(
|
|
107
|
+
backend: AgentBackend,
|
|
108
|
+
agent: Any,
|
|
109
|
+
query: str,
|
|
110
|
+
*,
|
|
111
|
+
progress_callback: ProgressCallback | None = None,
|
|
112
|
+
stats_callback: StatsCallback | None = None,
|
|
113
|
+
max_turns: int = DEFAULT_MAX_TURNS,
|
|
114
|
+
session: Any | None = None,
|
|
115
|
+
show_activity_log: bool = True,
|
|
116
|
+
) -> AgentRunResult:
|
|
117
|
+
"""Run *agent* through *backend* and report progress incrementally.
|
|
118
|
+
|
|
119
|
+
When ``show_activity_log`` is True the runner uses
|
|
120
|
+
:meth:`AgentBackend.stream_run`; otherwise it uses
|
|
121
|
+
:meth:`AgentBackend.run`.
|
|
122
|
+
"""
|
|
123
|
+
stats = AgentRunStats()
|
|
124
|
+
await _emit(
|
|
125
|
+
progress_callback,
|
|
126
|
+
"🚀 Starting agent…\n"
|
|
127
|
+
f" [cyan]Backend:[/cyan] {backend.name}\n"
|
|
128
|
+
f" [cyan]Max turns:[/cyan] {max_turns}\n"
|
|
129
|
+
f" [cyan]Activity log:[/cyan] "
|
|
130
|
+
f"{'enabled' if show_activity_log else 'disabled'}\n"
|
|
131
|
+
f" [cyan]Model:[/cyan] {getattr(agent, 'model', 'default')}\n",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if show_activity_log and progress_callback is not None:
|
|
135
|
+
stream = backend.stream_run(
|
|
136
|
+
agent, query, max_turns=max_turns, session=session
|
|
137
|
+
)
|
|
138
|
+
await _emit(
|
|
139
|
+
progress_callback,
|
|
140
|
+
"\n[bold cyan]📋 Agent Activity Log:[/bold cyan]\n",
|
|
141
|
+
)
|
|
142
|
+
try:
|
|
143
|
+
async for event in stream:
|
|
144
|
+
await _handle_event(
|
|
145
|
+
event, stats, progress_callback, stats_callback
|
|
146
|
+
)
|
|
147
|
+
except asyncio.CancelledError:
|
|
148
|
+
await _emit(
|
|
149
|
+
progress_callback,
|
|
150
|
+
"\n[bold red]🛑 Streaming cancelled[/bold red]",
|
|
151
|
+
)
|
|
152
|
+
raise
|
|
153
|
+
result = stream.result
|
|
154
|
+
else:
|
|
155
|
+
result = await backend.run(
|
|
156
|
+
agent, query, max_turns=max_turns, session=session
|
|
157
|
+
)
|
|
158
|
+
_update_stats_from_native(stats, result)
|
|
159
|
+
|
|
160
|
+
await _emit(
|
|
161
|
+
progress_callback,
|
|
162
|
+
"\n\n[bold green]🎉 Final Answer[/bold green]\n",
|
|
163
|
+
)
|
|
164
|
+
return AgentRunResult(result=result, stats=stats)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ── Helpers ────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def _handle_event(
|
|
171
|
+
event: AgentEvent,
|
|
172
|
+
stats: AgentRunStats,
|
|
173
|
+
progress_callback: ProgressCallback | None,
|
|
174
|
+
stats_callback: StatsCallback | None,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Dispatch a single :class:`AgentEvent` to its per-type handler."""
|
|
177
|
+
handler = _EVENT_HANDLERS.get(event.type)
|
|
178
|
+
if handler is None:
|
|
179
|
+
return
|
|
180
|
+
await handler(event, stats, progress_callback, stats_callback)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def _on_agent_active(
|
|
184
|
+
event: AgentEvent,
|
|
185
|
+
_stats: AgentRunStats,
|
|
186
|
+
progress_callback: ProgressCallback | None,
|
|
187
|
+
_stats_callback: StatsCallback | None,
|
|
188
|
+
) -> None:
|
|
189
|
+
await _emit(
|
|
190
|
+
progress_callback,
|
|
191
|
+
f"\n[bold]🔄 Agent active:[/bold] "
|
|
192
|
+
f"[cyan]{event.payload.get('name', 'Agent')}[/cyan]",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def _on_tool_call(
|
|
197
|
+
event: AgentEvent,
|
|
198
|
+
stats: AgentRunStats,
|
|
199
|
+
progress_callback: ProgressCallback | None,
|
|
200
|
+
stats_callback: StatsCallback | None,
|
|
201
|
+
) -> None:
|
|
202
|
+
name = str(event.payload.get("name", "unknown"))
|
|
203
|
+
args = event.payload.get("arguments", "{}")
|
|
204
|
+
stats.increment_tool_call(name)
|
|
205
|
+
await _emit_stats(stats_callback, stats)
|
|
206
|
+
await _emit(
|
|
207
|
+
progress_callback,
|
|
208
|
+
f"\n[bold yellow]🔧 Tool:[/bold yellow] [magenta]{name}[/magenta]",
|
|
209
|
+
)
|
|
210
|
+
formatted = _format_tool_args(args)
|
|
211
|
+
if formatted:
|
|
212
|
+
await _emit(progress_callback, f" {formatted}")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
async def _on_tool_output(
|
|
216
|
+
event: AgentEvent,
|
|
217
|
+
stats: AgentRunStats,
|
|
218
|
+
progress_callback: ProgressCallback | None,
|
|
219
|
+
stats_callback: StatsCallback | None,
|
|
220
|
+
) -> None:
|
|
221
|
+
output = event.payload.get("output")
|
|
222
|
+
stats.increment_tool_output()
|
|
223
|
+
result_count = _count_results(output)
|
|
224
|
+
if result_count:
|
|
225
|
+
stats.observe_items(result_count)
|
|
226
|
+
await _emit_stats(stats_callback, stats)
|
|
227
|
+
await _emit(progress_callback, "\n[bold green]✅ Result[/bold green]")
|
|
228
|
+
for line in _format_tool_output(output):
|
|
229
|
+
await _emit(progress_callback, f" {line}")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
async def _on_message(
|
|
233
|
+
event: AgentEvent,
|
|
234
|
+
_stats: AgentRunStats,
|
|
235
|
+
progress_callback: ProgressCallback | None,
|
|
236
|
+
_stats_callback: StatsCallback | None,
|
|
237
|
+
) -> None:
|
|
238
|
+
text = str(event.payload.get("text", ""))
|
|
239
|
+
if not text:
|
|
240
|
+
return
|
|
241
|
+
if len(text) < 500:
|
|
242
|
+
await _emit(
|
|
243
|
+
progress_callback,
|
|
244
|
+
f"\n[bold blue]💭 Thinking[/bold blue]\n [dim]{text}[/dim]",
|
|
245
|
+
)
|
|
246
|
+
else:
|
|
247
|
+
preview = text[:150].replace("\n", " ")
|
|
248
|
+
await _emit(
|
|
249
|
+
progress_callback,
|
|
250
|
+
f"\n[bold blue]💭 Composing response:[/bold blue] "
|
|
251
|
+
f"[dim]{preview}…[/dim]",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
_EventHandler = Callable[
|
|
256
|
+
[AgentEvent, AgentRunStats, ProgressCallback | None, StatsCallback | None],
|
|
257
|
+
Awaitable[None],
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
# Per-event-type dispatch table for ``_handle_event``. Adding a new
|
|
261
|
+
# event type means appending one entry here rather than threading
|
|
262
|
+
# another ``elif`` branch through a growing if-cascade.
|
|
263
|
+
_EVENT_HANDLERS: dict[str, _EventHandler] = {
|
|
264
|
+
"agent_active": _on_agent_active,
|
|
265
|
+
"tool_call": _on_tool_call,
|
|
266
|
+
"tool_output": _on_tool_output,
|
|
267
|
+
"message": _on_message,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
async def _emit(cb: ProgressCallback | None, message: str) -> None:
|
|
272
|
+
if cb is None:
|
|
273
|
+
return
|
|
274
|
+
result = cb(message)
|
|
275
|
+
if asyncio.iscoroutine(result):
|
|
276
|
+
await result
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
async def _emit_stats(
|
|
280
|
+
cb: StatsCallback | None, stats: AgentRunStats
|
|
281
|
+
) -> None:
|
|
282
|
+
if cb is None:
|
|
283
|
+
return
|
|
284
|
+
result = cb(stats)
|
|
285
|
+
if asyncio.iscoroutine(result):
|
|
286
|
+
await result
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _update_stats_from_native(stats: AgentRunStats, result: Any) -> None:
|
|
290
|
+
"""Populate stats from native-result items when streaming was disabled."""
|
|
291
|
+
new_items = getattr(result, "new_items", None) or []
|
|
292
|
+
for item in new_items:
|
|
293
|
+
item_type = getattr(item, "type", None)
|
|
294
|
+
if item_type == "tool_call_item":
|
|
295
|
+
raw_item = getattr(item, "raw_item", None) or item
|
|
296
|
+
stats.increment_tool_call(getattr(raw_item, "name", "unknown"))
|
|
297
|
+
elif item_type == "tool_call_output_item":
|
|
298
|
+
stats.increment_tool_output()
|
|
299
|
+
output = getattr(item, "output", None)
|
|
300
|
+
stats.observe_items(_count_results(output))
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _count_results(output: Any) -> int:
|
|
304
|
+
"""Best-effort count of result-like items in a tool output."""
|
|
305
|
+
if output is None:
|
|
306
|
+
return 0
|
|
307
|
+
results = getattr(output, "results", None)
|
|
308
|
+
if isinstance(results, list):
|
|
309
|
+
return len(results)
|
|
310
|
+
return 0
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _format_tool_args(args: Any) -> str:
|
|
314
|
+
"""Format tool args (JSON string or mapping) into a Rich-styled line."""
|
|
315
|
+
if isinstance(args, str):
|
|
316
|
+
if args in ("", "{}", "null"):
|
|
317
|
+
return ""
|
|
318
|
+
try:
|
|
319
|
+
data = json.loads(args)
|
|
320
|
+
except json.JSONDecodeError:
|
|
321
|
+
return f"[dim]{args}[/dim]"
|
|
322
|
+
elif isinstance(args, dict):
|
|
323
|
+
data = args
|
|
324
|
+
else:
|
|
325
|
+
return ""
|
|
326
|
+
parts: list[str] = []
|
|
327
|
+
for key, value in data.items():
|
|
328
|
+
formatted = (
|
|
329
|
+
f'"{value}"' if isinstance(value, str) else json.dumps(value)
|
|
330
|
+
)
|
|
331
|
+
parts.append(f"[blue]{key}[/blue]=[green]{formatted}[/green]")
|
|
332
|
+
return ", ".join(parts)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _format_tool_output(output: Any) -> list[str]:
|
|
336
|
+
if output is None:
|
|
337
|
+
return []
|
|
338
|
+
if isinstance(output, str):
|
|
339
|
+
if len(output) > 200:
|
|
340
|
+
return [
|
|
341
|
+
f"[cyan]Text:[/cyan] {len(output.split())} words",
|
|
342
|
+
f"[dim]Preview:[/dim] {output[:150]}…",
|
|
343
|
+
]
|
|
344
|
+
return [f"[green]{output}[/green]"]
|
|
345
|
+
text = str(output)
|
|
346
|
+
if len(text) > 300:
|
|
347
|
+
return [f"[dim]{text[:300]}…[/dim]"]
|
|
348
|
+
return [f"[dim]{text}[/dim]"]
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
__all__ = [
|
|
352
|
+
"AgentRunResult",
|
|
353
|
+
"AgentRunStats",
|
|
354
|
+
"DEFAULT_MAX_TURNS",
|
|
355
|
+
"ProgressCallback",
|
|
356
|
+
"StatsCallback",
|
|
357
|
+
"run_agent_with_progress",
|
|
358
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Generic tool-creation utility shared by all agent backends."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ellements.core import SimpleTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_tool_from_method(
|
|
13
|
+
method: Callable[..., Any],
|
|
14
|
+
*,
|
|
15
|
+
name: str | None = None,
|
|
16
|
+
description: str | None = None,
|
|
17
|
+
) -> SimpleTool:
|
|
18
|
+
"""Wrap a method as a canonical :class:`~ellements.core.SimpleTool`.
|
|
19
|
+
|
|
20
|
+
The result is backend-agnostic; backends adapt it to their native
|
|
21
|
+
tool type at agent-construction time.
|
|
22
|
+
"""
|
|
23
|
+
tool_name = name or method.__name__
|
|
24
|
+
tool_description = description or (
|
|
25
|
+
inspect.getdoc(method) or f"Execute {tool_name}"
|
|
26
|
+
)
|
|
27
|
+
return SimpleTool(method, name=tool_name, description=tool_description)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ["create_tool_from_method"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Benchmarking module — evaluate prompting strategies against standard LLM
|
|
2
|
+
benchmarks via `lm-evaluation-harness <https://github.com/EleutherAI/lm-evaluation-harness>`_.
|
|
3
|
+
|
|
4
|
+
Quick start::
|
|
5
|
+
|
|
6
|
+
from ellements.benchmarking import run_benchmark, compare_benchmarks
|
|
7
|
+
from ellements.execution import (
|
|
8
|
+
SelfConsistencyConfig,
|
|
9
|
+
SelfConsistencyStrategy,
|
|
10
|
+
SingleCallStrategy,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Benchmark a model on GSM8K
|
|
14
|
+
results = run_benchmark(model="openai/gpt-4o", tasks=["gsm8k"])
|
|
15
|
+
|
|
16
|
+
# Benchmark with a typed execution strategy config
|
|
17
|
+
results = run_benchmark(
|
|
18
|
+
model="openai/gpt-4o",
|
|
19
|
+
strategy=SelfConsistencyStrategy(),
|
|
20
|
+
strategy_config=SelfConsistencyConfig(samples=5),
|
|
21
|
+
tasks=["gsm8k"],
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Compare strategies
|
|
25
|
+
comparison = compare_benchmarks(
|
|
26
|
+
model="openai/gpt-4o",
|
|
27
|
+
strategies={
|
|
28
|
+
"baseline": None,
|
|
29
|
+
"single-call": SingleCallStrategy(),
|
|
30
|
+
},
|
|
31
|
+
tasks=["gsm8k"],
|
|
32
|
+
)
|
|
33
|
+
comparison.print_table()
|
|
34
|
+
|
|
35
|
+
Requires: ``pip install ellements[benchmarking]``
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from .harness import (
|
|
39
|
+
BenchmarkModel,
|
|
40
|
+
OnBenchmarkProgress,
|
|
41
|
+
compare_benchmarks,
|
|
42
|
+
run_benchmark,
|
|
43
|
+
)
|
|
44
|
+
from .results import BenchmarkComparison
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"BenchmarkComparison",
|
|
48
|
+
"BenchmarkModel",
|
|
49
|
+
"OnBenchmarkProgress",
|
|
50
|
+
"compare_benchmarks",
|
|
51
|
+
"run_benchmark",
|
|
52
|
+
]
|