Skip to Content
DocsRuntimeIn-proc · SQLite · Postgres

Runtime backends

InProcRuntime. The default

No journal, no durability. Crashes lose everything. Used implicitly when you don’t pass runtime=.

from loomflow import Agent agent = Agent("...", model="claude-opus-4-7") # InProcRuntime under the hood

Hot-path overhead is zero. The agent loop’s runtime.step(name, fn, *args) collapses to await fn(*args) directly.

SqliteRuntime. Single file, no infra

from loomflow import Agent from loomflow.runtime import SqliteRuntime agent = Agent( "...", model="claude-opus-4-7", runtime=SqliteRuntime("./journal.db"), )

Each call to runtime.step(...) writes a row to the journal table keyed by (session_id, step_name). On a fresh SqliteRuntime pointing at the same DB file, replaying the same session reads the cached row instead of re-executing.

The schema is a single table; no migrations needed. Multiple agents can share the same DB file (SQLite WAL handles concurrent writers).

Streaming steps

Architectures that stream model responses (runtime.stream_step(...)) journal the chunks as a JSON array. On replay, the chunks emit in order without hitting the network.

PostgresRuntime. Multi-instance durable

from loomflow import Agent from loomflow.runtime import PostgresRuntime runtime = await PostgresRuntime.connect( dsn="postgres://user:pw@host/db", schema="jeeves_journal", # optional schema namespace ) agent = Agent( "...", model="claude-opus-4-7", runtime=runtime, )

Install: pip install 'loomflow[postgres]'.

Schema migrations are idempotent. First connect runs the CREATE TABLE IF NOT EXISTS automatically.

When two instances of your service share the same Postgres journal, both can resume any session. Useful for blue/green deploys and distributed worker pools.

JournalStore. The lower-level primitive

SqliteRuntime and PostgresRuntime are convenience wrappers over JournaledRuntime + JournalStore:

from loomflow import Agent from loomflow.runtime import JournaledRuntime, SqliteJournalStore store = await SqliteJournalStore.connect("./journal.db") runtime = JournaledRuntime(store=store) agent = Agent("...", model="claude-opus-4-7", runtime=runtime)

Implementations:

ClassBacking store
InMemoryJournalStorePython dict (lost on exit)
SqliteJournalStoreSingle-file SQLite
PostgresJournalStorePostgres

Useful when you want to share a JournalStore across multiple runtimes (rare), or when you want to swap the backing store without re-wrapping the runtime.

Per-backend feature matrix

FeatureInProcSQLitePostgres
Single-process durability
Multi-instance shared journalpartial†
agent.resume(session_id, ...)
Async network setupn/aminimalyes
Hot-path overheadzerosmallnetwork roundtrip
Production-recommendeddev onlysingle instancemulti-instance

† SQLite supports multiple readers/writers via WAL mode but isn’t ideal for high-write workloads across many instances.

Don’t share InMemoryJournalStore across processes. It’s a Python dict. There’s nothing to share. The class exists for tests and for the in-proc default; don’t reach for it in production workflows that need durability.

Cleanup

The journal grows monotonically. For long-running services, expire old sessions periodically:

# SqliteRuntime await runtime.purge_before(datetime(2025, 1, 1, tzinfo=UTC)) # PostgresRuntime await runtime.purge_before(datetime(2025, 1, 1, tzinfo=UTC))

A reasonable policy: purge sessions older than 30 days, since most resumable workloads are short-lived. For audit / compliance, keep the audit log separately. That’s the permanent record.

Last updated on