BinSaqban commited on
Commit
66916eb
Β·
verified Β·
1 Parent(s): 42d10c2

Upload tensorstore_memory.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. tensorstore_memory.py +390 -0
tensorstore_memory.py ADDED
@@ -0,0 +1,390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ TensorStore Agent Memory β€” Google TensorStore-inspired Multi-Dimensional Agent Memory
3
+
4
+ Concepts from Google Neural Mapping:
5
+ - TensorStore: N-dimensional array storage for petabytes (C++/Python)
6
+ - Neuroglancer: Multi-resolution zoomable data viewer
7
+ - SegCLR: Self-supervised embedding learning
8
+
9
+ Applied to Agent Memory:
10
+ - Store all agent interactions as embedding vectors in a tensor
11
+ - Multi-resolution retrieval (recent, week, month, all-time)
12
+ - Semantic similarity search (not grep)
13
+ - Auto-clustering of related memories
14
+ - Zero external dependencies β€” pure numpy + built-in json
15
+
16
+ Usage:
17
+ from tensorstore_memory import AgentMemoryTensor
18
+ mem = AgentMemoryTensor(dimensions=384)
19
+ mem.store("rushd", "task completed: trade signal BTC", embedding=[...])
20
+ results = mem.query("what trades did we do?", top_k=5)
21
+ """
22
+
23
+ import json, time, math, os, hashlib
24
+ from pathlib import Path
25
+ from collections import defaultdict, OrderedDict
26
+ from datetime import datetime, timedelta
27
+ import threading
28
+
29
+ try:
30
+ import numpy as np
31
+ except ImportError:
32
+ np = None
33
+
34
+ # ─── Configuration ───────────────────────────────────────
35
+ MEMORY_DIR = Path(os.environ.get("TENSORSTORE_DIR", "/tmp/agent-tensorstore"))
36
+ DEFAULT_DIM = 384 # embedding dimension
37
+ MAX_RESOLUTIONS = 4 # zoom levels: recent, daily, weekly, all-time
38
+
39
+ # ─── Simple embedding (no external deps) ──────────────────
40
+ def simple_embed(text: str, dim: int = DEFAULT_DIM) -> list[float]:
41
+ """Lightweight text embedding using character n-gram hashing.
42
+ For production, plug in any embedding model (sentence-transformers, etc.)"""
43
+ if np is None:
44
+ # Fallback pure Python embedding
45
+ vec = [0.0] * dim
46
+ for i, ch in enumerate(text):
47
+ h = hash(f"{i}:{ch}") % dim
48
+ vec[h] += 1.0 / (i + 1)
49
+ # Normalize
50
+ norm = math.sqrt(sum(v*v for v in vec)) or 1
51
+ return [v/norm for v in vec]
52
+ else:
53
+ # Use numpy for faster hashing
54
+ vec = np.zeros(dim, dtype=np.float32)
55
+ for i, ch in enumerate(text):
56
+ h = abs(hash(f"{i}:{ch}")) % dim
57
+ vec[h] += 1.0 / (i + 1)
58
+ norm = np.linalg.norm(vec) or 1.0
59
+ return (vec / norm).tolist()
60
+
61
+ def cosine_similarity(a, b):
62
+ """Cosine similarity between two vectors."""
63
+ if np is not None:
64
+ a, b = np.array(a), np.array(b)
65
+ return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-8))
66
+ dot = sum(x*y for x,y in zip(a,b))
67
+ na = math.sqrt(sum(x*x for x in a)) + 1e-8
68
+ nb = math.sqrt(sum(x*x for x in b)) + 1e-8
69
+ return dot / (na * nb)
70
+
71
+
72
+ # ─── LRU Cache for hot memories ───────────────────────────
73
+ class LRUCache:
74
+ def __init__(self, maxsize: int = 1000):
75
+ self.cache = OrderedDict()
76
+ self.maxsize = maxsize
77
+ self._lock = threading.Lock()
78
+
79
+ def get(self, key):
80
+ with self._lock:
81
+ if key in self.cache:
82
+ self.cache.move_to_end(key)
83
+ return self.cache[key]
84
+ return None
85
+
86
+ def put(self, key, value):
87
+ with self._lock:
88
+ if key in self.cache:
89
+ self.cache.move_to_end(key)
90
+ self.cache[key] = value
91
+ while len(self.cache) > self.maxsize:
92
+ self.cache.popitem(last=False)
93
+
94
+
95
+ # ─── Core: Agent Memory Tensor ────────────────────────────
96
+ class AgentMemoryTensor:
97
+ """Multi-resolution memory tensor for agent interactions.
98
+ Inspired by Google TensorStore's n-dimensional array model."""
99
+
100
+ def __init__(self, dimensions: int = DEFAULT_DIM, cache_size: int = 1000):
101
+ self.dim = dimensions
102
+ self.memories: list[dict] = [] # [{"agent","text","embedding","ts","tags"}]
103
+ self.agents: dict[str, dict] = {}
104
+ self.cache = LRUCache(cache_size)
105
+ self._lock = threading.Lock()
106
+ self._dirty = False
107
+
108
+ # Resolution layers (like Neuroglancer zoom levels)
109
+ self.resolutions = {
110
+ "recent": {"max_age_hours": 1, "memories": []},
111
+ "daily": {"max_age_hours": 24, "memories": []},
112
+ "weekly": {"max_age_hours": 168, "memories": []},
113
+ "all_time": {"max_age_hours": float("inf"), "memories": []},
114
+ }
115
+
116
+ MEMORY_DIR.mkdir(parents=True, exist_ok=True)
117
+ self._load()
118
+
119
+ # ─── Store ─────────────────────────────────────────────
120
+ def store(self, agent_id: str, text: str,
121
+ embedding: list[float] = None,
122
+ tags: list[str] = None,
123
+ metadata: dict = None) -> str:
124
+ """Store a memory entry. Returns memory_id."""
125
+ if embedding is None:
126
+ embedding = simple_embed(text, self.dim)
127
+
128
+ mem_id = hashlib.sha256(f"{agent_id}:{text}:{time.time()}".encode()).hexdigest()[:16]
129
+
130
+ entry = {
131
+ "id": mem_id,
132
+ "agent": agent_id,
133
+ "text": text[:500], # truncate long texts
134
+ "embedding": embedding,
135
+ "ts": time.time(),
136
+ "tags": tags or [],
137
+ "metadata": metadata or {},
138
+ }
139
+
140
+ with self._lock:
141
+ self.memories.append(entry)
142
+ if agent_id not in self.agents:
143
+ self.agents[agent_id] = {"count": 0, "first_seen": time.time(), "last_seen": time.time()}
144
+ self.agents[agent_id]["count"] += 1
145
+ self.agents[agent_id]["last_seen"] = time.time()
146
+
147
+ # Update resolutions
148
+ now = time.time()
149
+ for res_name, res_data in self.resolutions.items():
150
+ max_age = res_data["max_age_hours"] * 3600
151
+ if max_age == float("inf"):
152
+ res_data["memories"].append(mem_id)
153
+ else:
154
+ # Keep only memories within time window
155
+ res_data["memories"] = [m for m in res_data["memories"]
156
+ if any(mm["id"] == m and now - mm["ts"] <= max_age
157
+ for mm in self.memories[-100:])]
158
+ res_data["memories"].append(mem_id)
159
+
160
+ self._dirty = True
161
+
162
+ self.cache.put(mem_id, entry)
163
+ return mem_id
164
+
165
+ # ─── Query by text (semantic search) ───────────────────
166
+ def query(self, query_text: str, top_k: int = 5,
167
+ agent_filter: str = None, tag_filter: str = None,
168
+ resolution: str = "all_time") -> list[dict]:
169
+ """Semantic search across memories."""
170
+ query_emb = simple_embed(query_text, self.dim)
171
+
172
+ # Use cached result if available
173
+ cache_key = f"q:{query_text[:50]}:{top_k}:{agent_filter}:{tag_filter}:{resolution}"
174
+ cached = self.cache.get(cache_key)
175
+ if cached:
176
+ return cached
177
+
178
+ with self._lock:
179
+ # Filter by resolution
180
+ if resolution in self.resolutions:
181
+ valid_ids = set(self.resolutions[resolution]["memories"][-1000:])
182
+ candidates = [m for m in self.memories[-5000:] if m["id"] in valid_ids]
183
+ else:
184
+ candidates = self.memories[-5000:]
185
+
186
+ # Filter by agent/tag
187
+ if agent_filter:
188
+ candidates = [m for m in candidates if m["agent"] == agent_filter]
189
+ if tag_filter:
190
+ candidates = [m for m in candidates if tag_filter in m.get("tags", [])]
191
+
192
+ # Score and rank
193
+ scored = []
194
+ for mem in candidates:
195
+ sim = cosine_similarity(query_emb, mem["embedding"])
196
+ # Boost recent memories
197
+ recency = 1.0 / (1.0 + (time.time() - mem["ts"]) / 86400) # days ago
198
+ score = sim * 0.7 + recency * 0.3
199
+ scored.append((score, mem))
200
+
201
+ scored.sort(key=lambda x: x[0], reverse=True)
202
+ results = []
203
+ for score, mem in scored[:top_k]:
204
+ results.append({
205
+ "id": mem["id"],
206
+ "agent": mem["agent"],
207
+ "text": mem["text"],
208
+ "score": round(score, 4),
209
+ "similarity": round(cosine_similarity(query_emb, mem["embedding"]), 4),
210
+ "timestamp": datetime.fromtimestamp(mem["ts"]).isoformat(),
211
+ "tags": mem["tags"],
212
+ "metadata": mem.get("metadata", {}),
213
+ })
214
+
215
+ self.cache.put(cache_key, results)
216
+ return results
217
+
218
+ # ─── Get by agent (timeline) ───────────────────────────
219
+ def agent_timeline(self, agent_id: str, limit: int = 20) -> list[dict]:
220
+ """Get recent memories for a specific agent."""
221
+ with self._lock:
222
+ agent_mems = [m for m in self.memories[-limit*10:] if m["agent"] == agent_id]
223
+ return [{
224
+ "id": m["id"],
225
+ "text": m["text"],
226
+ "timestamp": datetime.fromtimestamp(m["ts"]).isoformat(),
227
+ "tags": m.get("tags", []),
228
+ } for m in agent_mems[-limit:]]
229
+
230
+ # ─── Similar agents (like SegCLR cell type clustering) ──
231
+ def similar_agents(self, agent_id: str, top_k: int = 5) -> list[dict]:
232
+ """Find agents with similar behavior patterns."""
233
+ if agent_id not in self.agents:
234
+ return []
235
+
236
+ # Build agent centroids from their memory embeddings
237
+ centroids = {}
238
+ with self._lock:
239
+ for aid in self.agents:
240
+ agent_mems = [m for m in self.memories[-1000:] if m["agent"] == aid]
241
+ if agent_mems:
242
+ if np is not None:
243
+ emb_matrix = np.array([m["embedding"] for m in agent_mems])
244
+ centroids[aid] = emb_matrix.mean(axis=0).tolist()
245
+ else:
246
+ centroid = [0.0] * self.dim
247
+ for m in agent_mems:
248
+ for i, v in enumerate(m["embedding"]):
249
+ centroid[i] += v
250
+ n = len(agent_mems)
251
+ centroids[aid] = [v/n for v in centroid]
252
+
253
+ target = centroids.get(agent_id)
254
+ if not target:
255
+ return []
256
+
257
+ similarities = []
258
+ for aid, centroid in centroids.items():
259
+ if aid != agent_id:
260
+ sim = cosine_similarity(target, centroid)
261
+ similarities.append({"agent": aid, "similarity": round(sim, 4)})
262
+
263
+ similarities.sort(key=lambda x: x["similarity"], reverse=True)
264
+ return similarities[:top_k]
265
+
266
+ # ─── Stats ─────────────────────────────────────────────
267
+ def stats(self) -> dict:
268
+ """Memory statistics like TensorStore's metadata inspection."""
269
+ with self._lock:
270
+ total = len(self.memories)
271
+ agents_count = len(self.agents)
272
+
273
+ # Memory size by agent
274
+ by_agent = {aid: data["count"] for aid, data in self.agents.items()}
275
+ top_agents = sorted(by_agent.items(), key=lambda x: x[1], reverse=True)[:10]
276
+
277
+ # Memory age distribution
278
+ now = time.time()
279
+ ages = {"<1h": 0, "1-24h": 0, "1-7d": 0, ">7d": 0}
280
+ for m in self.memories:
281
+ age_hours = (now - m["ts"]) / 3600
282
+ if age_hours < 1: ages["<1h"] += 1
283
+ elif age_hours < 24: ages["1-24h"] += 1
284
+ elif age_hours < 168: ages["1-7d"] += 1
285
+ else: ages[">7d"] += 1
286
+
287
+ return {
288
+ "total_memories": total,
289
+ "total_agents": agents_count,
290
+ "dimensions": self.dim,
291
+ "resolution_layers": len(self.resolutions),
292
+ "top_agents": dict(top_agents),
293
+ "age_distribution": ages,
294
+ "cache_size": len(self.cache.cache),
295
+ "memory_size_kb": round(total * self.dim * 4 / 1024, 1), # float32 estimate
296
+ }
297
+
298
+ # ─── Persistence ───────────────────────────────────────
299
+ def _load(self):
300
+ path = MEMORY_DIR / "memory.json"
301
+ if path.exists():
302
+ try:
303
+ with open(path) as f:
304
+ data = json.load(f)
305
+ self.memories = data.get("memories", [])
306
+ self.agents = data.get("agents", {})
307
+ except:
308
+ pass
309
+
310
+ def save(self):
311
+ path = MEMORY_DIR / "memory.json"
312
+ with self._lock:
313
+ # Keep last 10000 memories to avoid bloat
314
+ data = {
315
+ "memories": self.memories[-10000:],
316
+ "agents": self.agents,
317
+ }
318
+ with open(path, "w") as f:
319
+ json.dump(data, f, ensure_ascii=False)
320
+ self._dirty = False
321
+
322
+ def auto_save(self, interval_seconds: int = 60):
323
+ """Background auto-save thread."""
324
+ def _loop():
325
+ while True:
326
+ time.sleep(interval_seconds)
327
+ if self._dirty:
328
+ self.save()
329
+ t = threading.Thread(target=_loop, daemon=True)
330
+ t.start()
331
+
332
+
333
+ # ─── CLI Demo ─────────────────────────────────────────────
334
+ if __name__ == "__main__":
335
+ print("🧠 TensorStore Agent Memory β€” Google TensorStore-inspired Multi-Resolution Memory")
336
+ print(f" Dimensions: {DEFAULT_DIM} | Directory: {MEMORY_DIR}")
337
+
338
+ mem = AgentMemoryTensor(dimensions=DEFAULT_DIM)
339
+
340
+ # Demo: simulate agent memories
341
+ agents = ["rushd", "wafa", "awf", "dragon", "hermes", "musa", "zeus", "haytham"]
342
+ tasks = [
343
+ "routed task to awf for trading signal",
344
+ "verified output from dragon agent",
345
+ "executed BTC/USDT trade with 2% profit",
346
+ "memory search completed for hayula papers",
347
+ "skill code_review invoked on PR #42",
348
+ "Arabic text generation for blog post",
349
+ "error timeout on connection to M2",
350
+ "created new agent connectome snapshot",
351
+ ]
352
+
353
+ print(f"\nπŸ“ Storing {len(agents) * 5} memories...")
354
+ import random
355
+ for i in range(len(agents) * 5):
356
+ agent = random.choice(agents)
357
+ task = random.choice(tasks)
358
+ tags = random.sample(["trade", "code", "memory", "route", "error"], k=random.randint(1, 3))
359
+ mem.store(agent, task, tags=tags)
360
+
361
+ mem.save()
362
+
363
+ # Stats
364
+ s = mem.stats()
365
+ print(f"\nπŸ“Š Stats:")
366
+ print(f" Total: {s['total_memories']} memories across {s['total_agents']} agents")
367
+ print(f" Size: ~{s['memory_size_kb']} KB")
368
+ print(f" Cache: {s['cache_size']} entries")
369
+ print(f" Age: {s['age_distribution']}")
370
+
371
+ # Query
372
+ q = "what trading activity happened?"
373
+ print(f"\nπŸ” Query: '{q}'")
374
+ results = mem.query(q, top_k=3)
375
+ for r in results:
376
+ print(f" [{r['score']:.3f}] {r['agent']}: {r['text'][:60]}")
377
+
378
+ # Similar agents
379
+ print(f"\n🧬 Agents similar to 'awf':")
380
+ similar = mem.similar_agents("awf", top_k=3)
381
+ for s in similar:
382
+ print(f" {s['agent']}: similarity={s['similarity']}")
383
+
384
+ # Multi-resolution
385
+ print(f"\nπŸ”¬ Resolutions:")
386
+ for name, data in mem.resolutions.items():
387
+ print(f" {name}: {len(data['memories'])} memories (max_age={data['max_age_hours']}h)")
388
+
389
+ print(f"\nβœ… TensorStore Agent Memory ready!")
390
+ print(f" Memory file: {MEMORY_DIR}/memory.json")