STROT
// Documentation · v0.11.1

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.
// 01

Installation

The core package is pure stdlib. Pick an extra for where the decision model runs:

shell
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):

shell
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.

// 02

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.

quickstart.pyCompiled on first call, cached forever.
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.94

Each 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).

See it decide on live data — try the live demo ↗

// 03

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.

decide.py
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.

// 04

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:

compile.py
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 imports

At runtime, import the file directly:

compiled_pipeline.pyNo strot dependency.
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 blindly

run() manages this artifact lifecycle for you (compile, cache, execute); the compiler is for when you want the raw .py to ship yourself.

// 05

Parameters (run / decide)

ParameterDescription
dataRecords — list of dicts, dict of lists, or a DataFrame. The first sample_size records also ground the one-time compile.
query / questionsThe goal in natural language (run) / the typed-question config (decide).
action_registryrun() only — {action_key: description}. One handler synthesized per entry. >20 actions routes hierarchically (Option Tree Splitter).
llmSynthesizer spec, resolved only on cache miss: "anthropic/<model>", "openai/<model>", "claude-cli", or any (str) -> str callable.
nluRuntime decision model baked into the artifact. Default SupersonicLabs/Julia-1. A provider/model id (e.g. openai/gpt-4o-mini) uses the hosted runtime.
stateArtifact-store directory (default .strot). The persisted “compiled flag + code”.
sample_sizeRecords used for schema profiling and compile grounding (default 25; use 100+ for calibration/distillation).
goldenKnown-answer records; a candidate artifact must pass them to be promoted.
target_riskα for a fitted conformal dispatch guarantee (marginal).
measure_labelsScore synthesized labels vs bare registry keys on a teacher-labeled sample; keep the measured winner.
distillTrain a logistic head on the frozen encoder’s embeddings; ship it only if it beats zero-shot on a held-out fold.
target_accuracyFit 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 to max_retries; exhaustion raises CompilationError.
  • 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.
// 06

Confidence & abstention

A compiled artifact never guesses blindly. Three modes, by how you compiled it:

ModeHowBehavior
Default threshold(no extra params)Routing confidence below 0.40 → FallbackException (collected in result.fallbacks).
Conformal guaranteetarget_risk=αDispatches only when the calibrated prediction set is a singleton; otherwise abstains. Guarantee is marginal: wrong dispatches ≤ α of all records.
Accuracy policytarget_accuracy=aLocal 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.

// 07

Runtime models (the nlu slot)

  • Local (default) — SupersonicLabs/Julia-1, a 144M decision model on CPU via its supersonic-julia package, ~60 ms/record, fully offline. Requires [local] + the prefetch step.
  • Hosted — any provider/model id (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=.
// 08

API reference

SymbolPurpose
strot.runCompile-and-execute a routing/dispatch pipeline.
strot.decideAnswer typed questions (choice/score/boolean) over records.
strot.prefetch_nluDownload + install the decision model for offline/image builds.
RunResult.outcomes, .fallbacks, .summary(), .cache_hit, .artifact_path.
StrotCompiler / CompiledAssetLow-level compile + standalone .save().
anthropic_llm_client / openai_llm_client / claude_cli_llm_clientSynthesizer factories.
FallbackExceptionRaised by a compiled artifact on an abstained record.
CompilationErrorRaised when self-correction retries are exhausted.
introspect_schema, SchemaProfilerVerified schema from a sample.
// 09

Development

shell
poetry install --extras all
poetry run pytest

MIT licensed. Source, issues, contributions: github.com/strot-ai/STROT-Compiler