Skip to main content

recorded

Wrap any computation you want queryable and replayable. @recorder makes the call's arguments, return value, and any projected fields a typed row in SQLite. From there: query the recorded space as a typed table, replay rows by idempotency key, render any row as LLM context.

It works for HTTP calls, LLM turns, and broker requests — but the general pattern is the result of a function is data you can query later. Scanners, feature pipelines, derived metrics, scoring loops — anywhere the row IS the result.

Four properties together — that's the library, and the reason it isn't a logging tool you already have:

  • Transparent at the call site. The wrapped function still returns its natural value; adding or removing @recorder is a safe edit at every call site, with no shape change for callers.
  • Idempotent by key. Pass key="..." and the second call returns the recorded response without re-running the function. Same key in another process joins the in-flight work.
  • Projected, not just logged. Declare data=Model and that slot becomes a queryable index over the full payload — the wide response stays on disk faithfully; data is the slice you'll filter on.
  • Read API shaped for analysis and LLMs. Pull rows back as typed objects, filter by data fields, or render any row as prompt-ready context with Job.to_prompt().
from recorded import recorder, query
from pydantic import BaseModel

class FileMetrics(BaseModel):
    path: str
    loc: int
    has_async: bool

@recorder(kind="scan.file", data=FileMetrics)
def measure(path: str) -> FileMetrics:
    ...   # parse, count, classify — pure computation

for py in pathlib.Path("src").rglob("*.py"):
    measure(str(py), key=str(py))   # each call is a typed row

# Now the recorded space is queryable — no recompute:
for job in query(kind="scan.file", where_data={"has_async": True}):
    print(job.data.loc, job.data.path)

Same shape works for I/O wrappers — swap measure(path) for fetch(url) or place_order(req) and the rest is identical.

The substrate is one SQLite file (jobs.db), four slots per row, a three-write lifecycle. The bare-call path above is what most apps run; durable submission via a leader process is opt-in and lives in its own Advanced section below.

Privacy: recorded writes function arguments, return values, and exceptions to SQLite verbatim. The DB at jobs.db becomes a durable record of everything wrapped — including any secrets the function handled. Use recorded.fastapi.capture_request(redact_headers=...) for HTTP request capture; for general functions, redact sensitive arguments yourself before they cross the decorator boundary. See configuration for redaction options.


What it isn't

  • A multi-process job queue. SQLite WAL handles concurrent writers fine, but cross-process JobHandle.wait() falls back to polling at 200ms cadence — don't expect Redis-grade fan-out latency.
  • A query DSL. where_data= is equality on top-level keys, by design. Anything richer goes through recorded.connection() and raw SQL — the library doesn't grow a query language.
  • A schema-versioning library. One table, one schema, frozen.
  • A distributed-tracing replacement. Use OpenTelemetry for spans across services. Use recorded when you want a row per call you can grep tomorrow.
  • A heavy dep. Stdlib only for the core. Pydantic / FastAPI / Starlette are auto-detected if installed but never imported.

Idempotency — the second call is free

@recorder(kind="orders.place")
def place_order(req): ...

place_order(req, key="order-42")    # runs the function, records the row
place_order(req, key="order-42")    # returns the cached response — no broker call

Works across processes via a partial unique index on (kind, key). Failed jobs retry by default; retry_failed=False raises JoinedSiblingFailedError carrying the prior error payload instead.

Deeper: usage/idempotency.md.

Queryable projections — data=Model + recorded.query(...)

class OrderView(BaseModel):
    order_id: str
    customer_id: int

@recorder(kind="orders.place", data=OrderView)
def place_order(req): ...

# Auto-projects from the response (or use attach() for fields not on the response).
recorded.query(kind="orders.*", where_data={"customer_id": 7})

The data slot is the queryable index — declare what you'll filter by; everything else stays in the wide response slot. The full response is recorded faithfully (the audit invariant); data is just the slice you'll grep on.

Deeper: usage/typed-slots.md.

Advanced: durable submission via leader process

Most users won't need this. The bare @recorder path above runs in the caller's process, records the row, and returns — no extra infrastructure. .submit() is the opt-in tier for "fire here, run there, wait or come back later for the result":

handle = build_report.submit(spec, key="daily-2026-04-27")

job = handle.wait_sync(timeout=60.0)        # from sync code
job = await handle.wait(timeout=60.0)       # from async code

.submit() enqueues a pending row and returns a handle; a separate leader process (python -m recorded run --import myapp.tasks) claims and executes the row. Without a leader running, .submit() raises ConfigurationError rather than silently degrading; misconfigured deployments fail loudly on the first submit. The reaper sweeps orphaned running rows on the next leader-process bootstrap, so a crashed leader doesn't leave idempotency keys held forever. recorder.is_leader_running() is the deployment health check.

Deeper: usage/workers.md.


Where to read next

The full feature tour, in roughly this order. Each guide is independent — skip to the one you need.

guide covers
usage/decorator.md the decorator, sync vs async, cross-mode shims
usage/configuration.md configure(), lifecycles, multi-recorder safety
usage/reading.md last, get, query, connection, the CLI
usage/idempotency.md key=, retry_failed, the partial-unique-index trick
usage/typed-slots.md the four slots, attach(), attach_error()
usage/queries.md recorded.query(...), raw SQL, Job.to_prompt()
usage/extension-patterns.md extension patterns built on attach() + ContextVars
usage/fastapi.md lifespan integration, capture_request, two-shape pattern
usage/workers.md .submit(), the leader process, the reaper
usage/errors.md exception hierarchy, what to catch where

Or jump in by need — see the usage guide index.

Architecture: docs/HOW.md is the contributor tour; docs/WHY.md is the authoritative spec.

Runnable examples: docs/examples/ — codebase scanner, LLM-CLI replay, FastAPI service, batch HTTP consumer, fiscal snapshot.


Install

pip install recorded
  • Python 3.10+
  • SQLite 3.35.0+ (for UPDATE ... RETURNING). The stdlib sqlite3 on most modern platforms qualifies; RHEL 8 / older Debian may need a SQLite update.
  • Pydantic is optional. Auto-detected at import time if installed.

Local development

uv venv
uv pip install -e ".[dev]"
uv run pytest

pip install -e ".[dev]" works equivalently if uv is unavailable. The test suite uses real SQLite (no stdlib mocks) and runs in ~5 seconds.

Metadata

Release files for recorded 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for recorded 0.2.0
File Size Uploaded Provenance Actions
recorded-0.2.0.tar.gz 155.9 kB With provenance Details for recorded-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for recorded 0.2.0
File Interpreter ABI Platform Actions
recorded-0.2.0-py3-none-any.whl Python 3 none any Details for recorded-0.2.0-py3-none-any.whl

Total release size: 208.6 kB

Release files / recorded-0.2.0.tar.gz

Download URL recorded-0.2.0.tar.gz
Size 155.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a9bfa37777c01360e76a2acdae714e299d201254c0c9572ca26e9d19d1d4c22f
BLAKE2b-256 checksum
How to use checksums
d2f8cb68b9f63cb004088863d8b44f0d055dc74490955e51d56fd27455af35df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 7, 2026.

Transparency log

Release files / recorded-0.2.0-py3-none-any.whl

Download URL recorded-0.2.0-py3-none-any.whl
Size 52.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6aaa613f6e9383cbcd91b1b3d49121b0d45771bd5fefb9de7d2529d7e209244f
BLAKE2b-256 checksum
How to use checksums
0a08a8ec3be1a141411c12cbe546b7e42e42656b1dd9a3c67c996a4870aa18f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 7, 2026.

Transparency log

Security for recorded 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for recorded-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page