Add DakeraSession memory backend#3725
Conversation
Add a `DakeraSession` extension that implements the `Session` protocol on top of a self-hosted Dakera memory server, following the existing extension backends (Redis/MongoDB/Dapr): a backend class under `agents.extensions.memory`, a `dakera` optional extra, tests, a runnable example, and docs. Each conversation is stored in a per-session Dakera namespace derived from `session_id`, so history is isolated per conversation and restart-safe. A monotonically increasing per-item sequence in metadata preserves ordering for `get_items`/`pop_item` across writers. - src/agents/extensions/memory/dakera_session.py: DakeraSession backend - lazy export + `dakera` optional dependency wiring - tests/extensions/memory/test_dakera_session.py: full suite with an in-memory fake client (no live server) + Runner integration - test_memory_imports.py: cover the optional-extra error path (and support top-level optional modules in the helper) - examples/memory/dakera_session_example.py + docs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b63a52769
ℹ️ 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".
| return | ||
|
|
||
| async with self._lock: | ||
| seq = await self._ensure_next_seq() |
There was a problem hiding this comment.
Allocate sequence numbers atomically across workers
When two workers keep DakeraSession instances for the same session_id, each instance caches _next_seq independently; after one worker writes a batch, another stale writer can reuse the same sequence numbers for its own batch. Because reads and pops sort primarily by seq, multi-item turns from different workers can be interleaved out of chronological order, corrupting the conversation history this backend is meant to share across workers. The sequence reservation needs to be server-side/atomic or avoid a per-instance cached counter.
Useful? React with 👍 / 👎.
| memory = memories.pop() | ||
| memory_id = memory.get("id") | ||
| if memory_id: | ||
| with contextlib.suppress(Exception): | ||
| await self._client.forget(self._namespace, memory_id) |
There was a problem hiding this comment.
Do not return an item before a confirmed delete
When two workers call pop_item() concurrently, both can fetch the same latest memory before either forget completes; this code suppresses delete failures and returns the item anyway, so a 404/race or transient Dakera error can make both callers believe they removed the same item while it may remain or be returned twice. pop_item should only return after a successful delete, or use an atomic delete-and-return operation if Dakera provides one.
Useful? React with 👍 / 👎.
Summary
Adds a
DakeraSessionmemory backend that implements theSessionprotocol on top of a self-hosted Dakera memory server (a REST service for persistent agent memory). It follows the existing extension backends (RedisSession,MongoDBSession,DaprSession): a backend class underagents.extensions.memory, an optionaldakeraextra, tests, a runnable example, and docs.Why it's a faithful
Session:session_id("{key_prefix}:{session_id}"), so histories are isolated and the samesession_idalways resolves to the same stored history —get_items/pop_item/clear_sessionare restart-safe and shareable across workers pointing at the same server.seqin its metadata, so ordering is preserved across writers regardless of server return order.from_url(...)creates and owns anAsyncDakeraClient(closed byclose()); you can also inject a client your app already manages.Usage:
Install:
pip install "openai-agents[dakera]". Run a local server with thedakera-ai/dakera-deploydocker-compose stack (server + MinIO), which listens on port 3000.Test plan
tests/extensions/memory/test_dakera_session.py— full suite (add/get roundtrip, chronological ordering across add calls,limithandling andSessionSettingsdefault,pop_itemLIFO + empty,clear_session, session isolation, custom key prefix, unicode/complex-item fidelity, corrupted-entry skipping, client lifecycle, and end-to-endRunnerintegration). It uses an in-memory fake Dakera client, so it runs with no network or live server.tests/extensions/memory/test_memory_imports.py— extended to assert the friendly optional-extra error whendakerais missing (and the helper now supports top-level optional modules).ruff format --checkandruff check(pinnedruff==0.9.2),mypy(repo config), andpytestfor the new/affected tests — all green.Issue number
N/A — new integration contribution.
Checks
make format-check,make lint,make mypy, and the new tests/reviewbefore submitting this PR