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

Storage Adapters

A storage adapter is the backend that holds your audit trail — the durable home for every decision Briefcase captures, and the surface you query when someone asks why a decision was made.

storage For: platform & governance

How it works

  1. Init — call briefcase.init() once and construct a backend.

  2. Create — build a DecisionSnapshot for each classify_ticket call.

  3. Savesave_decision() persists the record and returns its id.

  4. Query — pull records back with a SnapshotQuery when you need to review them.

flowchart LR
    A[classify_ticket] --> B[DecisionSnapshot]
    B --> C[SqliteBackend]
    C --> D[(Audit trail)]
    E[Reviewer query] --> C

Install

Terminal window
pip install briefcase-ai[storage]

Which backend should I use?

BackendPersistenceReach for it when
SqliteBackend.in_memory()No — gone on exitTests and local experiments, where no durable audit trail is needed
SqliteBackend("path.db")Yes — a file on diskA single node where you want a real, queryable audit trail
BufferedBackendYes — wraps another backendHigh write volume, where batching save_decision calls matters

BufferedBackend is not a separate store — it wraps a durable backend (such as SqliteBackend) and batches save_decision calls until the buffer fills, so you trade a small flush delay for less write pressure under load.

Init -> create -> save -> query

  1. Init the runtime and backend

    import briefcase
    from briefcase.storage import SqliteBackend
    briefcase.init() # start the native runtime once per process
    backend = SqliteBackend("decisions.db")
  2. Create a decision for each classify_ticket call.

    from briefcase import DecisionSnapshot, Input, Output
    decision = DecisionSnapshot("classify_ticket")
    decision.add_input(Input("text", "reset my password", "string"))
    output = Output("category", "account_access", "string")
    output.with_confidence(0.92)
    decision.add_output(output)
    decision.add_tag("queue", "support")
  3. Save the record — save_decision() returns its id.

    decision_id = backend.save_decision(decision)
    loaded = backend.load_decision(decision_id)
    print(loaded.function_name) # classify_ticket
  4. Query the audit trail with a SnapshotQuery.

    from briefcase import SnapshotQuery
    results = backend.query(
    SnapshotQuery()
    .with_function_name("classify_ticket")
    .with_tag("queue", "support")
    )
    print(len(results))

briefcase.init() must be called once before using a backend to start the native runtime.

Backends in detail

In-memory (for tests)

SqliteBackend.in_memory() keeps data in memory — fast and ephemeral, the right choice for tests where you do not need records to survive the process.

import briefcase
from briefcase.storage import SqliteBackend
briefcase.init()
backend = SqliteBackend.in_memory()

File on disk (for a real audit trail)

SqliteBackend(path) writes to a file — a durable, queryable audit trail in one place, the workhorse for single-node deployments.

import briefcase
from briefcase.storage import SqliteBackend
briefcase.init()
backend = SqliteBackend("decisions.db")
print(backend.health_check()) # True

Buffered (for high volume)

BufferedBackend wraps a durable backend and batches save_decision calls until the buffer fills.

import briefcase
from briefcase.storage import SqliteBackend, BufferedBackend
from briefcase import DecisionSnapshot, Input
briefcase.init()
backend = BufferedBackend(SqliteBackend("decisions.db"), buffer_size=100)
decision = DecisionSnapshot("classify_ticket")
decision.add_input(Input("text", "update my address", "string"))
backend.save_decision(decision)

A governance query: load decisions for review

The point of a durable backend is the review it enables. When a reviewer asks for last week’s support-queue decisions, you answer with a tagged SnapshotQuery against the same store that captured them.

import briefcase
from briefcase.storage import SqliteBackend
from briefcase import SnapshotQuery
briefcase.init()
backend = SqliteBackend("decisions.db")
# Pull every support-queue triage decision for review
query = (
SnapshotQuery()
.with_function_name("classify_ticket")
.with_tag("queue", "support")
.with_limit(50)
.with_offset(0)
)
for decision in backend.query(query):
# hand each record to a reviewer, or replay it to verify
...

SnapshotQuery supports with_function_name, with_module_name, with_tag, with_limit, and with_offset. From here a reviewer can audit a decision or replay it to reproduce exactly what happened.

Snapshots: grouping multiple decisions

A Snapshot groups several decisions; save() returns the snapshot id and load() returns it.

import briefcase
from briefcase.storage import SqliteBackend
from briefcase import DecisionSnapshot, Input, Snapshot
briefcase.init()
backend = SqliteBackend.in_memory()
decision = DecisionSnapshot("classify_ticket")
decision.add_input(Input("text", "where is my order", "string"))
session = Snapshot("session")
session.add_decision(decision)
snapshot_id = backend.save(session)
restored = backend.load(snapshot_id)
print(len(restored.decisions)) # 1

The persistence interface

SqliteBackend exposes the full interface (BufferedBackend only buffers save_decision calls before flushing them to the backend it wraps):

backend.save(snapshot) # store a Snapshot, returns its id
backend.load(snapshot_id) # load a Snapshot
backend.save_decision(decision) # store a DecisionSnapshot, returns its id
backend.load_decision(decision_id)
backend.query(snapshot_query) # run a SnapshotQuery
backend.delete(snapshot_id)
backend.health_check()

Available backends

BackendClassDescription
SQLiteSqliteBackendLocal SQLite database (file or in-memory)
BufferedBufferedBackendWraps a backend and batches writes

Where this fits

Storage is the Store & Query act: the durable home for everything Capture produced, and the surface the later acts read from.