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,541 @@
|
|
|
1
|
+
"""Tree-of-thought strategy — three search modes over one strategy.
|
|
2
|
+
|
|
3
|
+
Implements three variants behind a single :class:`TreeOfThoughtStrategy`
|
|
4
|
+
selected via the ``mode`` config field:
|
|
5
|
+
|
|
6
|
+
- ``"beam"`` (default): canonical BFS with step-level beam search
|
|
7
|
+
(Yao et al., 2023). Required prompts: ``thought_step``,
|
|
8
|
+
``evaluate_step``, ``synthesize``.
|
|
9
|
+
- ``"dfs"``: depth-first with backtracking + early exit on max-scoring
|
|
10
|
+
leaf. Same required prompts as ``"beam"``.
|
|
11
|
+
- ``"simple"``: simplified flat search — generate N complete solutions,
|
|
12
|
+
evaluate them together, synthesize the best. Required prompts:
|
|
13
|
+
``generate``, ``evaluate``, ``synthesize``.
|
|
14
|
+
|
|
15
|
+
All evaluations use a structured :class:`Evaluation` with a numeric
|
|
16
|
+
``score`` (0.0 – 1.0) and short ``reasoning`` — no keyword scanning.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import logging
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from ellements.core import LLMClient
|
|
27
|
+
from pydantic import BaseModel, Field
|
|
28
|
+
|
|
29
|
+
from .config import StrategyConfigInput, TreeOfThoughtConfig
|
|
30
|
+
from .strategies import BaseStrategy, StepRecord, StrategyResult
|
|
31
|
+
|
|
32
|
+
_logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Evaluation(BaseModel):
|
|
36
|
+
"""Structured evaluation produced by the ``evaluate`` stage."""
|
|
37
|
+
|
|
38
|
+
score: float = Field(
|
|
39
|
+
ge=0.0,
|
|
40
|
+
le=1.0,
|
|
41
|
+
description="Quality score in [0.0, 1.0]; higher is better",
|
|
42
|
+
)
|
|
43
|
+
reasoning: str = Field(
|
|
44
|
+
default="",
|
|
45
|
+
description="Short justification for the score",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TreeOfThoughtStrategy(BaseStrategy):
|
|
50
|
+
"""Tree-of-Thought reasoning with selectable search mode."""
|
|
51
|
+
|
|
52
|
+
async def execute(
|
|
53
|
+
self,
|
|
54
|
+
prompts: Mapping[str, str],
|
|
55
|
+
client: LLMClient,
|
|
56
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None = None,
|
|
57
|
+
config: StrategyConfigInput = None,
|
|
58
|
+
) -> StrategyResult:
|
|
59
|
+
typed_config = self._normalize_config(config, TreeOfThoughtConfig)
|
|
60
|
+
if typed_config.mode == "simple":
|
|
61
|
+
return await self._execute_simple(prompts, client, typed_config, tools)
|
|
62
|
+
if typed_config.mode == "dfs":
|
|
63
|
+
return await self._execute_dfs(prompts, client, typed_config, tools)
|
|
64
|
+
return await self._execute_beam(prompts, client, typed_config, tools)
|
|
65
|
+
|
|
66
|
+
# ── beam (BFS) ──────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
async def _execute_beam(
|
|
69
|
+
self,
|
|
70
|
+
prompts: Mapping[str, str],
|
|
71
|
+
client: LLMClient,
|
|
72
|
+
config: TreeOfThoughtConfig,
|
|
73
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None,
|
|
74
|
+
) -> StrategyResult:
|
|
75
|
+
system = prompts.get("system")
|
|
76
|
+
sem = asyncio.Semaphore(config.max_concurrent)
|
|
77
|
+
steps: list[StepRecord] = []
|
|
78
|
+
|
|
79
|
+
thought_template = self._get_prompt(prompts, "thought_step")
|
|
80
|
+
eval_template = self._get_prompt(prompts, "evaluate_step")
|
|
81
|
+
|
|
82
|
+
thought_temp = self._stage_temperature(
|
|
83
|
+
config, "thought", default=config.thought_temperature
|
|
84
|
+
)
|
|
85
|
+
evaluate_temp = self._stage_temperature(
|
|
86
|
+
config, "evaluate", default=config.evaluate_temperature
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
states: list[str] = [""]
|
|
90
|
+
|
|
91
|
+
for depth in range(config.max_depth):
|
|
92
|
+
candidates: list[tuple[str, float]] = []
|
|
93
|
+
|
|
94
|
+
for state_idx, state in enumerate(states):
|
|
95
|
+
thoughts_prompt = self._render_template(
|
|
96
|
+
thought_template, {"state": state}
|
|
97
|
+
)
|
|
98
|
+
thoughts = await asyncio.gather(
|
|
99
|
+
*(
|
|
100
|
+
self._guarded_complete(
|
|
101
|
+
sem,
|
|
102
|
+
client,
|
|
103
|
+
thoughts_prompt,
|
|
104
|
+
config=config,
|
|
105
|
+
temperature=thought_temp,
|
|
106
|
+
system=system,
|
|
107
|
+
tools=tools,
|
|
108
|
+
)
|
|
109
|
+
for _ in range(config.branching_factor)
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
for t_idx, thought in enumerate(thoughts):
|
|
114
|
+
new_state = self._record_thought(
|
|
115
|
+
thought=thought,
|
|
116
|
+
state=state,
|
|
117
|
+
depth=depth,
|
|
118
|
+
step_name=f"thought_d{depth}_s{state_idx}_b{t_idx}",
|
|
119
|
+
step_metadata={
|
|
120
|
+
"depth": depth,
|
|
121
|
+
"state_idx": state_idx,
|
|
122
|
+
"branch": t_idx,
|
|
123
|
+
},
|
|
124
|
+
steps=steps,
|
|
125
|
+
config=config,
|
|
126
|
+
)
|
|
127
|
+
evaluation = await self._evaluate_state(
|
|
128
|
+
eval_template=eval_template,
|
|
129
|
+
state=new_state,
|
|
130
|
+
sem=sem,
|
|
131
|
+
client=client,
|
|
132
|
+
config=config,
|
|
133
|
+
evaluate_temp=evaluate_temp,
|
|
134
|
+
system=system,
|
|
135
|
+
steps=steps,
|
|
136
|
+
step_name=f"eval_d{depth}_s{state_idx}_b{t_idx}",
|
|
137
|
+
step_metadata={"depth": depth},
|
|
138
|
+
)
|
|
139
|
+
if evaluation.score > 0:
|
|
140
|
+
candidates.append((new_state, evaluation.score))
|
|
141
|
+
|
|
142
|
+
if not candidates:
|
|
143
|
+
_logger.warning(
|
|
144
|
+
"No viable candidates at depth %d, stopping early", depth
|
|
145
|
+
)
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
candidates.sort(key=lambda item: item[1], reverse=True)
|
|
149
|
+
states = [state for state, _ in candidates[: config.beam_width]]
|
|
150
|
+
|
|
151
|
+
best_path = states[0] if states else ""
|
|
152
|
+
synth_steps = await self._synthesize(
|
|
153
|
+
client,
|
|
154
|
+
prompts,
|
|
155
|
+
best_path,
|
|
156
|
+
config=config,
|
|
157
|
+
system=system,
|
|
158
|
+
tools=tools,
|
|
159
|
+
)
|
|
160
|
+
steps.extend(synth_steps)
|
|
161
|
+
for step in synth_steps:
|
|
162
|
+
self._notify_step(config, step)
|
|
163
|
+
|
|
164
|
+
return StrategyResult(
|
|
165
|
+
output=synth_steps[-1].response if synth_steps else "",
|
|
166
|
+
steps=steps,
|
|
167
|
+
metadata={
|
|
168
|
+
"mode": "beam",
|
|
169
|
+
"max_depth": config.max_depth,
|
|
170
|
+
"beam_width": config.beam_width,
|
|
171
|
+
"branching_factor": config.branching_factor,
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# ── dfs ─────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
async def _execute_dfs(
|
|
178
|
+
self,
|
|
179
|
+
prompts: Mapping[str, str],
|
|
180
|
+
client: LLMClient,
|
|
181
|
+
config: TreeOfThoughtConfig,
|
|
182
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None,
|
|
183
|
+
) -> StrategyResult:
|
|
184
|
+
system = prompts.get("system")
|
|
185
|
+
sem = asyncio.Semaphore(config.max_concurrent)
|
|
186
|
+
steps: list[StepRecord] = []
|
|
187
|
+
|
|
188
|
+
thought_template = self._get_prompt(prompts, "thought_step")
|
|
189
|
+
eval_template = self._get_prompt(prompts, "evaluate_step")
|
|
190
|
+
|
|
191
|
+
thought_temp = self._stage_temperature(
|
|
192
|
+
config, "thought", default=config.thought_temperature
|
|
193
|
+
)
|
|
194
|
+
evaluate_temp = self._stage_temperature(
|
|
195
|
+
config, "evaluate", default=config.evaluate_temperature
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
best_state: str | None = None
|
|
199
|
+
best_score: float = -1.0
|
|
200
|
+
|
|
201
|
+
async def dfs(state: str, depth: int) -> None:
|
|
202
|
+
nonlocal best_state, best_score
|
|
203
|
+
|
|
204
|
+
if depth >= config.max_depth:
|
|
205
|
+
evaluation = await self._evaluate_state(
|
|
206
|
+
eval_template=eval_template,
|
|
207
|
+
state=state,
|
|
208
|
+
sem=sem,
|
|
209
|
+
client=client,
|
|
210
|
+
config=config,
|
|
211
|
+
evaluate_temp=evaluate_temp,
|
|
212
|
+
system=system,
|
|
213
|
+
steps=steps,
|
|
214
|
+
step_name=f"eval_leaf_d{depth}",
|
|
215
|
+
step_metadata={"depth": depth, "leaf": True},
|
|
216
|
+
)
|
|
217
|
+
if evaluation.score > best_score:
|
|
218
|
+
best_score = evaluation.score
|
|
219
|
+
best_state = state
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
thoughts_prompt = self._render_template(
|
|
223
|
+
thought_template, {"state": state}
|
|
224
|
+
)
|
|
225
|
+
thoughts = await asyncio.gather(
|
|
226
|
+
*(
|
|
227
|
+
self._guarded_complete(
|
|
228
|
+
sem,
|
|
229
|
+
client,
|
|
230
|
+
thoughts_prompt,
|
|
231
|
+
config=config,
|
|
232
|
+
temperature=thought_temp,
|
|
233
|
+
system=system,
|
|
234
|
+
tools=tools,
|
|
235
|
+
)
|
|
236
|
+
for _ in range(config.branching_factor)
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
for t_idx, thought in enumerate(thoughts):
|
|
241
|
+
new_state = self._record_thought(
|
|
242
|
+
thought=thought,
|
|
243
|
+
state=state,
|
|
244
|
+
depth=depth,
|
|
245
|
+
step_name=f"thought_d{depth}_b{t_idx}",
|
|
246
|
+
step_metadata={"depth": depth, "branch": t_idx},
|
|
247
|
+
steps=steps,
|
|
248
|
+
config=config,
|
|
249
|
+
)
|
|
250
|
+
evaluation = await self._evaluate_state(
|
|
251
|
+
eval_template=eval_template,
|
|
252
|
+
state=new_state,
|
|
253
|
+
sem=sem,
|
|
254
|
+
client=client,
|
|
255
|
+
config=config,
|
|
256
|
+
evaluate_temp=evaluate_temp,
|
|
257
|
+
system=system,
|
|
258
|
+
steps=steps,
|
|
259
|
+
step_name=f"eval_d{depth}_b{t_idx}",
|
|
260
|
+
step_metadata={"depth": depth},
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if evaluation.score <= 0:
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
await dfs(new_state, depth + 1)
|
|
267
|
+
if best_score >= 1.0:
|
|
268
|
+
return
|
|
269
|
+
|
|
270
|
+
await dfs("", 0)
|
|
271
|
+
|
|
272
|
+
synth_steps = await self._synthesize(
|
|
273
|
+
client,
|
|
274
|
+
prompts,
|
|
275
|
+
best_state or "",
|
|
276
|
+
config=config,
|
|
277
|
+
system=system,
|
|
278
|
+
tools=tools,
|
|
279
|
+
)
|
|
280
|
+
steps.extend(synth_steps)
|
|
281
|
+
for step in synth_steps:
|
|
282
|
+
self._notify_step(config, step)
|
|
283
|
+
|
|
284
|
+
return StrategyResult(
|
|
285
|
+
output=synth_steps[-1].response if synth_steps else "",
|
|
286
|
+
steps=steps,
|
|
287
|
+
metadata={
|
|
288
|
+
"mode": "dfs",
|
|
289
|
+
"max_depth": config.max_depth,
|
|
290
|
+
"branching_factor": config.branching_factor,
|
|
291
|
+
"best_score": best_score,
|
|
292
|
+
},
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# ── simple (flat) ───────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
async def _execute_simple(
|
|
298
|
+
self,
|
|
299
|
+
prompts: Mapping[str, str],
|
|
300
|
+
client: LLMClient,
|
|
301
|
+
config: TreeOfThoughtConfig,
|
|
302
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None,
|
|
303
|
+
) -> StrategyResult:
|
|
304
|
+
system = prompts.get("system")
|
|
305
|
+
sem = asyncio.Semaphore(config.max_concurrent)
|
|
306
|
+
steps: list[StepRecord] = []
|
|
307
|
+
|
|
308
|
+
generate_template = self._get_prompt(prompts, "generate")
|
|
309
|
+
evaluate_template = self._get_prompt(prompts, "evaluate")
|
|
310
|
+
synthesize_template = self._get_prompt(prompts, "synthesize")
|
|
311
|
+
|
|
312
|
+
thought_temp = self._stage_temperature(
|
|
313
|
+
config, "generate", default=config.thought_temperature
|
|
314
|
+
)
|
|
315
|
+
evaluate_temp = self._stage_temperature(
|
|
316
|
+
config, "evaluate", default=config.evaluate_temperature
|
|
317
|
+
)
|
|
318
|
+
synthesize_temp = self._stage_temperature(
|
|
319
|
+
config, "synthesize", default=config.synthesize_temperature
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
async def _gen_one() -> str:
|
|
323
|
+
async with sem:
|
|
324
|
+
return await self._complete(
|
|
325
|
+
client,
|
|
326
|
+
generate_template,
|
|
327
|
+
config=config,
|
|
328
|
+
temperature=thought_temp,
|
|
329
|
+
system=system,
|
|
330
|
+
tools=tools,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
generated = await asyncio.gather(
|
|
334
|
+
*(_gen_one() for _ in range(config.branching_factor))
|
|
335
|
+
)
|
|
336
|
+
for i, resp in enumerate(generated):
|
|
337
|
+
steps.append(
|
|
338
|
+
StepRecord(
|
|
339
|
+
name=f"generate_{i}",
|
|
340
|
+
prompt_key="generate",
|
|
341
|
+
response=resp,
|
|
342
|
+
metadata={"branch": i},
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
self._notify_step(config, steps[-1])
|
|
346
|
+
|
|
347
|
+
candidates_text = "\n\n".join(
|
|
348
|
+
f"--- Path {i + 1} ---\n{resp}" for i, resp in enumerate(generated)
|
|
349
|
+
)
|
|
350
|
+
eval_prompt = self._render_template(
|
|
351
|
+
evaluate_template, {"candidates": candidates_text}
|
|
352
|
+
)
|
|
353
|
+
evaluation = await self._guarded_evaluate(
|
|
354
|
+
sem,
|
|
355
|
+
client,
|
|
356
|
+
eval_prompt,
|
|
357
|
+
config=config,
|
|
358
|
+
temperature=evaluate_temp,
|
|
359
|
+
system=system,
|
|
360
|
+
)
|
|
361
|
+
steps.append(
|
|
362
|
+
StepRecord(
|
|
363
|
+
name="evaluate",
|
|
364
|
+
prompt_key="evaluate",
|
|
365
|
+
response=evaluation.model_dump_json(),
|
|
366
|
+
metadata={"score": evaluation.score},
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
self._notify_step(config, steps[-1])
|
|
370
|
+
|
|
371
|
+
synth_prompt = self._render_template(
|
|
372
|
+
synthesize_template,
|
|
373
|
+
{
|
|
374
|
+
"candidates": candidates_text,
|
|
375
|
+
"evaluation": evaluation.reasoning,
|
|
376
|
+
"best_approach": evaluation.reasoning,
|
|
377
|
+
},
|
|
378
|
+
)
|
|
379
|
+
synthesis = await self._complete(
|
|
380
|
+
client,
|
|
381
|
+
synth_prompt,
|
|
382
|
+
config=config,
|
|
383
|
+
temperature=synthesize_temp,
|
|
384
|
+
system=system,
|
|
385
|
+
tools=tools,
|
|
386
|
+
)
|
|
387
|
+
steps.append(
|
|
388
|
+
StepRecord(
|
|
389
|
+
name="synthesize", prompt_key="synthesize", response=synthesis
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
self._notify_step(config, steps[-1])
|
|
393
|
+
|
|
394
|
+
return StrategyResult(
|
|
395
|
+
output=synthesis,
|
|
396
|
+
steps=steps,
|
|
397
|
+
metadata={
|
|
398
|
+
"mode": "simple",
|
|
399
|
+
"branching_factor": config.branching_factor,
|
|
400
|
+
},
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# ── shared helpers ──────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
def _record_thought(
|
|
406
|
+
self,
|
|
407
|
+
*,
|
|
408
|
+
thought: str,
|
|
409
|
+
state: str,
|
|
410
|
+
depth: int,
|
|
411
|
+
step_name: str,
|
|
412
|
+
step_metadata: dict[str, Any],
|
|
413
|
+
steps: list[StepRecord],
|
|
414
|
+
config: TreeOfThoughtConfig,
|
|
415
|
+
) -> str:
|
|
416
|
+
"""Append a ``thought_step`` record and return the extended state.
|
|
417
|
+
|
|
418
|
+
Shared by ``_execute_beam`` and ``_execute_dfs`` so they don't
|
|
419
|
+
each open-code the StepRecord/notify dance after every thought.
|
|
420
|
+
"""
|
|
421
|
+
new_state = f"{state}\n\nStep {depth + 1}: {thought}".strip()
|
|
422
|
+
steps.append(
|
|
423
|
+
StepRecord(
|
|
424
|
+
name=step_name,
|
|
425
|
+
prompt_key="thought_step",
|
|
426
|
+
response=thought,
|
|
427
|
+
metadata=step_metadata,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
self._notify_step(config, steps[-1])
|
|
431
|
+
return new_state
|
|
432
|
+
|
|
433
|
+
async def _evaluate_state(
|
|
434
|
+
self,
|
|
435
|
+
*,
|
|
436
|
+
eval_template: str,
|
|
437
|
+
state: str,
|
|
438
|
+
sem: asyncio.Semaphore,
|
|
439
|
+
client: LLMClient,
|
|
440
|
+
config: TreeOfThoughtConfig,
|
|
441
|
+
evaluate_temp: float,
|
|
442
|
+
system: str | None,
|
|
443
|
+
steps: list[StepRecord],
|
|
444
|
+
step_name: str,
|
|
445
|
+
step_metadata: dict[str, Any],
|
|
446
|
+
) -> Evaluation:
|
|
447
|
+
"""Score *state* and append the matching ``evaluate_step`` record."""
|
|
448
|
+
eval_prompt = self._render_template(eval_template, {"state": state})
|
|
449
|
+
evaluation = await self._guarded_evaluate(
|
|
450
|
+
sem,
|
|
451
|
+
client,
|
|
452
|
+
eval_prompt,
|
|
453
|
+
config=config,
|
|
454
|
+
temperature=evaluate_temp,
|
|
455
|
+
system=system,
|
|
456
|
+
)
|
|
457
|
+
steps.append(
|
|
458
|
+
StepRecord(
|
|
459
|
+
name=step_name,
|
|
460
|
+
prompt_key="evaluate_step",
|
|
461
|
+
response=evaluation.model_dump_json(),
|
|
462
|
+
metadata={**step_metadata, "score": evaluation.score},
|
|
463
|
+
)
|
|
464
|
+
)
|
|
465
|
+
self._notify_step(config, steps[-1])
|
|
466
|
+
return evaluation
|
|
467
|
+
|
|
468
|
+
async def _synthesize(
|
|
469
|
+
self,
|
|
470
|
+
client: LLMClient,
|
|
471
|
+
prompts: Mapping[str, str],
|
|
472
|
+
best_path: str,
|
|
473
|
+
*,
|
|
474
|
+
config: TreeOfThoughtConfig,
|
|
475
|
+
system: str | None,
|
|
476
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None,
|
|
477
|
+
) -> list[StepRecord]:
|
|
478
|
+
template = self._get_prompt(prompts, "synthesize")
|
|
479
|
+
synth_prompt = self._render_template(template, {"best_path": best_path})
|
|
480
|
+
synthesize_temp = self._stage_temperature(
|
|
481
|
+
config, "synthesize", default=config.synthesize_temperature
|
|
482
|
+
)
|
|
483
|
+
response = await self._complete(
|
|
484
|
+
client,
|
|
485
|
+
synth_prompt,
|
|
486
|
+
config=config,
|
|
487
|
+
temperature=synthesize_temp,
|
|
488
|
+
system=system,
|
|
489
|
+
tools=tools,
|
|
490
|
+
)
|
|
491
|
+
return [
|
|
492
|
+
StepRecord(
|
|
493
|
+
name="synthesize", prompt_key="synthesize", response=response
|
|
494
|
+
)
|
|
495
|
+
]
|
|
496
|
+
|
|
497
|
+
@classmethod
|
|
498
|
+
async def _guarded_complete(
|
|
499
|
+
cls,
|
|
500
|
+
sem: asyncio.Semaphore,
|
|
501
|
+
client: LLMClient,
|
|
502
|
+
prompt: str,
|
|
503
|
+
*,
|
|
504
|
+
config: TreeOfThoughtConfig,
|
|
505
|
+
temperature: float,
|
|
506
|
+
system: str | None,
|
|
507
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None,
|
|
508
|
+
) -> str:
|
|
509
|
+
async with sem:
|
|
510
|
+
return await cls._complete(
|
|
511
|
+
client,
|
|
512
|
+
prompt,
|
|
513
|
+
config=config,
|
|
514
|
+
temperature=temperature,
|
|
515
|
+
system=system,
|
|
516
|
+
tools=tools,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
@classmethod
|
|
520
|
+
async def _guarded_evaluate(
|
|
521
|
+
cls,
|
|
522
|
+
sem: asyncio.Semaphore,
|
|
523
|
+
client: LLMClient,
|
|
524
|
+
prompt: str,
|
|
525
|
+
*,
|
|
526
|
+
config: TreeOfThoughtConfig,
|
|
527
|
+
temperature: float,
|
|
528
|
+
system: str | None,
|
|
529
|
+
) -> Evaluation:
|
|
530
|
+
async with sem:
|
|
531
|
+
return await cls._complete_structured(
|
|
532
|
+
client,
|
|
533
|
+
prompt,
|
|
534
|
+
response_model=Evaluation,
|
|
535
|
+
config=config,
|
|
536
|
+
temperature=temperature,
|
|
537
|
+
system=system,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
__all__ = ["Evaluation", "TreeOfThoughtStrategy"]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Agentic finite-state linguistic machines for ellements."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import det, nl
|
|
6
|
+
from .context import FSLMContext
|
|
7
|
+
from .definition import MachineDefinition, RuntimeBindings
|
|
8
|
+
from .dsl import MachineBuilder, StateBuilder, TransitionBuilder, machine
|
|
9
|
+
from .errors import (
|
|
10
|
+
BudgetExhaustedError,
|
|
11
|
+
FSLMError,
|
|
12
|
+
InvalidTransitionError,
|
|
13
|
+
InvariantViolationError,
|
|
14
|
+
SafetyBlockedError,
|
|
15
|
+
SpecValidationError,
|
|
16
|
+
)
|
|
17
|
+
from .evaluators import LLMDecisionEvaluator
|
|
18
|
+
from .kernel import ActionExecutor, FSLMKernel, GuardEvaluator, InvariantChecker
|
|
19
|
+
from .loading import load_machine_definition, load_machine_spec
|
|
20
|
+
from .models import (
|
|
21
|
+
ActionResult,
|
|
22
|
+
ActionSpec,
|
|
23
|
+
AffordanceSpec,
|
|
24
|
+
BudgetSpec,
|
|
25
|
+
ConfidencePolicy,
|
|
26
|
+
DecisionResult,
|
|
27
|
+
EffectSpec,
|
|
28
|
+
EventPatternSpec,
|
|
29
|
+
ExecutionPolicy,
|
|
30
|
+
FSLMEvent,
|
|
31
|
+
GuardSpec,
|
|
32
|
+
InvariantSpec,
|
|
33
|
+
MachineSnapshot,
|
|
34
|
+
MachineSpec,
|
|
35
|
+
OutputRecord,
|
|
36
|
+
OutputSpec,
|
|
37
|
+
StateSpec,
|
|
38
|
+
StepResult,
|
|
39
|
+
ToolRef,
|
|
40
|
+
TransitionSpec,
|
|
41
|
+
)
|
|
42
|
+
from .observers import (
|
|
43
|
+
FSLMEventRecord,
|
|
44
|
+
FSLMObserver,
|
|
45
|
+
JsonlFSLMObserver,
|
|
46
|
+
RecordingFSLMObserver,
|
|
47
|
+
RichConsoleFSLMObserver,
|
|
48
|
+
)
|
|
49
|
+
from .persistence import InMemoryFSLMStore, LocalFSLMStore
|
|
50
|
+
from .rendering import make_console, render_snapshot, render_step, render_validation
|
|
51
|
+
from .visualization import to_mermaid
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"ActionExecutor",
|
|
55
|
+
"ActionResult",
|
|
56
|
+
"ActionSpec",
|
|
57
|
+
"AffordanceSpec",
|
|
58
|
+
"BudgetExhaustedError",
|
|
59
|
+
"BudgetSpec",
|
|
60
|
+
"ConfidencePolicy",
|
|
61
|
+
"DecisionResult",
|
|
62
|
+
"EventPatternSpec",
|
|
63
|
+
"ExecutionPolicy",
|
|
64
|
+
"EffectSpec",
|
|
65
|
+
"FSLMContext",
|
|
66
|
+
"FSLMError",
|
|
67
|
+
"FSLMEvent",
|
|
68
|
+
"FSLMEventRecord",
|
|
69
|
+
"FSLMKernel",
|
|
70
|
+
"FSLMObserver",
|
|
71
|
+
"GuardEvaluator",
|
|
72
|
+
"GuardSpec",
|
|
73
|
+
"InMemoryFSLMStore",
|
|
74
|
+
"InvalidTransitionError",
|
|
75
|
+
"InvariantChecker",
|
|
76
|
+
"InvariantSpec",
|
|
77
|
+
"InvariantViolationError",
|
|
78
|
+
"JsonlFSLMObserver",
|
|
79
|
+
"LocalFSLMStore",
|
|
80
|
+
"LLMDecisionEvaluator",
|
|
81
|
+
"MachineBuilder",
|
|
82
|
+
"MachineDefinition",
|
|
83
|
+
"MachineSnapshot",
|
|
84
|
+
"MachineSpec",
|
|
85
|
+
"OutputRecord",
|
|
86
|
+
"OutputSpec",
|
|
87
|
+
"RecordingFSLMObserver",
|
|
88
|
+
"RichConsoleFSLMObserver",
|
|
89
|
+
"RuntimeBindings",
|
|
90
|
+
"SafetyBlockedError",
|
|
91
|
+
"SpecValidationError",
|
|
92
|
+
"StateBuilder",
|
|
93
|
+
"StateSpec",
|
|
94
|
+
"StepResult",
|
|
95
|
+
"ToolRef",
|
|
96
|
+
"TransitionBuilder",
|
|
97
|
+
"TransitionSpec",
|
|
98
|
+
"det",
|
|
99
|
+
"load_machine_definition",
|
|
100
|
+
"machine",
|
|
101
|
+
"make_console",
|
|
102
|
+
"load_machine_spec",
|
|
103
|
+
"nl",
|
|
104
|
+
"render_snapshot",
|
|
105
|
+
"render_step",
|
|
106
|
+
"render_validation",
|
|
107
|
+
"to_mermaid",
|
|
108
|
+
]
|