-
Type:
Improvement
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: ABX
-
None
-
Python Drivers
-
None
-
None
-
None
-
None
-
None
-
None
TL;DR
Add a MongoDB-backed persistence option to the llama-agents workflow server so users can run a durable WorkflowServer on MongoDB, the same way they can today on SQLite or Postgres. This is a single, self-contained addition to the llama-agents-server package: one new store class that implements the existing persistence interface, plus MongoDB driver-handshake metadata so the usage is identifiable in server-side telemetry.
Context
What the product is. llama-agents is an open-source framework for building and running AI agent workflows (event-driven, async Python programs). The llama-agents-server package wraps a workflow as a web service (WorkflowServer) that exposes it over HTTP. To survive process restarts, the server persists its runtime data to a database.
Concepts you need (defined before use):
- Workflow run — one execution of a workflow, identified by a run_id.
- Handler — the server-side record tracking a run's status (running, completed, failed), its result, and timestamps.
- Event — a message the workflow emits during a run. Events form an ordered log per run (a monotonic sequence number), which the server replays and streams to clients.
- Tick — one step of the workflow's internal execution loop, also stored as an ordered log per run.
- State store — a per-run key/value store holding the workflow's own Context state (serialized as JSON), separate from the event/tick logs.
- Workflow store — the umbrella persistence backend that owns all of the above (handlers + events + ticks + per-run state stores). This is the pluggable piece.
The problem. Today the framework can persist to SQLite (single-process) or Postgres (via the DBOS runtime, for distributed/production). It cannot use MongoDB. There is a clean, documented extension point — an abstract base class every backend implements — but no MongoDB implementation ships. Users who standardize on MongoDB currently cannot back the workflow server with it.
Desired User Experience
The following four scenarios show what a user writes today versus what they should be able to write once this ticket ships. In every case, only the store construction line changes; the rest of the app is identical.
Scenario 1 — Ephemeral (no change; shown for baseline).
from llama_agents.server import WorkflowServer # No workflow_store -> in-memory (MemoryWorkflowStore); state lost on restart. server = WorkflowServer() server.add_workflow("greet", greet_wf)
Scenario 2 — Durable, single process.
# BEFORE — the only zero-setup durable option is a local SQLite file from llama_agents.server import WorkflowServer, SqliteWorkflowStore store = SqliteWorkflowStore(db_path="workflows.db") # local file on disk server = WorkflowServer(workflow_store=store) server.add_workflow("greet", greet_wf)
# AFTER — the same durability, but backed by MongoDB from llama_agents.server import WorkflowServer, MongoWorkflowStore store = MongoWorkflowStore( # store owns the connection uri="mongodb://localhost:27017", database="llama_agents", ) server = WorkflowServer(workflow_store=store) server.add_workflow("greet", greet_wf) # ^ Only the store line changed. Handlers, events, ticks, and per-run # Context state now persist to MongoDB and survive a restart.
Scenario 3 — Production / distributed (multiple server replicas sharing one database).
# BEFORE — the only shared-database option is Postgres via the DBOS runtime from dbos import DBOS from llama_agents.dbos import DBOSRuntime from llama_agents.server import WorkflowServer DBOS(config={ "name": "my-app", "system_database_url": "postgresql://user:pass@localhost:5432/mydb", "run_admin_server": False, }) runtime = DBOSRuntime() server = WorkflowServer( workflow_store=runtime.create_workflow_store(), runtime=runtime.build_server_runtime(), ) server.add_workflow("greet", greet_wf)
# AFTER — a MongoDB deployment (e.g. a replica set) shared by multiple replicas, # without pulling in DBOS/Postgres at all from llama_agents.server import WorkflowServer, MongoWorkflowStore store = MongoWorkflowStore( uri="mongodb://user:pass@host1:27017,host2:27017/?replicaSet=rs0", database="llama_agents", ) server = WorkflowServer(workflow_store=store) server.add_workflow("greet", greet_wf) # ^ Every replica constructs the same store pointing at the same database; # they share handler/event/tick state, matching today's shared-Postgres story.
Scenario 4 — Bring-your-own client (reuse an existing connection).
# AFTER — inject a pre-built client the app already owns from pymongo import AsyncMongoClient from llama_agents.server import WorkflowServer, MongoWorkflowStore client = AsyncMongoClient("mongodb://localhost:27017") # caller owns this client store = MongoWorkflowStore(client=client, database="llama_agents") server = WorkflowServer(workflow_store=store) # ^ The store reuses the caller's client and does NOT override its handshake # metadata. (When the store is given a `uri` instead, it creates its own # client and stamps a component-distinct driver name on it — see AC below.)
Task
Goal:
Let users run the llama-agents workflow server on MongoDB, just like they can on SQLite or Postgres today — for both single-server and multi-server setups — so teams already on MongoDB don't need a second database.
Where:
- Package to change: packages/llama-agents-server
- The interface to implement: _store/abstract_workflow_store.py
- Reference implementations to mirror: sqlite/ and postgres_workflow_store.py (plus their state-store companions sqlite_state_store.py / postgres_state_store.py). PostgresWorkflowStore already models the two-mode connection pattern (owns a pool from a dsn, or reuses an injected pool); mirror that for MongoDB's uri vs. client.
- Public export list: server/{}init{}.py
- Docs to update — GitHub source: deployment.md (live page for reference: Persistence section)
(Product) Acceptance Criteria
- [ ] Passes the equivalent of the existing store suites, adapted for MongoDB — test_sqlite_workflow_store.py, test_postgres_workflow_store.py, test_workflow_store_events.py, test_sqlite_state_store.py, test_postgres_state_store.py.
- [ ] MongoWorkflowStore imports from llama_agents.server and the persistence docs show a runnable MongoDB example.
- [ ] A follow-up DOCSP ticket has been created to document this feature
- [ ] Client metatada has been implemented with the value llamaindex-agents
References
MongoDB documentation (required reading for the implementation):
- PyMongo async driver — AsyncMongoClient — the async client the store constructs (or receives) for all reads/writes.
- MongoDB handshake spec — wrapping-library metadata — the contract behind the "attributable in telemetry" criterion; defines how a wrapping library appends a component-distinct driver name during the handshake.
- PyMongo DriverInfo — the API used to stamp that driver name onto a store-created client (leave an injected client's metadata untouched).
- MongoDB change streams — the native mechanism to consider for live event streaming (the alternative to polling), analogous to Postgres LISTEN/NOTIFY in the reference store.
- Unique indexes — for enforcing the per-run monotonic sequence on events/ticks, mirroring the unique-constraint retry logic in the SQL stores.
Additional in-repo code (not linked above):
- _pool.py — the PoolProvider ownership abstraction (create vs. borrowed) that PostgresWorkflowStore uses to decide whether it owns or reuses a connection; the model for MongoDB's uri-owns vs. client-injected split.
- memory_workflow_store.py — the in-memory reference backend, including its condition-based (non-polling) subscribe_events override worth mirroring for live streaming.
- WorkflowServer (server.py) — where workflow_store is injected, confirming there is no connection-string dispatcher to modify.
- state_store.py (SDK protocol) — the per-run Context state protocol the store's state-store companion must satisfy; defined in llama-index-workflows and requires no change.