Core API¶
This page is auto-generated from docstrings via mkdocstrings. Source-of-truth lives in the Python files.
Patterns¶
consensus¶
executionkit.patterns.consensus.consensus
async
¶
consensus(provider: LLMProvider, prompt: str, *, num_samples: int = 5, strategy: VotingStrategy | str = 'majority', temperature: float = _DEFAULT_TEMPERATURE, max_tokens: int = DEFAULT_MAX_TOKENS, max_concurrency: int = _DEFAULT_CONSENSUS_CONCURRENCY, retry: RetryConfig | None = None, max_cost: TokenUsage | None = None, trace: TraceCallback | None = None) -> PatternResult[str]
Run parallel LLM samples and aggregate via voting.
Fires num_samples concurrent completions and applies the chosen
voting strategy to determine the winning response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
LLMProvider
|
LLM provider to call. |
required |
prompt
|
str
|
User prompt sent identically to every sample. |
required |
num_samples
|
int
|
Number of parallel completions to request. Must be >= 1. |
5
|
strategy
|
VotingStrategy | str
|
|
'majority'
|
temperature
|
float
|
Sampling temperature (higher = more diverse). |
_DEFAULT_TEMPERATURE
|
max_tokens
|
int
|
Maximum tokens per completion. |
DEFAULT_MAX_TOKENS
|
max_concurrency
|
int
|
Semaphore limit for parallel calls. |
_DEFAULT_CONSENSUS_CONCURRENCY
|
retry
|
RetryConfig | None
|
Optional retry configuration per call. |
None
|
max_cost
|
TokenUsage | None
|
Optional token/call budget. Passed to each individual
|
None
|
trace
|
TraceCallback | None
|
Optional structured trace callback. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
PatternResult[str]
|
class: |
PatternResult[str]
|
|
|
PatternResult[str]
|
|
Raises:
| Type | Description |
|---|---|
ConsensusFailedError
|
When |
ValueError
|
If |
Source code in executionkit/patterns/consensus.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | |
refine_loop¶
executionkit.patterns.refine_loop.refine_loop
async
¶
refine_loop(provider: LLMProvider, prompt: str, *, evaluator: Evaluator | None = None, max_eval_chars: int = _DEFAULT_MAX_EVAL_CHARS, target_score: float = 0.9, max_iterations: int = 5, patience: int = 3, delta_threshold: float = 0.01, temperature: float = _DEFAULT_TEMPERATURE, max_tokens: int = DEFAULT_MAX_TOKENS, max_cost: TokenUsage | None = None, retry: RetryConfig | None = None, trace: TraceCallback | None = None, on_checkpoint: CheckpointCallback | None = None) -> PatternResult[str]
Iteratively refine an LLM response until convergence or budget exhaustion.
Generates an initial response, evaluates it, then enters a refinement
loop. Each iteration asks the LLM to improve upon the previous output
given its score. The loop terminates when the
:class:ConvergenceDetector signals convergence (target score reached
or score deltas stall beyond patience) or max_iterations is hit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
LLMProvider
|
LLM provider to call. |
required |
prompt
|
str
|
The original user prompt. |
required |
evaluator
|
Evaluator | None
|
Async callable |
None
|
target_score
|
float
|
Convergence target in |
0.9
|
max_iterations
|
int
|
Maximum refinement iterations (excluding the initial generation). |
5
|
patience
|
int
|
Stale-delta iterations before convergence is declared. |
3
|
delta_threshold
|
float
|
Minimum meaningful score improvement. |
0.01
|
temperature
|
float
|
Sampling temperature for generation calls. |
_DEFAULT_TEMPERATURE
|
max_tokens
|
int
|
Maximum tokens per completion. |
DEFAULT_MAX_TOKENS
|
max_cost
|
TokenUsage | None
|
Optional token/call budget. |
None
|
retry
|
RetryConfig | None
|
Optional retry configuration per call. |
None
|
trace
|
TraceCallback | None
|
Optional structured trace callback. |
None
|
on_checkpoint
|
CheckpointCallback | None
|
Optional callback invoked after scoring each candidate
(the initial generation as iteration 0, then each refinement
iteration), receiving |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
PatternResult[str]
|
class: |
PatternResult[str]
|
|
|
PatternResult[str]
|
|
Source code in executionkit/patterns/refine_loop.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | |
react_loop¶
executionkit.patterns.react_loop.react_loop
async
¶
react_loop(provider: ToolCallingProvider, prompt: str | None = None, tools: Sequence[Tool] = (), *, messages: Sequence[dict[str, Any]] | None = None, max_rounds: int = _DEFAULT_MAX_ROUNDS, max_observation_chars: int = _DEFAULT_MAX_OBSERVATION_CHARS, tool_timeout: float | None = None, max_tool_calls_per_round: int = _DEFAULT_MAX_TOOL_CALLS_PER_ROUND, temperature: float = _DEFAULT_TEMPERATURE, max_tokens: int = DEFAULT_MAX_TOKENS, max_cost: TokenUsage | None = None, retry: RetryConfig | None = None, max_history_messages: int | None = None, trace: TraceCallback | None = None, approval_gate: ApprovalGate | None = None, redact_trace_args: bool = True, on_checkpoint: CheckpointCallback | None = None, summarizer: HistorySummarizer | None = None) -> PatternResult[str]
Execute a think-act-observe tool-calling loop.
The LLM is called repeatedly with the conversation history and
available tool schemas. When the LLM returns tool calls, each tool
is executed and its result appended as a tool-role message. The loop
ends when the LLM responds without tool calls (final answer) or
max_rounds is exhausted.
Supply either prompt (a single new user turn) or messages (a
full prior conversation to continue), not both. Passing messages enables
multi-turn assistants: the returned metadata["messages"] holds the
complete updated transcript (the input history plus this run's assistant
turns, tool results, and final answer) ready to feed into the next call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
ToolCallingProvider
|
LLM provider to call. |
required |
prompt
|
str | None
|
Initial user prompt. Sugar for |
None
|
tools
|
Sequence[Tool]
|
Sequence of :class: |
()
|
messages
|
Sequence[dict[str, Any]] | None
|
A prior conversation (OpenAI-format message dicts) to continue instead of starting from prompt. The list is copied, never mutated. |
None
|
max_rounds
|
int
|
Maximum think-act-observe cycles. |
_DEFAULT_MAX_ROUNDS
|
max_observation_chars
|
int
|
Truncation limit for each tool result. |
_DEFAULT_MAX_OBSERVATION_CHARS
|
tool_timeout
|
float | None
|
Per-call timeout override. Falls back to
|
None
|
max_tool_calls_per_round
|
int
|
Ceiling on model-requested tool calls executed in a single round (they run concurrently, so this bounds the fan-out). Surplus calls are never executed; each receives a rejection observation telling the model to retry with fewer. |
_DEFAULT_MAX_TOOL_CALLS_PER_ROUND
|
temperature
|
float
|
Sampling temperature (lower = more deterministic). |
_DEFAULT_TEMPERATURE
|
max_tokens
|
int
|
Maximum tokens per LLM completion. |
DEFAULT_MAX_TOKENS
|
max_cost
|
TokenUsage | None
|
Optional token/call budget. |
None
|
retry
|
RetryConfig | None
|
Optional retry configuration per LLM call. |
None
|
max_history_messages
|
int | None
|
When set, trim the message history to at most
this many entries before each LLM call. Always keeps the first
message (the original prompt). |
None
|
trace
|
TraceCallback | None
|
Optional structured trace callback. |
None
|
approval_gate
|
ApprovalGate | None
|
Optional gate checked before each tool execution. |
None
|
redact_trace_args
|
bool
|
When |
True
|
on_checkpoint
|
CheckpointCallback | None
|
Optional callback invoked after each round that dispatched
tool calls, receiving |
None
|
summarizer
|
HistorySummarizer | None
|
Optional async callback used together with
The callback may return either the summary text or a
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
PatternResult[str]
|
class: |
PatternResult[str]
|
response, |
|
PatternResult[str]
|
|
Source code in executionkit/patterns/react_loop.py
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | |
structured¶
executionkit.patterns.structured.structured
async
¶
structured(provider: LLMProvider, prompt: str, *, validator: StructuredValidator | None = None, max_retries: int = 3, temperature: float = 0.0, max_tokens: int = 4096, max_cost: TokenUsage | None = None, retry: RetryConfig | None = None, trace: TraceCallback | None = None, stream: bool = False) -> PatternResult[StructuredValue]
Request JSON output, parse it, and optionally repair invalid responses.
Validators should return None, True, or "" for success. Any
other value is treated as a validation failure and included in the repair
prompt. max_retries=0 is supported and means "make one parse attempt
with no repair call".
Source code in executionkit/patterns/structured.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
pipe¶
executionkit.compose.pipe
async
¶
pipe(provider: LLMProvider, prompt: str, *steps: PatternStep, max_budget: TokenUsage | None = None, **shared_kwargs: Any) -> PatternResult[Any]
Chain reasoning patterns, threading output as the next prompt.
Each step must be an async callable with the signature::
async def step(provider, prompt, **kwargs) -> PatternResult[Any]
The value of each result is converted to a string and passed as the
prompt to the following step. Costs are accumulated and, when
max_budget is given, the remaining budget is forwarded to each step
via the max_cost keyword argument.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
LLMProvider
|
LLM provider passed unchanged to every step. |
required |
prompt
|
str
|
Initial input prompt. |
required |
*steps
|
PatternStep
|
Async pattern callables to chain in order. |
()
|
max_budget
|
TokenUsage | None
|
Optional shared token/call budget across all steps. |
None
|
**shared_kwargs
|
Any
|
Extra keyword arguments forwarded to every step. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
The |
PatternResult[Any]
|
class: |
PatternResult[Any]
|
with its |
|
PatternResult[Any]
|
If steps is empty the prompt is returned as-is with zero cost. |
|
PatternResult[Any]
|
The result |
|
PatternResult[Any]
|
class: |
|
PatternResult[Any]
|
error path the same |
|
PatternResult[Any]
|
spend) is attached to the re-raised exception's |
Source code in executionkit/compose.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | |
executionkit.compose.PatternStep ¶
Bases: Protocol
Callable protocol for a single step in a :func:pipe chain.
Each step must accept (provider, prompt, **kwargs) and return an
awaitable :class:~executionkit.types.PatternResult. Extra keyword
arguments (e.g. max_cost) are filtered to only those the step
actually accepts, so steps that do not declare **kwargs will not
receive unsupported arguments.
Sync wrappers¶
from executionkit import (
consensus_sync,
refine_loop_sync,
react_loop_sync,
structured_sync,
pipe_sync,
)
Each sync wrapper takes the same arguments as its async counterpart and runs it via asyncio.run. They raise RuntimeError when called inside a running event loop — use await directly there.
Value types¶
executionkit.types.PatternResult
dataclass
¶
PatternResult(value: T, score: float | None = None, cost: TokenUsage = TokenUsage(), metadata: MappingProxyType[str, Any] = (lambda: MappingProxyType({}))())
Bases: Generic[T]
Result returned by every reasoning pattern.
metadata keys vary by pattern. Each pattern documents its own keys in its
function docstring. Do not rely on undocumented keys — they are private.
executionkit.types.TokenUsage
dataclass
¶
Accumulated token and call counts.
__sub__ ¶
Return the field-wise difference self - other.
Useful for computing the delta between two :class:CostTracker
snapshots (e.g. per-step cost attribution). No clamping is applied,
so callers that subtract a later snapshot from an earlier one can
observe negative fields.
Source code in executionkit/types.py
executionkit.types.Tool
dataclass
¶
Tool(name: str, description: str, parameters: Mapping[str, Any], execute: Callable[..., Awaitable[str]], timeout: float = DEFAULT_TOOL_TIMEOUT_SECONDS)
Describes a tool available for LLM tool-calling.
parameters is a JSON Schema mapping describing the function arguments.
Automatically wrapped in a read-only proxy.
execute is the async callable invoked when the LLM requests this tool.
executionkit.types.VotingStrategy ¶
Bases: StrEnum
Strategy for consensus voting.
executionkit.types.Evaluator
module-attribute
¶
Async callable that scores a response string on [0.0, 1.0].
Evals¶
executionkit.evals.EvalCase
dataclass
¶
A single eval case with a runner and a result check.
executionkit.evals.EvalResult
dataclass
¶
EvalResult(name: str, passed: bool, reason: str = '', metadata: MappingProxyType[str, Any] = (lambda: MappingProxyType({}))())
Outcome of a single eval case.
executionkit.evals.EvalReport
dataclass
¶
Aggregate eval report.
Attributes:
| Name | Type | Description |
|---|---|---|
results |
tuple[EvalResult, ...]
|
Per-case outcomes. |
min_accuracy |
float | None
|
Optional accuracy threshold used by
:func: |
accuracy_passed
property
¶
True when accuracy >= min_accuracy (live-suite gate).
Falls back to :attr:passed when no min_accuracy was configured,
so callers can always use this property as the single gate regardless
of suite type.
passed
property
¶
True when every case passed (100% — required for deterministic suites).
summary ¶
executionkit.evals.run_eval_suite
async
¶
Run eval cases in order and return pass/fail results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cases
|
Sequence[EvalCase]
|
Eval cases to run. |
required |
min_accuracy
|
float | None
|
When provided, the report is considered passing when
|
None
|
Returns:
| Type | Description |
|---|---|
EvalReport
|
class: |
EvalReport
|
|
EvalReport
|
|
Source code in executionkit/evals.py
executionkit.evals.live_provider_from_env ¶
Build an opt-in live eval provider from EXECUTIONKIT_* env vars.
Returns None unless EXECUTIONKIT_LIVE_EVAL=1. When enabled,
EXECUTIONKIT_BASE_URL and EXECUTIONKIT_MODEL are required;
EXECUTIONKIT_API_KEY is optional and defaults to an empty string.
Source code in executionkit/evals.py
Observability¶
executionkit.observability.TraceEvent
dataclass
¶
A structured event emitted by patterns and lightweight primitives.
executionkit.observability.TraceCallback
module-attribute
¶
executionkit.observability.emit_trace
async
¶
Emit event to an optional callback.
Source code in executionkit/observability.py
Routing¶
executionkit.routing.RouteRule
dataclass
¶
RouteRule(name: str, provider: LLMProvider, predicate: RoutePredicate, metadata: Mapping[str, Any] = dict())
A named provider selection rule.
executionkit.routing.Router ¶
Select a provider by evaluating rules before a pattern call.
Source code in executionkit/routing.py
run
async
¶
run(pattern: RoutedPattern, prompt: str, *, context: Mapping[str, Any] | None = None, **kwargs: Any) -> PatternResult[Any]
Select a provider from context, then call pattern with it.
Routing inputs are passed explicitly via context and forwarded only to
the route predicates; **kwargs are forwarded only to pattern.
Keeping the two disjoint stops routing keys (e.g. tier) from leaking
into the pattern call — which would raise TypeError for any pattern
that does not declare them.
Source code in executionkit/routing.py
Workflow and planning¶
executionkit.workflow.Step
dataclass
¶
Step(name: str, run: WorkflowRun, depends_on: tuple[str, ...] = (), metadata: Mapping[str, Any] = dict())
A named workflow step with optional dependencies.
executionkit.workflow.Workflow ¶
Run named steps once their dependencies are available.
Source code in executionkit/workflow.py
run
async
¶
run(initial_context: Mapping[str, Any] | None = None, *, trace: TraceCallback | None = None, approval_gate: ApprovalGate | None = None, checkpoint_fn: CheckpointFn | None = None, resume_from: WorkflowCheckpoint | None = None) -> WorkflowResult
Execute the workflow, optionally checkpointing after each batch.
Parameters¶
initial_context:
Key/value pairs injected into the step context before execution.
trace:
Optional async or sync callback receiving
:class:~executionkit.observability.TraceEvent objects.
approval_gate:
Optional gate consulted before each step runs.
checkpoint_fn:
Called with a :class:WorkflowCheckpoint after each batch of
completed steps. The caller is responsible for persisting the
checkpoint; this library imposes no storage requirement.
resume_from:
A previously saved :class:WorkflowCheckpoint. Steps whose
names already appear in resume_from.outputs are skipped;
accumulated outputs and token budget are restored verbatim.
When None (default), the workflow starts from the beginning.
Source code in executionkit/workflow.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
executionkit.workflow.WorkflowResult
dataclass
¶
Outputs and aggregate cost from a workflow run.
executionkit.planning.PlanStep
dataclass
¶
A human-readable executable plan step.
executionkit.planning.Plan ¶
executionkit.planning.PlanResult
dataclass
¶
Outputs and aggregate cost from plan execution.
Approval¶
executionkit.approval.ApprovalRequest
dataclass
¶
ApprovalRequest(action: str, subject: str, metadata: MappingProxyType[str, Any] = (lambda: MappingProxyType({}))())
A request to approve an operation before it runs.
executionkit.approval.ApprovalDecision
dataclass
¶
Approval result.
executionkit.approval.ApprovalGate ¶
ApprovalGate(callback: ApprovalCallback, *, timeout_seconds: float | None = None, on_timeout: Literal['approve', 'deny', 'raise'] = 'raise')
Async-compatible approval callback wrapper.
Parameters¶
callback:
Synchronous or async callable that receives an :class:ApprovalRequest
and returns an :class:ApprovalDecision (or a truthy/falsy value).
timeout_seconds:
If given, the callback must resolve within this many seconds. When it
does not, on_timeout controls the fallback behaviour. None
(default) preserves the original blocking behaviour.
on_timeout:
What to do when timeout_seconds elapses before the callback resolves.
* ``"raise"`` (default) — raise :class:`ApprovalTimeoutError`.
* ``"approve"`` — treat the timed-out request as approved.
* ``"deny"`` — treat the timed-out request as denied.
.. warning::
Setting on_timeout="approve" creates a fail-open gate: if the
approval callback does not respond in time, the operation is
automatically permitted. This is a potential privilege-escalation
vector. Only use this option when you have deliberately decided that
availability is more important than security for the guarded operation.
Source code in executionkit/approval.py
executionkit.approval.ApprovalDeniedError ¶
ApprovalDeniedError(message: str, *, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: ExecutionKitError
Raised when an approval-gated operation is denied.
Source code in executionkit/errors.py
Session¶
executionkit.kit.Kit ¶
Kit(provider: LLMProvider, *, track_cost: bool = True, messages: Sequence[dict[str, Any]] | None = None, rate_limiter: TokenBucket | None = None)
Session that holds a :class:~executionkit.provider.Provider and
tracks cumulative token usage across all pattern calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
LLMProvider
|
The LLM provider to use for all calls. |
required |
track_cost
|
bool
|
When |
True
|
messages
|
Sequence[dict[str, Any]] | None
|
Optional seed conversation (OpenAI-format message dicts) for
multi-turn use via :meth: |
None
|
rate_limiter
|
TokenBucket | None
|
Optional :class: |
None
|
Conversation state
:attr:messages holds the running transcript across :meth:turn calls.
The single-shot pattern methods (:meth:react, :meth:consensus, …) do
not read or write it — use :meth:turn for stateful conversations.
Source code in executionkit/kit.py
consensus
async
¶
Run the :func:~executionkit.patterns.consensus.consensus pattern.
All keyword arguments are forwarded unchanged to :func:consensus.
Source code in executionkit/kit.py
map_reduce
async
¶
Run the :func:~executionkit.patterns.map_reduce.map_reduce pattern.
All keyword arguments are forwarded unchanged to :func:map_reduce.
map_prompt_template and reduce_prompt_template are required
keyword arguments.
Source code in executionkit/kit.py
pipe
async
¶
Run :func:~executionkit.compose.pipe with this Kit's provider.
All keyword arguments are forwarded unchanged to :func:pipe.
Source code in executionkit/kit.py
react
async
¶
Run the :func:~executionkit.patterns.react_loop.react_loop pattern.
All keyword arguments are forwarded unchanged to :func:react_loop.
The provider must satisfy :class:~executionkit.provider.ToolCallingProvider;
a :exc:TypeError is raised if it does not.
Source code in executionkit/kit.py
refine
async
¶
Run the :func:~executionkit.patterns.refine_loop.refine_loop pattern.
All keyword arguments are forwarded unchanged to :func:refine_loop.
Source code in executionkit/kit.py
stream_consensus
async
¶
stream_consensus(prompt: str, *, temperature: float = _STREAM_CONSENSUS_TEMPERATURE, max_tokens: int = DEFAULT_MAX_TOKENS, max_cost: TokenUsage | None = None, trace: TraceCallback | None = None) -> StreamingPatternResult
Stream a single live completion of prompt.
Consensus voting needs complete responses to compare, so the full
pattern has no coherent token stream; this convenience method streams
one generation (no voting). Token deltas arrive live and
result.cost becomes accurate once the stream is drained, at which
point the spend is folded into this Kit's cumulative :attr:usage.
Source code in executionkit/kit.py
stream_react_loop
async
¶
stream_react_loop(prompt: str, tools: Sequence[Tool] = (), *, temperature: float = _STREAM_REACT_TEMPERATURE, max_tokens: int = DEFAULT_MAX_TOKENS, max_cost: TokenUsage | None = None, trace: TraceCallback | None = None) -> StreamingPatternResult
Stream a single live model turn for prompt.
The full ReAct loop runs tools across multiple rounds and cannot be
expressed as one token stream, so this convenience method streams a
single model generation. tools is accepted for parity with
:meth:react but is not executed (tool-call deltas carry no
message content). result.cost is accurate after the stream drains
and folds into this Kit's :attr:usage.
Source code in executionkit/kit.py
turn
async
¶
Run one conversational turn, carrying history across calls.
Appends user_text to :attr:messages, runs
:func:~executionkit.patterns.react_loop.react_loop over the full
history, then replaces :attr:messages with the returned transcript so
the next turn continues the conversation. Cost folds into
:attr:usage, exactly like :meth:react.
The provider must satisfy
:class:~executionkit.provider.ToolCallingProvider; a :exc:TypeError
is raised otherwise. If the turn fails (e.g. budget exhausted),
:attr:messages is left unchanged so the turn can be retried.
All other keyword arguments are forwarded unchanged to react_loop.
Source code in executionkit/kit.py
Cost tracking¶
executionkit.cost.CostTracker ¶
Mutable accumulator for token and call counts.
Source code in executionkit/cost.py
add_usage ¶
Add pre-computed usage to the tracker (e.g. from a pattern result).
Use this instead of accessing private fields directly.
Source code in executionkit/cost.py
record ¶
record_without_call ¶
Record token usage from a response without incrementing the call counter.
Used by :func:~executionkit.patterns.base.checked_complete because
:meth:reserve_call (called from _before_attempt) pre-increments
_calls before the await to uphold the asyncio concurrency
contract described in the module-level docstring. Token totals are
therefore recorded here, after the provider returns, without a second
increment to the call counter.
Source code in executionkit/cost.py
reserve_call ¶
Reserve a call slot before dispatching (for budget-safe accounting).
Called by :func:~executionkit.patterns.base.checked_complete before
awaiting the provider call. Reservations intentionally count every
wire attempt, including failed attempts, so call budgets cap retries
as well as successes.
Concurrency contract: this method must be called in the same
synchronous run-segment as the preceding budget check — with no
await between them — to prevent concurrent asyncio coroutines
from racing past the check. See module-level docstring for details.
This guarantee does NOT hold under threading.
Source code in executionkit/cost.py
snapshot ¶
Return current totals as an immutable snapshot without mutating state.
Equivalent to :meth:to_usage; provided as the public, intention-revealing
name for callers that capture point-in-time deltas (e.g. per-step cost
attribution or loop checkpoint state). Calling it never advances any
counter, so two consecutive snapshots with no intervening record are
equal.
Source code in executionkit/cost.py
to_usage ¶
Return an immutable snapshot of accumulated usage.
Engine helpers¶
executionkit.engine.convergence.ConvergenceDetector
dataclass
¶
ConvergenceDetector(delta_threshold: float = 0.01, patience: int = 3, score_threshold: float | None = None)
Tracks score history and detects convergence via delta + patience.
Convergence is declared when either:
- score_threshold is set and the current score meets or exceeds it, or
- The score delta has been below delta_threshold for patience
consecutive iterations.
Attributes:
| Name | Type | Description |
|---|---|---|
delta_threshold |
float
|
Minimum meaningful score improvement. |
patience |
int
|
How many consecutive stale iterations before stopping. |
score_threshold |
float | None
|
Optional absolute score target for early exit. |
reset ¶
should_stop ¶
Record a score and return whether convergence is reached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
score
|
float
|
Evaluator score, must be in [0.0, 1.0] and not NaN. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the loop should stop (converged or threshold met). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If score is NaN or outside [0.0, 1.0]. |
Source code in executionkit/engine/convergence.py
executionkit.engine.retry.RetryConfig
dataclass
¶
RetryConfig(max_retries: int = 3, base_delay: float = 1.0, max_delay: float = 60.0, exponential_base: float = 2.0, retryable: tuple[type[Exception], ...] = (RateLimitError, ProviderError), rate_limit_strategy: TokenBucket | None = None)
Immutable retry configuration with exponential backoff.
Attributes:
| Name | Type | Description |
|---|---|---|
max_retries |
int
|
Maximum number of retry attempts. 0 means no retries. |
base_delay |
float
|
Base delay in seconds before first retry. |
max_delay |
float
|
Maximum delay cap in seconds. |
exponential_base |
float
|
Multiplier for exponential backoff. |
retryable |
tuple[type[Exception], ...]
|
Tuple of exception types that trigger retries. |
rate_limit_strategy |
TokenBucket | None
|
Optional |
get_delay ¶
Calculate jittered backoff delay for the given attempt (1-indexed).
Uses full jitter (random value in [0, capped_exponential]) to prevent thundering-herd effects when multiple coroutines retry simultaneously.
Source code in executionkit/engine/retry.py
executionkit.engine.json_extraction.extract_json ¶
Extract JSON from LLM output using multiple strategies.
Strategies (in order):
1. Raw json.loads(text.strip())
2. Markdown code fences -- the first `json fence, then the first generic
fence whose body looks like JSON. Located withstr.findso an
unterminated fence in untrusted input degrades to a linear scan instead
of the polynomial backtracking a.*?regex would incur under DOTALL.
3. Balanced-brace extraction -- find first{or[``, track nesting
depth respecting string boundaries, find matching closer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Raw LLM response text that may contain JSON. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | list[Any]
|
Parsed JSON as a dict or list. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no valid JSON can be extracted. |
Source code in executionkit/engine/json_extraction.py
Errors¶
All exceptions inherit from ExecutionKitError and carry .cost (TokenUsage accumulated up to the failure) and .metadata (dict).
| Exception | Cause |
|---|---|
ExecutionKitError |
Base for all errors. |
LLMError |
Base for provider communication errors. |
RateLimitError |
HTTP 429 — retryable. Carries retry_after. |
PermanentError |
HTTP 401/403/404 — not retryable. |
ProviderError |
Unexpected HTTP failure — retryable. |
PatternError |
Base for pattern logic errors. |
BudgetExhaustedError |
Token or call budget exceeded. |
ConsensusFailedError |
Unanimous strategy could not agree. |
MaxIterationsError |
react_loop exhausted max_rounds without a final answer. |
executionkit.provider.ExecutionKitError ¶
ExecutionKitError(message: str, *, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: Exception
Base exception for all ExecutionKit errors.
Source code in executionkit/errors.py
executionkit.provider.RateLimitError ¶
RateLimitError(message: str, *, retry_after: float = 1.0, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: LLMError
Provider returned HTTP 429 — retryable after retry_after seconds.
Source code in executionkit/errors.py
executionkit.provider.BudgetExhaustedError ¶
BudgetExhaustedError(message: str, *, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: PatternError
Token or call budget exceeded.
Source code in executionkit/errors.py
executionkit.provider.ConsensusFailedError ¶
ConsensusFailedError(message: str, *, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: PatternError
Consensus pattern could not reach agreement.
Source code in executionkit/errors.py
executionkit.provider.MaxIterationsError ¶
MaxIterationsError(message: str, *, cost: TokenUsage | None = None, metadata: dict[str, Any] | None = None)
Bases: PatternError
Loop pattern exceeded its iteration limit.