extensions/memory: add Google Cloud Firestore session backend#2955
extensions/memory: add Google Cloud Firestore session backend#2955Kcstring wants to merge 1 commit intoopenai:mainfrom
Conversation
Add FirestoreSession, a production-grade session backend backed by Google Cloud Firestore. Conversation items are stored as documents in a messages sub-collection; a monotonic sequence counter on the parent session document (updated via a Firestore transaction) guarantees strict insertion order across concurrent writers. Public API mirrors the existing MongoDB/Redis backends: - FirestoreSession(session_id, *, client, ...) - FirestoreSession.from_project(session_id, *, project, ...) - get_items / add_items / pop_item / clear_session / close / ping Also adds: - firestore optional dependency group in pyproject.toml - 14 unit tests (no real Firestore server required)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4cfa66a775
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| @self._client.transaction() # type: ignore[arg-type] | ||
| async def _txn(transaction: Any) -> int: |
There was a problem hiding this comment.
Use Firestore transaction wrapper API correctly
In add_items, _txn is decorated with @self._client.transaction(), but AsyncClient.transaction() returns an AsyncTransaction object rather than the transactional decorator API, so this pattern fails against the real Firestore client and prevents message inserts from succeeding. The official async usage is to create a transaction object and wrap the coroutine with async_transactional (then call it with the transaction), so this code path can raise before any write and make the new FirestoreSession unusable in production.
Useful? React with 👍 / 👎.
|
Thanks for sharing this. While we do not include this module in the core SDK, please feel free to publish the code as your own package or use it in your own projects. |
Summary
Add
FirestoreSession— a production-grade session backend backed by Google Cloud Firestore — toagents.extensions.memory.Motivation: The SDK already ships Redis, MongoDB, SQLAlchemy, Dapr, and SQLite session backends. Firestore is a natural addition for teams running on Google Cloud Platform: it is serverless, scales automatically, and is widely used in GCP-native applications. This PR follows the same pattern established by the recently merged MongoDB backend.
Design:
messagessub-collection under each session document._seqcounter on the parent session document is incremented atomically via a Firestore transaction, guaranteeing strict insertion order across concurrent writers and processes.from_project(session_id, *, project)is a convenience constructor that creates anAsyncClientfrom a GCP project ID using Application Default Credentials.AsyncClientis also supported for applications that manage the client lifecycle themselves.Files changed:
src/agents/extensions/memory/firestore_session.py— newFirestoreSessionclasssrc/agents/extensions/memory/__init__.py— exportFirestoreSessionwith lazy import + helpful error messagepyproject.toml— addfirestore = ["google-cloud-firestore>=2.19"]optional dependency grouptests/extensions/memory/test_firestore_session.py— 14 unit tests (no real Firestore server required; uses in-process fakes)Test plan
All tests run without a real Firestore server by injecting lightweight in-memory fakes into
sys.modulesbefore the module under test is imported — the same approach used by the MongoDB test suite.Issue number
Checks
make lintandmake format