feat(extensions): add MongoDB session backend#2902
feat(extensions): add MongoDB session backend#2902alexbevi wants to merge 7 commits intoopenai:mainfrom
Conversation
Implements MongoDBSession under src/agents/extensions/memory/ following the extensions directory structure established in issue openai#1328. Uses pymongo>=4.13's native AsyncMongoClient (pymongo.asynchronous) rather than Motor. - Two-collection schema: agent_sessions (metadata) + agent_messages (items) - Chronological ordering via ObjectId; compound index on (session_id, _id) - Idempotent one-shot index creation with per-key asyncio.Lock - from_uri() factory with owned-client lifecycle tracking - Supports SessionSettings.limit and explicit per-call limit overrides - Gracefully skips corrupted/missing message_data documents - ping() / close() lifecycle helpers; close() only touches owned clients Adds 26 tests in tests/extensions/memory/test_mongodb_session.py using in-process fake pymongo types injected via sys.modules — no real MongoDB or pymongo installation required to run the suite. Install: pip install openai-agents[mongodb] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements both client metadata patterns per the add-client-metadata skill: - Pattern A (from_uri): passes driver=_DRIVER_INFO to AsyncMongoClient via client_kwargs.setdefault(), so caller-supplied driver values are preserved. - Pattern B (injected client): calls client.append_metadata(_DRIVER_INFO) in __init__ guarded by hasattr, compatible with PyMongo <4.14. _DRIVER_INFO is a module-level DriverInfo(name="openai-agents", version=...) where version is resolved at runtime via importlib.metadata. Adds three new tests covering both patterns and the no-overwrite guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c2a083d9f
ℹ️ 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".
- P1: Use client.close() (sync) instead of aclose() when closing an owned AsyncMongoClient; PyMongo does not expose aclose(). - P1: Include id(client) in _init_key so that different AsyncMongoClient instances pointing at different clusters each run their own index- creation pass rather than sharing a single guard. - P2: Add TypeError to the except tuple in get_items() and pop_item() so non-string BSON message_data values (e.g. int/object) are silently skipped rather than aborting history retrieval. Adds two new tests: test_non_string_message_data_is_skipped (P2) and test_different_clients_each_run_index_init (P1). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 436289f769
ℹ️ 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".
With pymongo now installed in dev deps, mypy resolves the real async types so several ignore codes were wrong or unused: - Make FakeAsyncMongoClient.close() async (MongoDBSession awaits it) - Change method-assignment ignores from [attr-defined] to [method-assign] - Add [assignment] to admin.command monkey-patch (overloaded function) - Remove ignores on _docs access and create_index reads that mypy now handles via the Any type parameter on AsyncCollection[Any] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 054d391340
ℹ️ 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".
- Replace asyncio.Lock process-wide guard with threading.Lock so _get_or_create_init_lock() is safe to call from any event loop - Replace id(client)-keyed dicts with WeakKeyDictionary so entries are pruned when clients are GC'd, preventing stale id reuse from skipping index creation on a new client - Add explicit seq field (monotonic per-session counter via $inc) to every message document and sort by seq instead of _id; ObjectId is only second-level accurate across processes and not reliably monotonic - Update FakeAsyncCollection in tests with find_one_and_update + $inc support; replace _initialized_keys/_init_locks clears with _init_state Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b166de716
ℹ️ 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".
…only guard The per-entry asyncio.Lock stored in _init_state was still loop-bound: a second event loop reusing the same AsyncMongoClient would hang or raise RuntimeError trying to acquire it. create_index is idempotent on the server side, so no async coordination is needed — concurrent first-time callers may each issue a redundant create_index round-trip, but that is harmless. Replace the asyncio.Lock with a plain bool guarded by the existing threading.Lock, removing the last asyncio.Lock from the process-wide registry entirely. Also removes the now-unused asyncio import. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Adds
MongoDBSessiontosrc/agents/extensions/memory/, a MongoDB-backed session store. I followed theextensions/memorydirectory structure established in #1328 as this was suggested as a path forward on the previous (now closed) PR #1364.Notes:
agent_sessions(metadata) +agent_messages(individual conversation items), with a compound index on(session_id, _id)for efficient per-session chronological queries.(id(client), database, sessions_collection, messages_collection)key, guarded by a per-keyasyncio.Lock. The client identity is included so that differentAsyncMongoClientinstances (e.g. pointing at different clusters) each get their own init pass.from_uri()factory owns the client lifecycle; injected clients are left unmanaged (caller's responsibility).SessionSettings.limitand explicit per-calllimitoverrides, consistent with all other session backends.Install:
pip install openai-agents[mongodb]Issue number
Relates to #1364, #1328
Checks
make lintandmake format