a2aproject / a2aproject/a2a-python

[Feat]: NoSQL (MongoDB) TaskStore / PushNotificationConfigStore backend — production-verified reference implementation available

Aperta
#1,210 2 commenti 0 reazioni 1 assegnatario Rivendicata da @ishymko Vedi su GitHub
component: server status: needs review
Lingua principale
Python
Stelle
2.1k
Fork
496
Merge medio
4g 17h
PR unite (30g)
12

Descrizione

## Summary

The SDK currently ships two `TaskStore` backends: `InMemoryTaskStore` and the SQLAlchemy-based `DatabaseTaskStore` (`a2a-sdk[sql]`). There is no supported path for document/NoSQL databases, even though task records are a natural fit for them. We built and deployed a MongoDB-backed `TaskStore` + `PushNotificationConfigStore` for a production A2A server (FastAPI, multi-replica) and would like to propose upstreaming a generic version — either as an optional extra (e.g. `a2a-sdk[mongodb]`) or as a documented reference implementation.

## Why a document store fits this data

- **Tasks are ephemeral, retention-bound records.** They are written per request, re-read for a bounded window (`GetTask`/`ListTasks`), and then expire. MongoDB TTL indexes handle retention natively (one index on an `updated_at` field); with SQL backends every deployment has to build its own cleanup job (cron / `EVENT` scheduler / partition rotation).
- **Many A2A server deployments already run a document store** for conversation state, and adding a relational database + Alembic migrations solely for task persistence is a heavy dependency for what is a single-collection workload.

## Implementation notes (lessons that may save others time)

We verified these against SDK 1.1.2 semantics; happy to carry them into a PR:

1. **Store the `Task` as serialized proto bytes, not ProtoJSON.** `Task.metadata` is a proto `Struct` with arbitrary client-supplied keys — keys containing `.` or `$` become invalid MongoDB document keys after `MessageToDict`. Storing `task.SerializeToString()` as opaque bytes (plus a handful of extracted top-level fields for querying: owner, `context_id`, `status.state`, `status.timestamp`) is exact, round-trip safe, and resilient to unknown fields across SDK upgrades.
2. **Owner partitioning parity.** All reads/writes are scoped by `owner_resolver(context)` (default `resolve_user_scope`), matching `InMemoryTaskStore` — including refusing cross-owner access on `get`/`delete`.
3. **Pagination matching in-memory semantics.** Sort `(status.timestamp desc — nulls last, id desc)`; the page token (`encode_page_token`) is the id of the first item of the requested page (inclusive anchor), so the store re-resolves the anchor document to build a `(timestamp, id)` cursor filter; unknown tokens raise `InvalidParamsError`. MongoDB's BSON ordering (`Date > Null` under descending sort) reproduces the in-memory null-handling exactly.
4. **`get_info_for_dispatch` must be overridden** on custom `PushNotificationConfigStore` implementations (the base fallback silently drops notifications for authenticated owners — the docstring warns about this, but it is easy to miss). Since streaming turns call it once per emitted event, we front it with a short (5s) in-process memo keyed by `task_id`, invalidated on `set_info`/`delete_info`.

The whole thing is ~200 lines for both stores. Deployed behind a FastAPI JSONRPC server; verified: task survives process restart and is served by a different replica, `ListTasks` cursor pagination over 100+ tasks, push configs dispatching across replicas.

## Questions for maintainers

1. Is there interest in an official NoSQL backend, and if so, would you prefer an optional extra (`a2a-sdk[mongodb]`, pymongo async client) or a documented recipe in the docs/examples?
2. If an extra: should it mirror `DatabaseTaskStore`'s constructor conventions (`create_table` → `ensure_indexes`, `owner_resolver` injection), and should retention (TTL) be part of the contract or left to operators?
3. Would you want the same treatment for `PushNotificationConfigStore` in the same PR?

If maintainers are open to it, we're happy to submit the PR with tests (the store semantics above are covered by a small in-memory fake collection in our suite; we can port those tests).

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.