v0.1Rust core · Python 3.10–3.14 · MIT

Same input.
Same output.
Every time.

beacon is the deterministic layer between your Python code and your LLM. Templates, JSON extraction, tool-call parsing, schema validation, redaction and retries as pure functions with strict contracts, plus snapshot tests for the parts you control.

Get started
  • 8 pure blocks
  • 1–10 µs per call
  • 0 silent fixes
python · beacon
>>> from beacon import extract_json, hash_content
>>> raw = llm("Classify: I want to cancel")
>>> raw
'Sure! ```json\n{"intent": "cancel", "confidence": 0.93}\n``` Hope that helps.'
>>> extract_json(raw, schema=INTENT)
{'intent': 'cancel', 'confidence': 0.93}
>>> hash_content(_)
'1f1bc8aeb9db2e0b'
runs0
unique outputs1
digest1f1bc8aeb9db2e0b
Why beacon

LLM code is non-deterministic by accident.

The model is supposed to be the only unpredictable part of your system. In practice, the glue around it adds its own: greedy regexes, silent coercion, injectable prompts, random retries. beacon replaces that glue with blocks that have strict, documented contracts.

Without beacon
raw = llm(prompt)
data = json.loads(raw)
# JSONDecodeError: the model said "Sure! ```json ..."

match = re.search(r"\{.*\}", raw, re.S)   # the usual "fix"
data = json.loads(match.group())
# greedy: grabs from the first "{" in the prose to the
# last "}" — works in testing, breaks in production

Brittle regex scraping, or a "repair" library that guesses.

With beacon
data = extract_json(raw, schema=INTENT)

# fences, preamble, citations like [1]: handled.
# malformed JSON is never guessed at:
#   MalformedOutputError: trailing comma before '}'
#   (line 1, column 40)    err.code == "json.trailing_comma"

One documented algorithm. Strict RFC 8259. Exact error positions.

The contract

Four guarantees, in writing.

"Deterministic" is easy to claim. beacon publishes the contract, and its test suite checks it against 111 golden files on three operating systems.

01

Pure functions

No hidden state, no caches, no clock, no environment variables, no randomness. The first call returns exactly what the thousandth does.

02

Stable on every machine

Byte-identical output across Linux, macOS and Windows, Python 3.10–3.14, CPU architectures and locales. Positions count code points, never bytes.

03

Errors are API

Every error has a stable code and message. Patch releases never change output, so snapshot tests of error behaviour keep passing.

04

Fails loudly

No repair modes. No trailing-comma guessing, no type coercion, no ignored schema keywords. If it is wrong, you find out where.

Read the full determinism contract →

Live playground

Try the real engine. In your browser.

This isn't a mock. beacon's Rust core is compiled to WebAssembly and runs right here, the same code the Python package runs. Break the JSON, hit Run 1,000×, and count the unique outputs.

Paste raw model output. beacon finds the JSON in fences or prose, or tells you exactly where it is broken.

Output
Loading beacon's Rust core (WebAssembly)…
IdleSame Rust core as the Python package, compiled to WebAssembly

Open the full-screen playground →

Snapshot testing

Test LLM apps like normal code.

Model output isn't deterministic. What your code does with a given output is. Record a response once and snapshot your parsing, validation and routing. Every behaviour change then shows up as a readable diff in review.

  • First run writes, later runs compare byte for byte.
  • CI fails on missing or changed snapshots, on Linux, macOS and Windows.
  • Errors are snapshottable: type, code, message, position.
  • One command to accept intended changes: python -m beacon.snapshots update
Snapshot testing guide →
tests/test_intents.py
from beacon import extract_json
from beacon.snapshots import assert_snapshot

def test_cancel_intent():
    raw = load_fixture("cancel.txt")     # a recorded model response
    result = extract_json(raw, schema=INTENT)
    assert_snapshot("intents/cancel", result, input=raw, schema=INTENT)
snapshots/intents/cancel.yaml+6 −3
 # beacon snapshot v1 name: intents/cancel input: |-   Sure! ```json   {"intent": "cancel", "confidence": 0.93}   ``` value:-  intent: cancel+  intent: cancellation   confidence: 0.93 schema_validation:-  passed: true-  errors: []+  passed: false+  errors:+    - path: "/intent"+      keyword: enum
✕ SnapshotMismatchError: snapshot 'intents/cancel' does not match. Run python -m beacon.snapshots update if the change is intended.
Performance

Microseconds, not milliseconds.

The blocks are compiled Rust, so they barely touch your latency budget. The project's target is under 0.5 ms per call, and every block comes in around 50× under it. Determinism doesn't have to cost anything.

50×headroom under the
0.5 ms per-call budget
Get started

From install to a tested pipeline in five minutes.

  1. 1

    Install

    One abi3 wheel per platform covers Python 3.10 to 3.14. No Rust toolchain needed.

    pip install llm-beacon
    # optional integrations
    pip install "llm-beacon[langchain]"
  2. 2

    Wrap your model call

    Render the prompt, call any LLM client, then extract and validate the result.

    from beacon import template, extract_json
    
    prompt = template("Classify: <m>{m}</m>", m=message)
    raw = llm(prompt)                # any client
    intent = extract_json(raw, schema=INTENT)
  3. 3

    Lock it in with a snapshot

    Record a real response once. From then on, any behaviour change shows up as a diff.

    from beacon.snapshots import assert_snapshot
    
    def test_cancel():
        assert_snapshot("intents/cancel",
                        extract_json(RECORDED, schema=INTENT))

Make the model the
only moving part.

Get started