[v2] feat: Add TIAMAT persistent cloud memory backend#2528
[v2] feat: Add TIAMAT persistent cloud memory backend#2528toxfox69 wants to merge 3 commits intoopenai:mainfrom
Conversation
Adds a TiamatSession class that provides persistent, cloud-based cross-session memory for OpenAI Agents via https://memory.tiamat.live. Features: - Zero-infrastructure persistent memory (no Redis/SQLite setup) - FTS5 full-text search across conversation history - Knowledge triples for structured memory - Cross-device agent memory via cloud API - Drop-in replacement for default session handling Addresses openai#832 (mem0 integration request) and openai#2244 (context compaction) by providing a lightweight, API-based memory solution with built-in summarization support. Includes: - examples/tiamat_memory/tiamat_session.py - Core TiamatSession class - examples/tiamat_memory/agent_with_memory.py - Usage example - examples/tiamat_memory/README.md - Documentation
Addresses code review feedback: - P1: pop_item() no longer calls get_items() while holding the lock. Extracted _get_items_unlocked() to avoid re-acquiring the non-reentrant asyncio.Lock, which would deadlock. - P1: get_items() now respects clear_session markers. Parses all items in two passes: first finds the latest _tiamat_clear marker sequence, then filters to only return items stored after that clear point. - P2: When no limit is requested (limit=None, session_settings.limit=None), fetch_limit now defaults to 10000 instead of hardcoded 100, ensuring full conversation history is retrievable.
|
This is a resubmission of #2525 with all review feedback addressed. The three issues flagged by Codex (lock deadlock in pop_item, clear_session marker not respected, hardcoded limit cap) have all been fixed. Happy to address any additional feedback. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f194cbc2bc
ℹ️ 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".
| items = await self._get_items_unlocked(session_limit=None) | ||
| if not items: | ||
| return None | ||
| return items[-1] |
There was a problem hiding this comment.
Make
pop_item remove the returned memory
pop_item currently reads the tail item and returns it, but it never persists any deletion/tombstone, so repeated calls return the same record forever. This violates the SessionABC.pop_item contract and can break retry cleanup paths that rely on destructive pops (for example rewind_session_items in src/agents/run_internal/session_persistence.py loops until items are removed), causing retries to stall when more than one item must be rewound.
Useful? React with 👍 / 👎.
| await self._client.post( | ||
| "/api/memory/store", | ||
| json={ | ||
| "content": json.dumps({"_tiamat_clear": True, "_tiamat_ts": time.time()}), |
There was a problem hiding this comment.
Write clear markers with sequence metadata
clear_session stores a marker without _tiamat_seq, but _get_items_unlocked computes last_clear_seq via item.get("_tiamat_seq", 0) and keeps all items with seq > last_clear_seq. In sessions with existing history, this means clear usually hides only sequence 0 and leaves most prior messages visible, so clear_session does not actually clear conversation state.
Useful? React with 👍 / 👎.
| """Get raw memories for this session.""" | ||
| resp = await self._client.post( | ||
| "/api/memory/recall", | ||
| json={"query": self._make_tag(), "limit": 100}, |
There was a problem hiding this comment.
Stop capping raw-memory fetch at 100 for sequencing
add_items derives the next _tiamat_seq from len(existing), but _get_raw_memories always recalls only 100 entries. After a session exceeds 100 memories, new writes reuse sequence numbers around 100, which breaks ordering assumptions in get_items/clear filtering/pop logic that depend on monotonically increasing _tiamat_seq values.
Useful? React with 👍 / 👎.
…ry limit Addresses all 3 review comments from Codex bot: 1. pop_item now stores a tombstone marker (_tiamat_popped) before returning, so the item is excluded from future reads. This prevents infinite loops in rewind_session_items which calls pop_item repeatedly until items are removed. 2. clear_session now computes the next sequence number and includes _tiamat_seq in the clear marker. Previously the marker had no seq, defaulting to 0, which only hid seq-0 items. 3. Replaced _get_raw_memories (hardcoded limit:100) with _next_seq_unlocked that scans all memories (limit:10000) to find the true max sequence number. Prevents seq collisions after 100 items. Also refactored internal parsing into _fetch_active_items_unlocked which handles clear markers, pop tombstones, and ordering in one pass. Added test_tiamat_session.py with 8 tests covering: - pop_item is destructive (key contract test) - pop_item removes from tail one at a time - clear_session hides all items regardless of seq - sequence numbers work past 100 items - no internal metadata leaks in returned items Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
toxfox69
left a comment
There was a problem hiding this comment.
Thanks for the thorough review — all three issues fixed in 26ec52b.
Fix 1 (P1): pop_item now destructive — stores a _tiamat_popped tombstone marker with the item's sequence number before returning. _fetch_active_items_unlocked() filters out items matching any pop tombstone, so rewind_session_items terminates correctly.
Fix 2 (P1): clear_session now includes _tiamat_seq — computes the next available sequence number via _next_seq_unlocked() and stores it on the clear marker. All items with seq < clear_seq are now properly filtered.
Fix 3 (P2): Removed hardcoded limit:100 — replaced _get_raw_memories() with _next_seq_unlocked() which scans all memories (limit:10000) to find the true max sequence number. Prevents seq collisions after 100 items.
Refactored: extracted _fetch_active_items_unlocked() which handles clear markers, pop tombstones, and ordering in one pass. Both _get_items_unlocked and pop_item use it.
Added 8 tests in test_tiamat_session.py using a mock HTTP transport — no network required. Key test: test_pop_item_is_destructive verifies two consecutive pops on a single-item session return the item then None.
|
@toxfox69 As I mentioned at #2525 (comment) , we won't have this example as part of this repo. Please have your own repo for it. |
Summary
TiamatSession — a persistent cloud memory backend for the OpenAI Agents SDK via TIAMAT Memory API. Zero-infrastructure, cross-session agent memory with FTS5 full-text search.
SessionABCinterfacehttpx— no Redis, SQLite, or infrastructure setupThis is a v2 of #2525, addressing all review feedback:
Fix 1 (P1):
pop_itemdeadlockpop_item()acquiredself._lock, then calledself.get_items()which tried to acquire the sameasyncio.Lock— deadlock (non-reentrant lock)._get_items_unlocked()helper. Bothget_items()andpop_item()call this internal method —get_items()acquires the lock first,pop_item()calls it while already holding the lock.Fix 2 (P1):
clear_sessionmarker not respectedclear_session()stored a_tiamat_clearmarker, butget_items()never filtered on it — old items returned after clear._get_items_unlocked(): first pass finds latest_tiamat_clearsequence number, second pass filters to only return items after that clear point.Fix 3 (P2): Hardcoded
limit:100truncating historysession_limit or 100silently capped at 100 entries when no limit was set.session_limit if session_limit and session_limit > 0 else 10000— generous default ceiling that respects explicit limits.Files
examples/tiamat_memory/tiamat_session.py— Core session class (all fixes applied)examples/tiamat_memory/agent_with_memory.py— Working exampleexamples/tiamat_memory/README.md— DocumentationTest Plan
pop_itemcompletes without deadlock under concurrent accessget_itemsreturns empty list afterclear_session