feat(agent): persist conversation tree via a Store seam + in-memory Store (Wave 2)#39
Closed
urmzd wants to merge 1 commit into
Closed
feat(agent): persist conversation tree via a Store seam + in-memory Store (Wave 2)#39urmzd wants to merge 1 commit into
urmzd wants to merge 1 commit into
Conversation
25e6229 to
daf80a7
Compare
Wire AGENT TREE PERSISTENCE behind the existing types.Store seam, testable without Postgres. - Add AgentConfig.Store + WithStore option (default nil = today's in-memory-only behavior, fully backward compatible). - runLoop persists each new node (and branch tip) to the Store as it is added, via Store.Tx so the tip never points at an unsaved node. Best-effort: errors are logged, never fatal. - NewAgent persists the root node + main branch up front when a Store is configured, giving LoadTreeFromStore an anchor before the first Invoke. - Add LoadTreeFromStore helper (Store.LoadTree + tree.FromStore) for the read/resume path. - New package agent/store/memstore: in-memory types.Store implementing the full interface (SaveNode/LoadNode/LoadChildren/LoadPath/SaveBranch/ LoadBranch/ListBranches/SaveCheckpoint/LoadCheckpoint/LoadTree/Tx) with atomic buffered transactions. Tests: memstore unit tests (round-trip, children order, path, branches, checkpoints, reachable-subtree LoadTree, Tx commit/rollback); agent multi-turn Invoke -> reconstruct tree from memstore -> assert full message history round-trips; backward-compat (nil Store) and root-on-construction.
daf80a7 to
ea28a47
Compare
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 2 — Durability spine
Wires agent tree persistence behind the existing
types.Storeseam, fully testable without Postgres. This is a bounded, green slice: the in-memory-only path is byte-for-byte unchanged, and a configured Store now durably records the conversation tree.Stacked on #38 (foundation) — merge after #38.
What landed (with tests)
agentpackageAgentConfig.Store types.Store+WithStore(...)option. Defaultnilpreserves today's in-memory-only behavior (fully backward compatible).runLooppersists every new node and its branch tip as it is added — at all fourAddChildsites (input messages, assistant message, tool-result message, handoff overlay). Persistence goes throughStore.Txso a reader never observes a branch tip pointing at an unsaved node. It is best-effort: a nil Store is a no-op, and any error is logged (slog.Warn), never fatal — a persistence failure cannot break a live run.NewAgentpersists the root node + main branch up front when a Store is configured, soLoadTreeFromStorehas an anchor even before the firstInvoke.LoadTreeFromStore(ctx, store, rootID, active)helper: the read/resume counterpart, built onStore.LoadTree+tree.FromStore. Pass the rebuilt tree toNewAgentviaWithTreeto resume a persisted session.New package
agent/store/memstoretypes.Storeimplementing the full interface:SaveNode,LoadNode,LoadChildren,LoadPath,SaveBranch,LoadBranch,ListBranches,SaveCheckpoint,LoadCheckpoint,LoadTree,Tx.LoadTreereturns only the subtree reachable fromrootID(BFS over child-order), ordered root-first — mirroring pgstore's recursive descendant query closely enough fortree.FromStore.Txbuffers writes and applies them atomically on success; on error nothing is persisted (all-or-nothing, matching pgstore's DB transaction).Tests
agent/store/memstore/memstore_test.go— table/unit tests: save/load round-trip, returned-pointer isolation, child insertion order, path, branches, checkpoints, reachable-subtreeLoadTree, andTxcommit + rollback.agent/agent_store_test.go:TestStoreMultiTurnRoundTrip— build an agent with a memstore, run twoInvoketurns, reconstruct the tree purely from the Store (LoadTreeFromStore→FromStore→LoadTree), and assert the full flattened message history round-trips message-for-message (plus content spot-checks).TestStoreNilIsBackwardCompatible— no Store: defaultnil, Invoke still populates the tree.TestStorePersistsRootOnConstruction— root node + main branch are written at construction.Green
go build ./...✅go vet ./agent/ ./agent/store/memstore/✅go test ./agent/ ./agent/store/memstore/✅ (also ran./agent/tree/)gofmtclean.go test ./...(rag/kg packages download embedding models).Deferred (TODO, beyond this slice)
agent/pgstoretransactional per-turn wiring (this slice only added the in-memory Store + the generic Store-agnostic persistence in runLoop; pgstore already implements the interface but is not yet exercised per-turn here).WithStore-driven auto-hydrate path insideNewAgent(currently hydration is explicit viaLoadTreeFromStore+WithTree, kept simple and correct).