The open decision compiler.
Compile your intent once with an LLM. Then decide on every record forever — classification, scoring, routing — as type-safe JSON with probabilities and confidence, on your own hardware, at zero marginal cost.
Agents at compile time. Never at runtime.
Installation
The core package is pure stdlib. Pick an extra for where the decision model runs:
pip install strot-ai # core: compile logic, cache, calibration math pip install strot-ai[local] # + torch/transformers for the local decision model pip install strot-ai[hosted] # + openai for a hosted decision runtime pip install strot-ai[anthropic] # + anthropic for the LLM synthesizer
Local decision model — one extra step
The default model, SupersonicLabs/Julia-1 (Apache-2.0), ships its own inference package inside its Hugging Face repository — it is not on PyPI. Provision it once (bake this into your Docker image for offline production):
pip install strot-ai[local] python -c "import strot; strot.prefetch_nlu(local_dir='./julia-1', install=True)" export STROT_NLU_CHECKPOINT=./julia-1
After this, production runs fully offline.
Do not pip install julia — that is an unrelated package.
Quickstart — run()
run() compiles a decision pipeline on first call and executes every record through it. The LLM is used once, on the first compile; every later call (and every restart sharing the state directory) is a cache hit — no LLM, no API key, pure local execution.
import strot
tickets = [
{"ticket_id": 101, "channel": "email", "body": "I was double charged"},
{"ticket_id": 102, "channel": "chat", "body": "App crashes on export"},
]
result = strot.run(
tickets,
query="Triage incoming support tickets into the correct workflow.",
action_registry={
"billing_dispute": "Refunds, invoice corrections, double charges",
"technical_defect": "Crashes, bugs, broken behavior",
},
llm="anthropic/claude-opus-4-8", # used once, on first compile only
)
for outcome in result.outcomes:
print(outcome["action"], round(outcome["confidence"], 2))
# billing_dispute 0.97
# technical_defect 0.94Each outcome is {"index", "record", "action", "confidence", "result"}, where result is the output of the compiled handler for the chosen action. Low-confidence records are collected in result.fallbacks instead of being decided blindly (see Confidence & abstention).
Typed decisions — decide()
decide() answers typed questions over each record and returns bounded JSON — ideal when you want structured signals rather than a single routed action. Three question types: choice, score, boolean.
result = strot.decide(
tickets,
questions={
"is_bug_report": {"type": "boolean",
"instructions": "Does this mention malfunctions, errors, or crashes?"},
"customer_sentiment": {"type": "score", "min": 1, "max": 5,
"instructions": "1 is furious, 5 is delighted."},
"routing_bucket": {"type": "choice",
"options": ["billing_team", "engineering_team", "product_feedback", "spam"]},
},
llm="anthropic/claude-opus-4-8",
)
result.outcomes[0]["decision"]
# {
# "booleans": {"is_bug_report": {"type": "boolean", "probability": 0.9998}},
# "scores": {"customer_sentiment": {"value": 1.79, "confidence": 0.899}},
# "choices": {"routing_bucket": {"selected": "engineering_team",
# "probabilities": {"engineering_team": 0.9992, "product_feedback": 0.0008,
# "billing_team": 0.0, "spam": 0.0},
# "confidence": 0.9992}},
# }decide() always returns a decision with its calibrated confidence — it never abstains. The caller applies whatever threshold its use case requires.
Standalone artifacts (advanced)
For deploying a compiled pipeline as a single file with no strot dependency, use the low-level compiler and save the artifact:
from strot import StrotCompiler, anthropic_llm_client
compiler = StrotCompiler(
nlu_model="SupersonicLabs/Julia-1",
llm_client=anthropic_llm_client("claude-opus-4-8"),
)
asset = compiler.compile(
sample=tickets,
goal="Triage incoming support tickets into the correct workflow.",
action_registry={
"billing_dispute": "Refunds, invoice corrections, double charges",
"technical_defect": "Crashes, bugs, broken behavior",
},
)
asset.save("compiled_pipeline.py") # self-contained, zero strot importsAt runtime, import the file directly:
from compiled_pipeline import CompiledPipeline, FallbackException
pipeline = CompiledPipeline()
try:
print(pipeline.process({"ticket_id": 7, "body": "charged twice"}))
except FallbackException as exc:
quarantine(exc) # low confidence: don't execute blindlyrun() manages this artifact lifecycle for you (compile, cache, execute); the compiler is for when you want the raw .py to ship yourself.
Parameters (run / decide)
| Parameter | Description |
|---|---|
data | Records — list of dicts, dict of lists, or a DataFrame. The first sample_size records also ground the one-time compile. |
query / questions | The goal in natural language (run) / the typed-question config (decide). |
action_registry | run() only — {action_key: description}. One handler synthesized per entry. >20 actions routes hierarchically (Option Tree Splitter). |
llm | Synthesizer spec, resolved only on cache miss: "anthropic/<model>", "openai/<model>", "claude-cli", or any (str) -> str callable. |
nlu | Runtime decision model baked into the artifact. Default SupersonicLabs/Julia-1. A provider/model id (e.g. openai/gpt-4o-mini) uses the hosted runtime. |
state | Artifact-store directory (default .strot). The persisted “compiled flag + code”. |
sample_size | Records used for schema profiling and compile grounding (default 25; use 100+ for calibration/distillation). |
golden | Known-answer records; a candidate artifact must pass them to be promoted. |
target_risk | α for a fitted conformal dispatch guarantee (marginal). |
measure_labels | Score synthesized labels vs bare registry keys on a teacher-labeled sample; keep the measured winner. |
distill | Train a logistic head on the frozen encoder’s embeddings; ship it only if it beats zero-shot on a held-out fold. |
target_accuracy | Fit a local dispatch policy (temperature + per-class bias + cutoff) so auto-decided records meet an accuracy target; below-cutoff records abstain. |
Compile-time behavior you can rely on
- Self-correction — synthesized code is validated (
ast.parse, output contract, import + dynamic-exec blocklist). Failures retry with the error appended, up tomax_retries; exhaustion raisesCompilationError. - Golden promotion — with
golden=, the candidate is executed against your known answers inside the repair loop; only passing artifacts are served. - Drift busting — the cache key includes a schema fingerprint, so a changed column set or type triggers exactly one recompile; value changes don’t.
Confidence & abstention
A compiled artifact never guesses blindly. Three modes, by how you compiled it:
| Mode | How | Behavior |
|---|---|---|
| Default threshold | (no extra params) | Routing confidence below 0.40 → FallbackException (collected in result.fallbacks). |
| Conformal guarantee | target_risk=α | Dispatches only when the calibrated prediction set is a singleton; otherwise abstains. Guarantee is marginal: wrong dispatches ≤ α of all records. |
| Accuracy policy | target_accuracy=a | Local corrections + a fitted cutoff; records below the cutoff abstain. Empirical policy, not a finite-sample guarantee. Optimizes accuracy — for imbalanced tasks where minority recall matters, prefer a recall objective. |
In all modes, abstained records land in result.fallbacks — route them to human review. decide() always returns the decision + confidence and lets you threshold.
Runtime models (the nlu slot)
- Local (default) —
SupersonicLabs/Julia-1, a 144M decision model on CPU via itssupersonic-juliapackage, ~60 ms/record, fully offline. Requires[local]+ the prefetch step. - Hosted — any
provider/modelid (openai/gpt-4o-mini, …) routed via a hosted API with strict Structured Outputs and zero torch imports. Requires[hosted]and the provider’s API key in the environment. - Swappable — the compiler, gates, and calibration are model-agnostic; dropping in a task-trained specialist is a one-string change to
nlu=.
API reference
| Symbol | Purpose |
|---|---|
strot.run | Compile-and-execute a routing/dispatch pipeline. |
strot.decide | Answer typed questions (choice/score/boolean) over records. |
strot.prefetch_nlu | Download + install the decision model for offline/image builds. |
RunResult | .outcomes, .fallbacks, .summary(), .cache_hit, .artifact_path. |
StrotCompiler / CompiledAsset | Low-level compile + standalone .save(). |
anthropic_llm_client / openai_llm_client / claude_cli_llm_client | Synthesizer factories. |
FallbackException | Raised by a compiled artifact on an abstained record. |
CompilationError | Raised when self-correction retries are exhausted. |
introspect_schema, SchemaProfiler | Verified schema from a sample. |
Development
poetry install --extras all poetry run pytest
MIT licensed. Source, issues, contributions: github.com/strot-ai/STROT-Compiler