trace-memory 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,715 @@
1
+ Metadata-Version: 2.4
2
+ Name: trace-memory
3
+ Version: 1.0.0
4
+ Summary: TRACE β€” Temporal Retrieval And Context Engine: self-healing hierarchical memory for LLM agents.
5
+ License: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/husain34/TRACE
7
+ Project-URL: Repository, https://github.com/husain34/TRACE
8
+ Project-URL: Bug Tracker, https://github.com/husain34/TRACE/issues
9
+ Keywords: llm,rag,memory,agent,context-tree,vector-database,conversation,chatbot,ai
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ License-File: NOTICE
25
+ Requires-Dist: openai>=1.0.0
26
+ Requires-Dist: python-dotenv>=1.0.0
27
+ Requires-Dist: sentence-transformers>=2.2.0
28
+ Dynamic: license-file
29
+
30
+ <div align="center">
31
+ <img src="assets/logo.png" alt="TRACE Logo" width="500">
32
+ </div>
33
+
34
+ # TRACE β€” Temporal Retrieval And Context Engine
35
+
36
+ > **Hierarchical, background tree organizing memory for long-running LLM agents.**
37
+ > Reduce context loss. Reduce broken agent tasks. Stop paying for tokens on chat history you don't need.
38
+
39
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue)](https://python.org)
40
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
41
+ [![OpenAI-compatible](https://img.shields.io/badge/LLM-OpenAI%20compatible-orange)](https://platform.openai.com/docs/api-reference)
42
+
43
+ > **This repo has two parts β€” they are completely independent:**
44
+ > - 🧠 **`trace_memory/`** β€” the lightweight memory engine. Install it, import it, and integrate it into your own app. Zero UI, zero bloat.
45
+ > - πŸ–₯️ **`nexus_terminal/`** β€” an optional demo chatbot built on top of the engine. Use it to test TRACE live, explore how it works, and run experiments. You do not need it to use TRACE.
46
+
47
+ ---
48
+
49
+ ## Table of Contents
50
+
51
+ 1. [What is TRACE?](#1-what-is-trace)
52
+ 2. [Why Standard RAG is Poorly Suited for Agent Memory](#2-why-standard-rag-is-poorly-suited-for-agent-memory)
53
+ 3. [The Architecture: How TRACE Fixes It](#3-the-architecture-how-trace-fixes-it)
54
+ - [The Foundation β€” ChatIndex & the B+Tree](#31-the-foundation--chatindex--the-btree)
55
+ - [Feature 1 β€” Multi-Path Surgical Retrieval](#32-feature-1--multi-path-surgical-retrieval)
56
+ - [Feature 2 β€” Background Tree Organizer](#33-feature-2--background-tree-organizer)
57
+ - [Feature 3 β€” Trivial Leaf Archiving](#34-feature-3--trivial-leaf-archiving)
58
+ 4. [Installation](#4-installation)
59
+ 5. [Quick Start: The Nexus Terminal](#-quick-start-the-nexus-terminal)
60
+ 6. [Full API Reference](#6-full-api-reference)
61
+ - [CTree](#61-ctree)
62
+ - [VectorDatabase](#62-vectordatabase)
63
+ - [PromptSynthesizer](#63-promptsynthesizer)
64
+ - [Data Classes](#64-data-classes)
65
+ 7. [Integration Guide](#7-integration-guide)
66
+ - [With LM Studio (local)](#71-with-lm-studio-local)
67
+ - [With OpenAI](#72-with-openai)
68
+ - [With NVIDIA NIM / any OpenAI-compatible endpoint](#73-with-nvidia-nim--any-openai-compatible-endpoint)
69
+
70
+ 8. [Edge-Case Stress Tests & Validation](#8-edge-case-stress-tests--validation)
71
+ 9. [Environment Variables](#9-environment-variables)
72
+ 10. [Dependencies](#10-dependencies)
73
+ 11. [Contributing](#11-contributing)
74
+ 12. [License](#12-license)
75
+
76
+ ---
77
+
78
+ ## 1. What is TRACE?
79
+
80
+ TRACE is a Python library that gives your LLM agent **structured, searchable, self-organizing long-term memory**.
81
+
82
+ Instead of naΓ―vely stuffing an ever-growing chat log into every prompt, TRACE organises every conversation exchange into a **hierarchical B+Tree of named topic branches**. When the agent needs context, TRACE performs a fast cosine similarity search across topic summaries and retrieves only the *surgically relevant* branches β€” not the entire history.
83
+
84
+ At rest (while the agent is not actively chatting), TRACE's **background reorganizer** evaluates the entire tree against four strict axioms and merges semantically related branches under shared parents β€” inspired by memory consolidation processes.
85
+
86
+ The result: an agent **designed to preserve cross-session constraints through hierarchical retrieval**, that reduces hallucination of stale context, and operates at **a fraction of the token cost** of sliding-window or full-history approaches.
87
+
88
+ ---
89
+
90
+ ## 2. Why Standard RAG is Poorly Suited for Agent Memory
91
+
92
+ <div align="center">
93
+ <img src="assets/architecture.png" alt="Standard RAG vs TRACE Architecture">
94
+ </div>
95
+
96
+ Standard RAG works extremely well for:
97
+ - documentation search
98
+ - knowledge bases
99
+ - code retrieval
100
+ - enterprise search
101
+
102
+ The problem isn't RAG. The problem is using RAG as persistent memory.
103
+
104
+ ### The Three Failure Modes
105
+
106
+ | Failure Mode | What Happens | Real Impact |
107
+ |---|---|---|
108
+ | **Temporal Blindness** | RAG retrieves semantically similar chunks regardless of *when* they occurred. Old, overridden decisions surface alongside current ones. | Agent contradicts itself, repeats resolved problems, or reinstates abandoned plans. |
109
+ | **Context Rot** | Sliding windows drop early messages as the conversation grows. | Constraints set in message 3 are gone by message 50. The agent forgets that Sarah is allergic to peanuts. |
110
+ | **Lossy Summarization** | Compressing history into a single paragraph erases detail and nuance. | Agent loses track of branching plans, multi-hop constraints, and edge-case handling agreed upon earlier. |
111
+
112
+ ### Why Sliding Windows Fall Short for Long-Term Memory
113
+
114
+ A fixed-size sliding window is the most common approach β€” and it works well for on-demand, single-session queries where you need detailed, verbatim access to recent exchanges. But it is fundamentally unsuited for long-term agent memory:
115
+
116
+ - **No semantic awareness**: message #1 and message #200 are weighted equally as long as they fit.
117
+ - **Guaranteed forgetting**: anything outside the window is permanently gone from the agent's context.
118
+ - **No structure**: a flat list tells the LLM nothing about *which* topics are related or *which* branch an earlier constraint belongs to.
119
+
120
+ For a simple chatbot or a document Q&A tool, a sliding window is perfectly fine. For a long-running agent performing multi-step tasks across sessions, it is catastrophic.
121
+
122
+ ### Why not MemGPT or Zep?
123
+
124
+ MemGPT is incredibly powerful, but it functions like a full operating system with tiered memory (RAM, disk) that the LLM must explicitly learn to manage via function calls. This introduces significant overhead and requires highly capable models.
125
+
126
+ TRACE is meant to be a **lightweight, drop-in component**, not a full runtime environment. It focuses specifically on modelling conversations as a hierarchical tree to natively surface multi-hop constraints without forcing the LLM to actively manage its own memory banks.
127
+
128
+ ---
129
+
130
+ ## 3. The Architecture: How TRACE Fixes It
131
+
132
+ ### 3.1 The Foundation β€” ChatIndex & the B+Tree
133
+
134
+ TRACE builds on the open-source **ChatIndex** architecture (credit: Mingtian Zhang, Ray, VectifyAI). A modified version of ChatIndex's core logic is bundled directly within TRACE, which models conversation history as a B+Tree:
135
+
136
+ - **Leaf nodes (MessageNodes)** β€” raw user/assistant exchanges.
137
+ - **Internal nodes (TopicNodes)** β€” LLM-generated topic labels and summaries for each branch.
138
+ - **Root** β€” a virtual anchor node.
139
+
140
+ Every time an exchange is added (`tree.add()`), an LLM call classifies whether the exchange continues the current topic or starts a new branch. If a new branch, the LLM selects the most appropriate parent in the ancestry chain.
141
+
142
+ This gives TRACE a **structured map of the entire conversation history** β€” not a flat log β€” with topic metadata at every branch.
143
+
144
+ **What TRACE adds to ChatIndex**: ChatIndex primarily retrieves context through a single traversal path. While effective for hierarchical exploration, information spread across multiple semantically related branches may require multiple retrieval steps. TRACE augments this with vector-based retrieval across topic summaries, allowing context from multiple branches to be surfaced simultaneously.
145
+
146
+ ---
147
+
148
+ ### 3.2 Feature 1 β€” Multi-Path Surgical Retrieval
149
+
150
+ **The problem**: A single ancestry path only captures the current conversational thread. Cross-branch constraints (e.g., "Sarah's allergy" in Branch 1, "party cake" in Branch 3) are invisible unless both branches are active.
151
+
152
+ **The solution**: Every time the agent needs to respond, `PromptSynthesizer` runs a **cosine similarity search** against the VectorDatabase of embedded topic summaries:
153
+
154
+ ```
155
+ User query β†’ embed() β†’ query vector
156
+ ↓
157
+ VDB: cosine search across ALL topic summaries
158
+ ↓
159
+ Filter: keep all nodes above base cosine threshold
160
+ ↓
161
+ Walk full ancestry of each qualifying node
162
+ ↓
163
+ Deduplicate shared ancestor nodes
164
+ ↓
165
+ Rank by similarity β†’ take Top-3 paths
166
+ ↓
167
+ Format compact multi-path context block
168
+ ```
169
+
170
+ This means the LLM receives context from **multiple relevant branches simultaneously**, not just the current thread β€” saving thousands of tokens compared to full-history injection, while synthesising information across branches that were never explicitly connected.
171
+
172
+ **The cross-branch synthesis stress test** (validated with `gpt-oss-20b` via NVIDIA NIM):
173
+
174
+ <div align="center">
175
+ <img src="assets/sarah_test.png" alt="Cross-Branch Synthesis Stress Test Terminal Output">
176
+ </div>
177
+
178
+ ```
179
+ Branch 1: "Sarah is allergic to peanuts."
180
+ Branch 2: "Weather in Tokyo?" (noise)
181
+ Branch 3: "Planning Sarah's surprise party, baking a cake."
182
+ Branch 4: "Fixing a bike tire?" (noise)
183
+ Branch 5: "Found a Thai Peanut Butter Cake recipe."
184
+
185
+ Prompt: "I'm making the Peanut Butter Cake for Sarah's party. Good idea?"
186
+
187
+ Result: The AI aggressively stopped the user.
188
+ The VDB surgically bypassed Branches 2 & 4 (noise),
189
+ retrieved Branches 1, 3, and 5,
190
+ and synthesised them to catch the life-threatening allergy conflict
191
+ β€” without any explicit link between them ever being made.
192
+ ```
193
+
194
+ ---
195
+
196
+ ### 3.3 Feature 2 β€” Background Tree Organizer
197
+
198
+ <div align="center">
199
+ <img src="assets/reorganization.png" alt="B+Tree Reorganization Diagram">
200
+ </div>
201
+
202
+ **The problem**: Long conversations naturally fragment. Topics discussed in different sessions may be semantically identical but live in separate branches, causing redundant retrieval and diluted context.
203
+
204
+ **The solution**: `tree.reorganize()` runs a conservative, four-rule-guarded merge pass:
205
+
206
+ ```
207
+ Phase 1 β€” Collect all frozen (inactive) TopicNodes
208
+ Phase 2 β€” Generate missing summaries; embed all candidates
209
+ Phase 3 β€” Compute pairwise cosine similarity
210
+ Phase 4 β€” For each pair above threshold, apply 4 axioms:
211
+ Axiom 1 β€” Chronological Guard
212
+ Axiom 2 β€” Frozen State check
213
+ Axiom 3 β€” Similarity threshold (default 0.55)
214
+ Axiom 4 β€” LLM Veto
215
+ If all 4 pass β†’ merge (newer becomes child of older)
216
+ Phase 5 β€” Optional: prune trivial leaf messages
217
+ ```
218
+
219
+ #### The Four Axioms in Detail
220
+
221
+ | Axiom | Rule | Why |
222
+ |---|---|---|
223
+ | **1. Chronological Guard** | The older node absorbs the newer one β€” never the reverse. | Preserves temporal ordering. The past cannot be restructured to appear after the present. |
224
+ | **2. Frozen State** | Only nodes outside the currently active ancestry path may be merged. | The live conversation thread is *never* touched. Zero risk of corrupting the active agent state. |
225
+ | **3. Similarity Threshold** | Cosine similarity between embeddings must exceed the threshold (default: `0.55`). | Pre-filters pairs using pure math before wasting an LLM call. |
226
+ | **4. LLM Veto** | The LLM independently confirms the merge makes semantic sense. | Catches false positives (e.g., two topics both mentioning "Python" β€” one about snakes, one about code). If vetoed, the merge is aborted. |
227
+
228
+ **Analogy**: Just like a human brain during sleep β€” when the body is at rest, the brain doesn't switch off. It replays the day's events, consolidates important memories into long-term storage, and prunes connections that are no longer relevant. TRACE does exactly this: when the agent is idle, it reorganizes its memory graph, surfaces hidden connections across branches, and prunes redundancy β€” all without corrupting the live agent state.
229
+
230
+ ---
231
+
232
+ ### 3.4 Feature 3 β€” Trivial Leaf Archiving
233
+
234
+ Short, throwaway exchanges ("ok", "thanks", "got it") pollute the tree with noise that wastes tokens and dilutes retrieval quality.
235
+
236
+ When `prune_trivial_leaves=True` is passed to `reorganize()`, TRACE detects MessageNodes where both the user and assistant messages are under 20 words, and **soft-archives** them β€” moving them to `tree._archived_nodes` instead of hard-deleting them. They are persisted to disk in case you ever need them, but they are excluded from all future retrieval and prompt synthesis.
237
+
238
+ ---
239
+
240
+ ## 4. Installation
241
+
242
+ ### From Source (recommended until PyPI release)
243
+
244
+ ```bash
245
+ git clone https://github.com/husain34/TRACE.git
246
+ cd TRACE
247
+ pip install -e .
248
+ ```
249
+
250
+ ### Dependencies installed automatically
251
+
252
+ | Package | Purpose |
253
+ |---|---|
254
+ | `openai>=1.0.0` | LLM API client (works with any OpenAI-compatible endpoint) |
255
+ | `python-dotenv>=1.0.0` | `.env` file support |
256
+
257
+ > **Note**: TRACE requires the bundled `trace._llm_utils` module (which provides `ChatGPT_API` and `extract_json`) for internal LLM calls.
258
+ >
259
+ > **Note**: TRACE's VectorDatabase uses only Python's built-in `sqlite3` and `struct` modules β€” no external vector DB dependency required.
260
+
261
+ ---
262
+
263
+ ### Minimal Integration (10 lines)
264
+
265
+ If you already have a chat loop and just want to plug TRACE in, this is all you need:
266
+
267
+ ```python
268
+ import openai
269
+ from trace_memory import CTree, VectorDatabase, PromptSynthesizer
270
+
271
+ # 1. Boot
272
+ client = openai.OpenAI(api_key="sk-...", base_url="http://127.0.0.1:1234/v1")
273
+ def embed(text): return client.embeddings.create(input=[text], model="nomic-embed-text").data[0].embedding
274
+
275
+ tree = CTree(api_key="sk-...", model="gpt-4o-mini", embed_fn=embed)
276
+ tree.vdb = VectorDatabase("session.db")
277
+ synth = PromptSynthesizer(ctree=tree, vector_db=tree.vdb)
278
+
279
+ # 2. Each turn: build prompt β†’ call LLM β†’ store exchange
280
+ while True:
281
+ user_input = input("You: ")
282
+ system_prompt = synth.synthesize_prompt(
283
+ user_query = user_input,
284
+ query_vector = embed(user_input),
285
+ active_node = tree.current_node,
286
+ recent_messages = tree.conversation[-6:],
287
+ )
288
+ response = client.chat.completions.create(
289
+ model = "gpt-4o-mini",
290
+ messages = [{"role": "system", "content": system_prompt},
291
+ *tree.conversation[-10:],
292
+ {"role": "user", "content": user_input}],
293
+ )
294
+ reply = response.choices[0].message.content
295
+ tree.add([{"role": "user", "content": user_input},
296
+ {"role": "assistant", "content": reply}])
297
+ print(f"AI: {reply}")
298
+
299
+ # 3. When the agent is idle, consolidate memory
300
+ stats = tree.reorganize(similarity_threshold=0.55, prune_trivial_leaves=True)
301
+ ```
302
+
303
+ ---
304
+
305
+ ## 5. πŸš€ Quick Start: The Nexus Terminal
306
+
307
+ TRACE comes with a fully-featured, gorgeous Terminal UI chatbot out of the box. It is designed as a lightweight sandbox just for testing out TRACEβ€”seeing how it works, running tests, and exploring the engine without heavy frontend overhead.
308
+
309
+ **Key Features included in the terminal:**
310
+ * **Dynamic VRAM Swapping**: Hot-swaps models on the fly (unloading Text, loading Vision) to prevent local GPU crashes.
311
+ * **Live Web Search**: Pre-generation routing secretly checks DuckDuckGo to prevent hallucinations on current events.
312
+ * **Visualizing the B+Tree**: Use `/tree` to instantly print and inspect the live hierarchical memory map.
313
+ * **Multimodal Ingestion**: Drop images in the folder and use `/ingest` to extract rich descriptions directly into long-term memory.
314
+ * **Gorgeous TUI**: Threaded background spinners and ANSI colors so the UI never freezes while testing.
315
+
316
+ To run it immediately:
317
+
318
+ 1. **Navigate to the terminal folder:**
319
+ ```bash
320
+ cd nexus_terminal
321
+ ```
322
+ 2. **Install the UI dependencies:**
323
+ ```bash
324
+ pip install -r requirements.txt
325
+ ```
326
+ 3. **Configure your models:**
327
+ Rename `.env.example` to `.env` and adjust the models/URLs to point to your local LM Studio or OpenAI endpoints.
328
+ 4. **Boot the engine:**
329
+ ```bash
330
+ python terminal.py
331
+ ```
332
+
333
+ ---
334
+
335
+ ## 6. Full API Reference
336
+
337
+ ### 6.1 `CTree`
338
+
339
+ The hierarchical conversation memory tree.
340
+
341
+ ```python
342
+ from trace_memory import CTree
343
+ ```
344
+
345
+ #### Constructor
346
+
347
+ ```python
348
+ CTree(
349
+ max_children: int = 5,
350
+ api_key: str = None, # falls back to OPENAI_API_KEY env var
351
+ model: str = "gpt-4o-mini",
352
+ auto_save_path: str = None, # auto-saves tree structure (not VDB) after every add() if set
353
+ embed_fn: Callable = None, # optional: inject your embed function at construction time
354
+ )
355
+ ```
356
+
357
+ #### Injectable attributes
358
+
359
+ ```python
360
+ tree.vdb = VectorDatabase("session.db") # VDB for semantic retrieval
361
+ tree.embed_fn = embed # callable(text: str) -> List[float]
362
+ ```
363
+
364
+ #### Methods
365
+
366
+ ---
367
+
368
+ ##### `tree.add(messages: List[dict])`
369
+
370
+ Ingest one completed exchange into the tree.
371
+
372
+ ```python
373
+ tree.add([
374
+ {"role": "user", "content": "What is quantum entanglement?"},
375
+ {"role": "assistant", "content": "Quantum entanglement is ..."},
376
+ ])
377
+
378
+ # With an optional system/context message
379
+ tree.add([
380
+ {"role": "system", "content": "[Tool result]: 42.3Β°C"},
381
+ {"role": "user", "content": "Is that dangerous?"},
382
+ {"role": "assistant", "content": "Yes, 42.3Β°C is critically high ..."},
383
+ ])
384
+ ```
385
+
386
+
387
+
388
+ ---
389
+
390
+ ##### `tree.reorganize(embed_fn=None, similarity_threshold=0.55, prune_trivial_leaves=False) -> dict`
391
+
392
+ Run one self-healing reorganization pass.
393
+
394
+ ```python
395
+ stats = tree.reorganize(
396
+ embed_fn = embed, # optional: overrides tree.embed_fn
397
+ similarity_threshold = 0.60, # raise for more conservative merges
398
+ prune_trivial_leaves = True, # archive short throwaway messages
399
+ )
400
+ print(stats)
401
+ # {'merged': 3, 'pruned': 7, 'skipped': 12, 'duration_secs': 4.2}
402
+ ```
403
+
404
+ **When to call**: Periodically when the agent is idle. Not after every message.
405
+
406
+ ---
407
+
408
+ ##### `tree.save(filepath: str, save_conversation: bool = False)`
409
+
410
+ Persist the tree to JSON.
411
+
412
+ ```python
413
+ tree.save("sessions/chat_001.json", save_conversation=True)
414
+ ```
415
+
416
+ `save_conversation=True` embeds the raw message list so the session can be fully restored later.
417
+
418
+ ---
419
+
420
+ ##### `CTree.load(filepath: str, api_key: str = None, model: str = None) -> CTree`
421
+
422
+ Restore a tree from a JSON file.
423
+
424
+ ```python
425
+ tree = CTree.load("sessions/chat_001.json", api_key="sk-...", embed_fn=embed)
426
+ tree.vdb = VectorDatabase("sessions/chat_001.db")
427
+
428
+ ```
429
+
430
+ ---
431
+
432
+ ##### `tree.get_ancestors(node, include_self=True, exclude_root=False) -> List[TopicNode]`
433
+
434
+ Return the ordered ancestry chain from root down to `node`.
435
+
436
+ ```python
437
+ path = tree.get_ancestors(tree.current_node, include_self=True, exclude_root=True)
438
+ for node in path:
439
+ print(f" {node.topic_name}: {node.summary}")
440
+ ```
441
+
442
+ ---
443
+
444
+ ##### `tree.generate_summaries()`
445
+
446
+ Manually trigger LLM summarisation of all frozen branches.
447
+ Called automatically during `save()` and internally during `reorganize()`. Can be used to manually pre-warm summaries if desired.
448
+
449
+ ---
450
+
451
+ ##### `tree.print_tree(node=None, indent=0, show_messages=False)`
452
+
453
+ Pretty-print the tree to stdout.
454
+
455
+ ```python
456
+ tree.print_tree(show_messages=True)
457
+ ```
458
+
459
+ Example output:
460
+ ```
461
+ ROOT (sub-nodes: 3)
462
+ β”œβ”€ Physics Discussions [0:12] (6 msgs)
463
+ Covered quantum entanglement and black hole thermodynamics.
464
+ β”œβ”€ Quantum Entanglement [0:6] (3 msgs)
465
+ β”œβ”€ Black Holes [6:12] (3 msgs)
466
+ β”œβ”€ Party Planning [12:20] (4 msgs)
467
+ Planning Sarah's surprise birthday party logistics.
468
+ ```
469
+
470
+ ---
471
+
472
+ #### Key attributes
473
+
474
+ | Attribute | Type | Description |
475
+ |---|---|---|
476
+ | `tree.conversation` | `List[dict]` | Flat list of all raw messages in chronological order. |
477
+ | `tree.current_node` | `TopicNode` | The currently active topic branch. |
478
+ | `tree.root` | `TopicNode` | The virtual root of the tree. |
479
+ | `tree._archived_nodes` | `List[MessageNode]` | Soft-archived trivial leaf messages. |
480
+ | `tree.auto_save_path` | `str \| None` | If set, auto-saves the tree structure (not the VDB) after every `add()`. |
481
+
482
+ ---
483
+
484
+ ### 6.2 `VectorDatabase`
485
+
486
+ A local SQLite vector store with two active tables: conversation vectors and topic summaries.
487
+
488
+ > **Note on Scaling:** TRACE uses pure Python (`sqlite3` and `struct`) to avoid heavy dependencies and run instantly on any machine. This is perfectly sufficient for local agents and moderate conversation histories. However, for massive scale (millions of vectors), you will need to swap this module for a dedicated vector database like FAISS or Chroma.
489
+
490
+ ```python
491
+ from trace_memory import VectorDatabase
492
+ ```
493
+
494
+ #### Constructor
495
+
496
+ ```python
497
+ vdb = VectorDatabase("path/to/session.db") # creates the DB if it doesn't exist
498
+ ```
499
+
500
+ ---
501
+
502
+ ---
503
+
504
+ #### Conversation methods
505
+
506
+ ##### `vdb.add_conversation_message(msg: ConversationVector)`
507
+
508
+ Store an embedded past conversation message for cross-thread recall.
509
+
510
+ ```python
511
+ from trace_memory import ConversationVector
512
+ import time, uuid
513
+
514
+ msg = ConversationVector(
515
+ message_id = str(uuid.uuid4()),
516
+ message_index = 0,
517
+ role = "user",
518
+ text = "Sarah is allergic to peanuts.",
519
+ embedding = embed("Sarah is allergic to peanuts."),
520
+ timestamp = time.time(),
521
+ thread_path = "ROOT β†’ Health Constraints β†’ Allergies",
522
+ )
523
+ vdb.add_conversation_message(msg)
524
+ ```
525
+
526
+ ##### `vdb.search_conversation(query_vector, top_k=2, min_similarity=0.5) -> List[ConversationVector]`
527
+
528
+ Retrieve semantically similar past messages from any branch.
529
+
530
+ ```python
531
+ recalls = vdb.search_conversation(
532
+ query_vector = embed("Is the cake safe for Sarah?"),
533
+ top_k = 3,
534
+ min_similarity = 0.45,
535
+ )
536
+ for r in recalls:
537
+ print(f"[{r.similarity:.2f}] {r.thread_path} β€” {r.role}: {r.text}")
538
+ ```
539
+
540
+ ---
541
+
542
+ #### Topic summary methods
543
+
544
+ ##### `vdb.upsert_topic_summary(node_id, topic_name, summary, embedding, ...)`
545
+
546
+ Insert or update a topic node's embedding. Called automatically by `CTree` when a node is frozen and summarised.
547
+
548
+ ##### `vdb.search_topic_summaries(query_vector, top_k=3, min_similarity=0.40) -> List[dict]`
549
+
550
+ Used internally by `PromptSynthesizer` for surgical multi-path retrieval.
551
+
552
+ ```python
553
+ hits = vdb.search_topic_summaries(
554
+ query_vector = embed("peanut allergy constraint"),
555
+ top_k = 3,
556
+ min_similarity = 0.35,
557
+ )
558
+ # Returns: [{"node_id": "...", "topic_name": "...", "summary": "...", "similarity": 0.82}, ...]
559
+ ```
560
+
561
+ ##### `vdb.delete_topic_summary(node_id: str)`
562
+
563
+ Remove a topic embedding by node ID. Called automatically during `reorganize()` when a node is moved.
564
+
565
+ ---
566
+
567
+ ### 6.3 `PromptSynthesizer`
568
+
569
+ Assembles the full RAG-enriched system prompt.
570
+
571
+ ```python
572
+ from trace_memory import PromptSynthesizer
573
+
574
+ synth = PromptSynthesizer(ctree=tree, vector_db=vdb)
575
+ ```
576
+
577
+ #### `synth.synthesize_prompt(...) -> str`
578
+
579
+ ```python
580
+ system_prompt = synth.synthesize_prompt(
581
+ user_query = "Is the cake safe for Sarah?",
582
+ query_vector = embed("Is the cake safe for Sarah?"),
583
+ active_node = tree.current_node,
584
+ recent_messages = tree.conversation[-6:],
585
+ top_k_history = 2, # max past messages to recall
586
+ min_history_similarity = 0.50, # min cosine score for conversation recall
587
+ )
588
+ ```
589
+
590
+ The returned string is a complete system prompt. Pass it directly as the `system` role message to your LLM.
591
+
592
+ ---
593
+
594
+ ### 6.4 Data Classes
595
+
596
+ #### `ConversationVector`
597
+
598
+ ```python
599
+ ConversationVector(
600
+ message_id: str,
601
+ message_index: int,
602
+ role: str, # "user" | "assistant" | "system"
603
+ text: str,
604
+ embedding: List[float],
605
+ timestamp: float, # unix timestamp
606
+ thread_path: str, # e.g. "ROOT β†’ Physics β†’ Black Holes"
607
+ similarity: float = 0.0,
608
+ )
609
+ ```
610
+
611
+
612
+ ---
613
+
614
+ ## 7. Integration Guide
615
+
616
+ ### 7.1 With LM Studio (local)
617
+
618
+ ```python
619
+ import os
620
+ os.environ["OPENAI_BASE_URL"] = "http://127.0.0.1:1234/v1"
621
+ os.environ["OPENAI_API_KEY"] = "lm-studio"
622
+
623
+ from trace_memory import CTree
624
+ tree = CTree(model="meta-llama-3.1-8b-instruct")
625
+ ```
626
+
627
+ Or use a `.env` file in your project root:
628
+
629
+ ```
630
+ OPENAI_BASE_URL=http://127.0.0.1:1234/v1
631
+ OPENAI_API_KEY=lm-studio
632
+ ```
633
+
634
+ ---
635
+
636
+ ### 7.2 With OpenAI
637
+
638
+ ```python
639
+ from trace_memory import CTree
640
+ tree = CTree(api_key="sk-...", model="gpt-4o-mini")
641
+ ```
642
+
643
+ ---
644
+
645
+ ### 7.3 With NVIDIA NIM / any OpenAI-compatible endpoint
646
+
647
+ ```python
648
+ import os
649
+ os.environ["OPENAI_BASE_URL"] = "https://integrate.api.nvidia.com/v1"
650
+ os.environ["OPENAI_API_KEY"] = "nvapi-..."
651
+
652
+ from trace_memory import CTree
653
+ tree = CTree(model="meta/llama-3.1-70b-instruct")
654
+ ```
655
+
656
+ ---
657
+
658
+ ## 8. Edge-Case Stress Tests & Validation
659
+
660
+ TRACE was validated against five adversarial test scenarios:
661
+
662
+ | Test | Description | Result |
663
+ |---|---|---|
664
+ | **Needle in a Haystack** | A critical constraint buried deep in a 200-message session | βœ… Retrieved with >0.7 cosine score |
665
+ | **Memory Overwrites** | User updates a constraint ("actually, Sarah can eat nuts now") | βœ… Newer node supersedes older via Chronological Guard |
666
+ | **Semantic Drift (Veto Test)** | Two topics share keywords but are in different domains (Python the snake vs. Python the language) | βœ… LLM Veto correctly aborted the merge |
667
+ | **Ship of Theseus** | Gradual topic drift β€” same entity discussed across 10 different branches | βœ… Reorganizer correctly consolidated into a shared parent |
668
+ | **Multi-Hop Reasoning** | Answer requires synthesising info from 3 non-adjacent branches (allergy + party + recipe) | βœ… Surgical retrieval surfaced all 3; LLM synthesised the conflict |
669
+
670
+ ---
671
+
672
+ ## 9. Environment Variables
673
+
674
+ | Variable | Default | Description |
675
+ |---|---|---|
676
+ | `OPENAI_BASE_URL` | `http://127.0.0.1:1234/v1` | LLM API endpoint |
677
+ | `OPENAI_API_KEY` | `lm-studio` | API key |
678
+
679
+ Set these in a `.env` file or directly in your shell.
680
+ TRACE loads `.env` automatically if `python-dotenv` is installed.
681
+
682
+ ---
683
+
684
+ ## 10. Dependencies
685
+
686
+ | Package | Version | Required |
687
+ |---|---|---|
688
+ | `openai` | β‰₯ 1.0.0 | βœ… Yes |
689
+ | `python-dotenv` | β‰₯ 1.0.0 | βœ… Yes |
690
+ | `sqlite3` | built-in | βœ… Yes (no install needed) |
691
+ | `struct` | built-in | βœ… Yes (no install needed) |
692
+
693
+ ---
694
+
695
+ ## 11. Contributing
696
+
697
+ Pull requests are welcome. For major changes, please open an issue first.
698
+
699
+ Areas where contributions are especially valuable:
700
+ - Additional embedding model adapters (Sentence Transformers, Cohere, etc.)
701
+ - Async support for `add()` and `reorganize()`
702
+ - Web UI for tree visualisation
703
+ - Benchmarks against other memory architectures (MemGPT, Zep, etc.)
704
+
705
+ ---
706
+
707
+ ## 12. License
708
+
709
+ This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.
710
+
711
+ *Note: This project includes code from ChatIndex, licensed under Apache 2.0. See [NOTICE](NOTICE) for details.*
712
+
713
+ ---
714
+
715
+ *Built by Husain Ghulam.*