multi-agent-lab / src /core /ledger_factory.py
agharsallah
feat: require live infrastructure for event store and inference, removing offline mode
eed2172
Raw
History Blame Contribute Delete
2.37 kB
"""Backend selection for the event ledger β€” the durable store is required.
One decision lives here: which ``Ledger`` backend to construct. The append-only
ledger is the single source of truth (ADR-0014); this only chooses *where* it is
durably stored.
A ``DATABASE_URL`` (Postgres/Neon, or any SQLAlchemy URL β€” e.g. ``sqlite://`` for
an in-memory store in tests) is **required**: the app persists to a real event
store and refuses to run without one. Construction raises when no URL is
resolved rather than silently degrading to an ephemeral in-memory ledger.
"""
from __future__ import annotations
import os
from src.core.ledger import Ledger
def database_url() -> str | None:
"""Return a non-empty ``DATABASE_URL`` from the environment, else ``None``."""
url = os.getenv("DATABASE_URL")
return url or None
def _normalize_db_url(url: str) -> str:
"""Steer a bare Postgres URL to the installed psycopg3 driver.
Neon (and most providers) hand out ``postgresql://`` / ``postgres://``, which
SQLAlchemy maps to psycopg2 β€” but this project ships psycopg3, so a
copy-pasted Neon URL would fail with a missing-driver error.
Rewrite the bare scheme to ``postgresql+psycopg://``; URLs that already name a
driver (``postgresql+...``) or use another backend (sqlite, …) pass through.
"""
for scheme in ("postgresql://", "postgres://"):
if url.startswith(scheme):
return "postgresql+psycopg://" + url[len(scheme) :]
return url
def make_ledger(url: str | None = None) -> Ledger:
"""Construct the durable ledger backend (required β€” never an in-memory fallback).
*url* overrides ``DATABASE_URL`` (useful for tests/scripts β€” pass ``"sqlite://"``
for an ephemeral in-memory store). Raises :class:`RuntimeError` when neither is
set: the app requires a real event store and must not silently run without one.
"""
resolved = url or database_url()
if not resolved:
raise RuntimeError(
"DATABASE_URL is required β€” the event store is not optional. "
"Set DATABASE_URL (e.g. a Neon postgresql:// URL, or sqlite:///runs/events.db for "
"a local file), or pass an explicit url to make_ledger()."
)
from src.core.sqlalchemy_ledger import SqlAlchemyLedger
return SqlAlchemyLedger(_normalize_db_url(resolved))