Skip to content
Docs for briefcase-ai v3.3.0see what’s new.

Quickstart

Install

Terminal window
pip install briefcase-ai

Pick your path

Briefcase records decisions two ways. They serve two different outcomes — pick based on whether the decision needs to outlive the current process.

PathOutcomeAPIWhen to use
Live ObservabilityWatch decisions as they happen@capture + observe()Local debugging, notebooks, low-overhead logging
Persistent DecisionsReload, replay & audit laterDecisionSnapshot + a backendAnything you’ll need to reproduce, verify, or govern
  • Lightweight: wraps a function and logs its inputs, outputs, and timing.
  • Handed to an exporter (console, file, or memory) — not persisted to a backend.
  • Best for low-overhead logging and live observability.

See Core Concepts for how each record is structured.

Record, persist, and replay

  1. Record a decision (Live Observability)

    The @capture decorator records every call — inputs, outputs, and timing — and hands the lightweight record to an exporter. But @capture alone has nowhere to send what it records: briefcase.observe() is the one call that wires up that exporter. Pass "memory" to collect records in a list, "console" to print them to stderr, or a .jsonl path to append them to a file. Because @capture exports in a background thread by default, pass async_capture=False when you want the record available synchronously — for example to read it back right after the call.

    import briefcase
    mem = briefcase.observe("memory") # send captured records to memory
    @briefcase.capture(decision_type="ticket-classification", async_capture=False)
    def classify_ticket(text: str) -> str:
    # call your model here
    return "billing"
    classify_ticket("My invoice is wrong")
    print(mem.records[0])

    @capture is for low-overhead logging. To persist a structured decision that you can reload and replay, build a DecisionSnapshot and store it with a backend.

  2. Persist a snapshot (Persistent Decisions)

    from briefcase import (
    DecisionSnapshot,
    Input,
    ModelParameters,
    Output,
    init,
    )
    from briefcase.storage import SqliteBackend
    init() # start the native runtime
    decision = DecisionSnapshot("classify_ticket")
    decision.add_input(Input("ticket_text", "My invoice is wrong", "string"))
    params = ModelParameters("gpt-4o-mini")
    params.with_provider("openai")
    params.with_parameter("temperature", 0.0)
    decision.with_model_parameters(params)
    output = Output("category", "billing", "string")
    output.with_confidence(0.93)
    decision.add_output(output)
    decision.with_execution_time(12.0)
    backend = SqliteBackend.in_memory() # or SqliteBackend("./decisions.db")
    decision_id = backend.save_decision(decision)
    print(f"Recorded decision {decision_id}")

    save_decision returns the snapshot ID. Use a file path instead of in_memory() to keep decisions across runs.

  3. Replay a decision

    Re-run a stored decision and compare the result against the original output.

    from briefcase.replay import ReplayEngine
    engine = ReplayEngine(backend)
    result = engine.replay(decision_id, "strict")
    print("status:", result.status)
    print("outputs match:", result.outputs_match)
    print("execution time (ms):", result.execution_time_ms)

    ReplayResult exposes .status, .outputs_match, .replay_output, .execution_time_ms, and .policy_violations. Valid replay modes are "strict" and "tolerant".

What’s next

You’ve captured, persisted, and replayed a decision. The journey continues with controlling what runs before a decision, auditing one after the fact, and choosing where persistent decisions live.