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,48 @@
|
|
|
1
|
+
"""Single-call strategy — the simplest execution pattern.
|
|
2
|
+
|
|
3
|
+
Makes one LLM call (optionally with tools) using the ``default`` prompt
|
|
4
|
+
and returns the result.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ellements.core import LLMClient
|
|
13
|
+
|
|
14
|
+
from .config import SingleCallConfig, StrategyConfigInput
|
|
15
|
+
from .strategies import BaseStrategy, StepRecord, StrategyResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SingleCallStrategy(BaseStrategy):
|
|
19
|
+
"""Execute a single LLM call with the ``default`` prompt."""
|
|
20
|
+
|
|
21
|
+
REQUIRED_PROMPTS = ("default",)
|
|
22
|
+
|
|
23
|
+
async def execute(
|
|
24
|
+
self,
|
|
25
|
+
prompts: Mapping[str, str],
|
|
26
|
+
client: LLMClient,
|
|
27
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None = None,
|
|
28
|
+
config: StrategyConfigInput = None,
|
|
29
|
+
) -> StrategyResult:
|
|
30
|
+
typed_config = self._normalize_config(config, SingleCallConfig)
|
|
31
|
+
prompt = self._get_prompt(prompts, "default")
|
|
32
|
+
|
|
33
|
+
temperature = self._stage_temperature(typed_config, "single", default=0.7)
|
|
34
|
+
if typed_config.temperature is not None:
|
|
35
|
+
temperature = typed_config.temperature
|
|
36
|
+
|
|
37
|
+
response = await self._complete(
|
|
38
|
+
client,
|
|
39
|
+
prompt,
|
|
40
|
+
config=typed_config,
|
|
41
|
+
temperature=temperature,
|
|
42
|
+
system=prompts.get("system"),
|
|
43
|
+
tools=tools,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
step = StepRecord(name="call", prompt_key="default", response=response)
|
|
47
|
+
self._notify_step(typed_config, step)
|
|
48
|
+
return StrategyResult(output=response, steps=[step])
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Strategy protocol and shared base utilities.
|
|
2
|
+
|
|
3
|
+
Strategies orchestrate multiple LLM calls in patterns like single-call,
|
|
4
|
+
self-consistency, tree-of-thought, reflection, and collaborative editing.
|
|
5
|
+
|
|
6
|
+
The :class:`Strategy` Protocol is the canonical public type. Any object
|
|
7
|
+
with a matching ``execute`` method satisfies it — no inheritance
|
|
8
|
+
required. :class:`BaseStrategy` is an optional helper that supplies
|
|
9
|
+
shared utilities (config normalization, prompt resolution, step
|
|
10
|
+
notification, runtime-template rendering, tool-aware LLM invocation).
|
|
11
|
+
|
|
12
|
+
Retry and backoff live exclusively on :class:`~ellements.core.LLMClient`.
|
|
13
|
+
Strategies must not retry on top.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import re
|
|
20
|
+
from collections.abc import Callable, Mapping
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import Any, Protocol, TypeVar, runtime_checkable
|
|
23
|
+
|
|
24
|
+
import chevron
|
|
25
|
+
from ellements.core import LLMClient, MessageInput, PromptKeyMissingError
|
|
26
|
+
from pydantic import BaseModel
|
|
27
|
+
|
|
28
|
+
from .config import (
|
|
29
|
+
ExecutionStrategyConfig,
|
|
30
|
+
StrategyConfigInput,
|
|
31
|
+
normalize_strategy_config,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
ConfigModelT = TypeVar("ConfigModelT", bound=ExecutionStrategyConfig)
|
|
37
|
+
StructuredT = TypeVar("StructuredT", bound=BaseModel)
|
|
38
|
+
|
|
39
|
+
# Callback invoked after each :class:`StepRecord` is produced.
|
|
40
|
+
OnStepCallback = Callable[["StepRecord"], None]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(slots=True)
|
|
44
|
+
class StepRecord:
|
|
45
|
+
"""One step in a strategy execution (single LLM call or aggregation)."""
|
|
46
|
+
|
|
47
|
+
name: str
|
|
48
|
+
prompt_key: str
|
|
49
|
+
response: str
|
|
50
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(slots=True)
|
|
54
|
+
class StrategyResult:
|
|
55
|
+
"""Final result of a strategy execution."""
|
|
56
|
+
|
|
57
|
+
output: str
|
|
58
|
+
steps: list[StepRecord] = field(default_factory=list)
|
|
59
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@runtime_checkable
|
|
63
|
+
class Strategy(Protocol):
|
|
64
|
+
"""Protocol for execution strategies.
|
|
65
|
+
|
|
66
|
+
Any object whose ``execute`` method matches this signature is a
|
|
67
|
+
valid strategy — no inheritance required. This is the public type
|
|
68
|
+
used in every signature that accepts a strategy.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
async def execute(
|
|
72
|
+
self,
|
|
73
|
+
prompts: Mapping[str, str],
|
|
74
|
+
client: LLMClient,
|
|
75
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None = None,
|
|
76
|
+
config: StrategyConfigInput = None,
|
|
77
|
+
) -> StrategyResult: ...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class BaseStrategy:
|
|
81
|
+
"""Optional helper base class with utilities shared by built-in strategies."""
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def _notify_step(config: StrategyConfigInput, step: StepRecord) -> None:
|
|
85
|
+
"""Invoke the ``on_step`` callback if one is present in *config*."""
|
|
86
|
+
if config is None:
|
|
87
|
+
return
|
|
88
|
+
callback: Any
|
|
89
|
+
if isinstance(config, Mapping):
|
|
90
|
+
callback = config.get("on_step")
|
|
91
|
+
else:
|
|
92
|
+
callback = getattr(config, "on_step", None)
|
|
93
|
+
if callable(callback):
|
|
94
|
+
callback(step)
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _normalize_config(
|
|
98
|
+
config: StrategyConfigInput,
|
|
99
|
+
config_model: type[ConfigModelT],
|
|
100
|
+
) -> ConfigModelT:
|
|
101
|
+
"""Normalize *config* to the typed model expected by the strategy."""
|
|
102
|
+
return normalize_strategy_config(config, config_model)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _get_prompt(
|
|
106
|
+
prompts: Mapping[str, str],
|
|
107
|
+
key: str,
|
|
108
|
+
*,
|
|
109
|
+
required: bool = True,
|
|
110
|
+
) -> str:
|
|
111
|
+
"""Return the prompt for *key*, with a strict required/optional contract.
|
|
112
|
+
|
|
113
|
+
- ``required=True`` and the key is missing → raises
|
|
114
|
+
:class:`PromptKeyMissingError`. There is no silent fallback to
|
|
115
|
+
``"default"``.
|
|
116
|
+
- ``required=False`` and the key is missing → returns ``""``.
|
|
117
|
+
"""
|
|
118
|
+
value = prompts.get(key)
|
|
119
|
+
if value is not None:
|
|
120
|
+
return value
|
|
121
|
+
if not required:
|
|
122
|
+
return ""
|
|
123
|
+
raise PromptKeyMissingError(
|
|
124
|
+
f"Strategy requires a prompt named {key!r}. "
|
|
125
|
+
f"Available: {sorted(prompts.keys())}."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _render_template(template: str, context: Mapping[str, Any]) -> str:
|
|
130
|
+
"""Render runtime placeholders against *context*.
|
|
131
|
+
|
|
132
|
+
Strategies support regular Mustache placeholders (``{{name}}``) plus
|
|
133
|
+
PromptSpec-style placeholders (``@{name}``) so executable PromptSpec
|
|
134
|
+
files can use the same placeholder form as the rest of the DSL.
|
|
135
|
+
"""
|
|
136
|
+
rendered: str = chevron.render(template, dict(context))
|
|
137
|
+
|
|
138
|
+
def replace_promptspec_placeholder(match: re.Match[str]) -> str:
|
|
139
|
+
key = match.group(1)
|
|
140
|
+
if key not in context:
|
|
141
|
+
return match.group(0)
|
|
142
|
+
return str(context[key])
|
|
143
|
+
|
|
144
|
+
return re.sub(
|
|
145
|
+
r"@\{([A-Za-z_][\w.-]*)\}",
|
|
146
|
+
replace_promptspec_placeholder,
|
|
147
|
+
rendered,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _stage_temperature(
|
|
152
|
+
config: ExecutionStrategyConfig,
|
|
153
|
+
stage: str,
|
|
154
|
+
default: float,
|
|
155
|
+
) -> float:
|
|
156
|
+
"""Resolve the effective temperature for a named pipeline stage.
|
|
157
|
+
|
|
158
|
+
``stage_temperatures[stage]`` overrides the per-stage attribute
|
|
159
|
+
on the typed config, which itself defaults to *default*.
|
|
160
|
+
"""
|
|
161
|
+
if config.stage_temperatures and stage in config.stage_temperatures:
|
|
162
|
+
return config.stage_temperatures[stage]
|
|
163
|
+
attr = getattr(config, f"{stage}_temperature", None)
|
|
164
|
+
if isinstance(attr, (int, float)):
|
|
165
|
+
return float(attr)
|
|
166
|
+
return default
|
|
167
|
+
|
|
168
|
+
@classmethod
|
|
169
|
+
async def _complete(
|
|
170
|
+
cls,
|
|
171
|
+
client: LLMClient,
|
|
172
|
+
prompt: str,
|
|
173
|
+
*,
|
|
174
|
+
config: ExecutionStrategyConfig,
|
|
175
|
+
temperature: float,
|
|
176
|
+
system: str | None = None,
|
|
177
|
+
tools: list[dict[str, Any]] | Mapping[str, Any] | None = None,
|
|
178
|
+
) -> str:
|
|
179
|
+
"""Make a single LLM call, with tools if any are provided.
|
|
180
|
+
|
|
181
|
+
Retries are handled inside :class:`LLMClient`; strategies do not
|
|
182
|
+
add a second retry layer.
|
|
183
|
+
"""
|
|
184
|
+
messages: list[dict[str, Any]] = []
|
|
185
|
+
if system:
|
|
186
|
+
messages.append({"role": "system", "content": system})
|
|
187
|
+
messages.append({"role": "user", "content": prompt})
|
|
188
|
+
|
|
189
|
+
if tools:
|
|
190
|
+
response = await client.complete_with_tools(
|
|
191
|
+
messages,
|
|
192
|
+
tools=tools,
|
|
193
|
+
model=config.model,
|
|
194
|
+
temperature=temperature,
|
|
195
|
+
)
|
|
196
|
+
return response.content
|
|
197
|
+
return await client.complete(
|
|
198
|
+
messages,
|
|
199
|
+
model=config.model,
|
|
200
|
+
temperature=temperature,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
async def _complete_structured(
|
|
205
|
+
client: LLMClient,
|
|
206
|
+
prompt: str,
|
|
207
|
+
response_model: type[StructuredT],
|
|
208
|
+
*,
|
|
209
|
+
config: ExecutionStrategyConfig,
|
|
210
|
+
temperature: float,
|
|
211
|
+
system: str | None = None,
|
|
212
|
+
) -> StructuredT:
|
|
213
|
+
"""Make a structured LLM call returning a Pydantic model instance."""
|
|
214
|
+
messages: list[dict[str, Any]] = []
|
|
215
|
+
if system:
|
|
216
|
+
messages.append({"role": "system", "content": system})
|
|
217
|
+
messages.append({"role": "user", "content": prompt})
|
|
218
|
+
return await client.complete_structured(
|
|
219
|
+
messages,
|
|
220
|
+
response_model=response_model,
|
|
221
|
+
model=config.model,
|
|
222
|
+
temperature=temperature,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
__all__ = [
|
|
227
|
+
"BaseStrategy",
|
|
228
|
+
"OnStepCallback",
|
|
229
|
+
"StepRecord",
|
|
230
|
+
"Strategy",
|
|
231
|
+
"StrategyResult",
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# Allow type-checkers to see MessageInput is referenced even though we
|
|
236
|
+
# don't currently expose it through this module.
|
|
237
|
+
_ = MessageInput # noqa: F841
|