Executor-Tyrant-Framework commited on
Commit
edeb6b2
Β·
unverified Β·
1 Parent(s): 4f79352

Update recursive_context.py

Browse files
Files changed (1) hide show
  1. recursive_context.py +149 -125
recursive_context.py CHANGED
@@ -18,71 +18,117 @@ class RecursiveContextManager:
18
  # --- AUTHENTICATION ---
19
  self.token = os.getenv("HF_TOKEN")
20
  self.dataset_id = os.getenv("DATASET_ID", "Executor-Tyrant-Framework/clawdbot-memory")
 
21
 
22
  # --- XET / DATABASE INIT ---
 
 
23
  self.xet_store = None
 
24
  try:
25
- # Try to load the Xet store if the file exists
26
  if (self.repo_path / "xet_storage.py").exists():
27
  import sys
28
  sys.path.append(str(self.repo_path))
29
  from xet_storage import XetVectorStore
30
- # Assuming Xet Repo URL is in env or default
31
- xet_url = os.getenv("XET_REPO_URL", "local/xet-repo")
32
- self.xet_store = XetVectorStore(xet_url)
33
  print("βœ… Xet Storage Driver Loaded.")
34
  except Exception as e:
35
  print(f"⚠️ Xet Driver not loaded: {e}")
36
 
37
- # --- MEMORY INIT ---
38
  self._init_memory()
 
39
 
 
 
 
40
  def _init_memory(self):
41
- """STARTUP: Download the brain from the Dataset."""
42
  self.memory_path.mkdir(parents=True, exist_ok=True)
43
  if self.token:
44
  try:
45
  hf_hub_download(
46
- repo_id=self.dataset_id,
47
- filename="notebook.json",
48
- repo_type="dataset",
49
- token=self.token,
50
- local_dir=self.memory_path,
51
- local_dir_use_symlinks=False
52
  )
53
- print(f"🧠 Brain restored from {self.dataset_id}")
54
- except Exception as e:
55
- print(f"⚠️ Memory download failed (Creating Fresh): {e}")
56
- self._save_local([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
 
 
 
58
  def _save_local(self, notes: List[Dict]):
59
  self.memory_path.mkdir(parents=True, exist_ok=True)
60
  self.notebook_file.write_text(json.dumps(notes, indent=2), encoding='utf-8')
61
 
62
  def _save_notebook(self, notes: List[Dict]):
63
- """SAVE: Disk + Cloud Sync."""
64
  self._save_local(notes)
65
  if self.token and self.dataset_id:
66
  try:
67
  api = HfApi(token=self.token)
68
  api.upload_file(
69
- path_or_fileobj=self.notebook_file,
70
- path_in_repo="notebook.json",
71
- repo_id=self.dataset_id,
72
- repo_type="dataset",
73
- commit_message=f"🧠 Notebook Update: {len(notes)} items"
74
  )
75
- except Exception as e:
76
- print(f"⚠️ Dataset sync failed: {e}")
77
 
78
  def _load_notebook(self) -> List[Dict]:
79
  if not self.notebook_file.exists(): return []
80
  try: return json.loads(self.notebook_file.read_text(encoding='utf-8'))
81
  except: return []
82
 
83
- # =========================================================================
84
- # 🧠 NOTEBOOK TOOLS
85
- # =========================================================================
86
  def notebook_read(self) -> str:
87
  notes = self._load_notebook()
88
  if not notes: return "Notebook is empty."
@@ -90,8 +136,7 @@ class RecursiveContextManager:
90
 
91
  def notebook_add(self, content: str) -> str:
92
  notes = self._load_notebook()
93
- timestamp = time.strftime("%Y-%m-%d %H:%M")
94
- notes.append({"timestamp": timestamp, "content": content})
95
  if len(notes) > 50: notes = notes[-50:]
96
  self._save_notebook(notes)
97
  return f"βœ… Note added & synced. ({len(notes)} items)"
@@ -105,80 +150,73 @@ class RecursiveContextManager:
105
  except IndexError: return "❌ Invalid index."
106
 
107
  # =========================================================================
108
- # πŸ—ΊοΈ CARTOGRAPHER (The Graph Tool)
109
  # =========================================================================
110
- def map_repository_structure(self) -> str:
111
- """Scans codebase to build a structural graph (AST-based)."""
112
- graph = {"nodes": [], "edges": []}
113
- try:
114
- # Initialize Client for semantic tagging (if available)
115
- client = InferenceClient(token=self.token) if self.token else None
116
-
117
- file_count = 0
118
- for file_path in self.repo_path.rglob('*.py'):
119
- if 'venv' in str(file_path) or 'site-packages' in str(file_path): continue
120
- rel_path = str(file_path.relative_to(self.repo_path))
121
- content = file_path.read_text(errors='ignore')
122
- file_count += 1
123
-
124
- graph["nodes"].append({"id": rel_path, "type": "file"})
125
-
126
- try:
127
- tree = ast.parse(content)
128
- for node in ast.walk(tree):
129
- if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
130
- node_id = f"{rel_path}::{node.name}"
131
- graph["nodes"].append({
132
- "id": node_id,
133
- "type": "function" if isinstance(node, ast.FunctionDef) else "class",
134
- "lineno": node.lineno
135
- })
136
- graph["edges"].append({"source": rel_path, "target": node_id, "relation": "defines"})
137
-
138
- for child in ast.walk(node):
139
- if isinstance(child, ast.Call) and hasattr(child.func, 'id'):
140
- graph["edges"].append({
141
- "source": node_id,
142
- "target": child.func.id,
143
- "relation": "calls"
144
- })
145
- except SyntaxError: continue
146
-
147
- # Save the Map locally (and ideally push to dataset later)
148
- map_path = self.memory_path / "repository_map.json"
149
- map_path.write_text(json.dumps(graph, indent=2))
150
- return f"βœ… Map Generated: {file_count} files, {len(graph['nodes'])} nodes. Saved to memory/repository_map.json"
151
 
152
- except Exception as e:
153
- return f"❌ Mapping failed: {e}"
 
 
 
 
 
 
 
 
 
154
 
155
- # =========================================================================
156
- # πŸ› οΈ STANDARD TOOLS
157
- # =========================================================================
158
  def search_code(self, query: str, n: int=5) -> List[Dict]:
159
  results = []
160
  try:
161
- # 1. Try Xet Semantic Search first
162
- if self.xet_store:
163
- # Mock embedding for now, real one would go here
164
- vector = [0.1] * 128
165
- return self.xet_store.similarity_search(vector, n)
166
-
167
- # 2. Fallback to Text Search
168
  for f in self.repo_path.rglob("*.py"):
 
169
  txt = f.read_text(errors='ignore')
170
  if query in txt:
171
  results.append({"file": f.name, "snippet": txt[:300]})
172
  except: pass
173
  return results[:n]
174
-
175
- def search_conversations(self, query: str, n: int=5) -> List[Dict]:
176
- # Connect to Xet or memory store here
177
- # For now, return recent history from log if Xet fails
178
- return []
179
-
180
  def search_testament(self, query: str, n: int=5) -> List[Dict]:
181
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
  def list_files(self, path: str = ".", max_depth: int = 3) -> str:
184
  try:
@@ -191,15 +229,6 @@ class RecursiveContextManager:
191
  return "\n".join(files[:50])
192
  except Exception as e: return str(e)
193
 
194
- def read_file(self, path: str, start: int = None, end: int = None) -> str:
195
- try:
196
- target = self.repo_path / path
197
- content = target.read_text(encoding='utf-8', errors='ignore')
198
- lines = content.splitlines()
199
- if start is not None and end is not None: lines = lines[start:end]
200
- return "\n".join(lines)
201
- except Exception as e: return str(e)
202
-
203
  def write_file(self, path: str, content: str) -> str:
204
  try:
205
  target = self.repo_path / path
@@ -215,33 +244,28 @@ class RecursiveContextManager:
215
  return f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
216
  except Exception as e: return f"Error: {e}"
217
 
218
- def push_to_github(self, message: str) -> str:
219
- """Push current state to the connected HF Space (Git)."""
220
- try:
221
- subprocess.run(["git", "config", "user.email", "clawdbot@system.local"], check=False)
222
- subprocess.run(["git", "config", "user.name", "Clawdbot"], check=False)
223
- subprocess.run(["git", "add", "."], check=True)
224
- subprocess.run(["git", "commit", "-m", message], check=True)
225
- # Note: 'git push' requires the token to be in the remote URL or credential helper
226
- return "βœ… Changes committed (Push requires configured remote with token)."
227
- except Exception as e: return f"Git Error: {e}"
228
-
229
- def pull_from_github(self, branch: str) -> str:
230
  try:
231
- subprocess.run(["git", "pull", "origin", branch], check=True)
232
- return f"βœ… Pulled {branch}"
233
- except Exception as e: return f"Git Pull Error: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
- def create_shadow_branch(self) -> str:
236
- ts = int(time.time())
237
- try:
238
- subprocess.run(["git", "checkout", "-b", f"shadow_{ts}"], check=True)
239
- return f"βœ… Created branch shadow_{ts}"
240
- except Exception as e: return f"Error: {e}"
241
-
242
  def get_stats(self) -> Dict:
243
  return {"total_files": len(list(self.repo_path.rglob("*"))), "conversations": 0}
244
-
245
- def save_conversation_turn(self, user_msg, assist_msg, turn_id):
246
- # Optional: Log turn to a file for ingestion
247
- pass
 
18
  # --- AUTHENTICATION ---
19
  self.token = os.getenv("HF_TOKEN")
20
  self.dataset_id = os.getenv("DATASET_ID", "Executor-Tyrant-Framework/clawdbot-memory")
21
+ self.client = InferenceClient(token=self.token) if self.token else None
22
 
23
  # --- XET / DATABASE INIT ---
24
+ self.xet_root = self.repo_path / "xet_data"
25
+ self.xet_dataset_file = "xet_vectors.json" # Claude's Strategy
26
  self.xet_store = None
27
+
28
  try:
 
29
  if (self.repo_path / "xet_storage.py").exists():
30
  import sys
31
  sys.path.append(str(self.repo_path))
32
  from xet_storage import XetVectorStore
33
+ self.xet_store = XetVectorStore(repo_path=str(self.xet_root))
 
 
34
  print("βœ… Xet Storage Driver Loaded.")
35
  except Exception as e:
36
  print(f"⚠️ Xet Driver not loaded: {e}")
37
 
38
+ # --- RESTORE MEMORY ---
39
  self._init_memory()
40
+ self._init_xet_memory()
41
 
42
+ # =========================================================================
43
+ # 🧠 SYNC LOGIC (Notebook + Xet JSON)
44
+ # =========================================================================
45
  def _init_memory(self):
46
+ """STARTUP: Download Notebook."""
47
  self.memory_path.mkdir(parents=True, exist_ok=True)
48
  if self.token:
49
  try:
50
  hf_hub_download(
51
+ repo_id=self.dataset_id, filename="notebook.json", repo_type="dataset",
52
+ token=self.token, local_dir=self.memory_path, local_dir_use_symlinks=False
 
 
 
 
53
  )
54
+ except Exception: self._save_local([])
55
+
56
+ def _init_xet_memory(self):
57
+ """STARTUP: Download Xet Vectors (JSON)."""
58
+ if not self.token or not self.xet_store: return
59
+ try:
60
+ local_path = hf_hub_download(
61
+ repo_id=self.dataset_id, filename=self.xet_dataset_file, repo_type="dataset",
62
+ token=self.token, local_dir=self.memory_path, local_dir_use_symlinks=False
63
+ )
64
+ # Restore to Xet Store
65
+ vectors = json.loads(Path(local_path).read_text())
66
+ for v in vectors:
67
+ self.xet_store.store_vector(v["id"], v["vector"], v["metadata"])
68
+ print(f"🧠 Restored {len(vectors)} vectors from Dataset")
69
+ except Exception as e:
70
+ print(f"⚠️ Xet restore failed (New dataset?): {e}")
71
+
72
+ def _backup_xet_to_dataset(self):
73
+ """Syncs all Xet vectors to HF Dataset as JSON."""
74
+ if not self.token or not self.xet_store: return
75
+
76
+ vectors = []
77
+ for f in self.xet_store.vectors_path.glob("*/*/*"):
78
+ if f.is_file():
79
+ try: vectors.append(json.loads(f.read_text()))
80
+ except: pass
81
+
82
+ backup_path = self.memory_path / self.xet_dataset_file
83
+ backup_path.write_text(json.dumps(vectors, indent=2))
84
+
85
+ try:
86
+ api = HfApi(token=self.token)
87
+ api.upload_file(
88
+ path_or_fileobj=backup_path, path_in_repo=self.xet_dataset_file,
89
+ repo_id=self.dataset_id, repo_type="dataset",
90
+ commit_message=f"🧠 Xet Backup: {len(vectors)} vectors"
91
+ )
92
+ print(f"☁️ Backed up {len(vectors)} vectors.")
93
+ except Exception as e:
94
+ print(f"⚠️ Xet backup failed: {e}")
95
+
96
+ # =========================================================================
97
+ # 🧬 EMBEDDINGS
98
+ # =========================================================================
99
+ def _get_embedding(self, text: str) -> List[float]:
100
+ if not self.client: return [0.0] * 384
101
+ try:
102
+ # feature-extraction returns list of floats
103
+ response = self.client.feature_extraction(text, model="sentence-transformers/all-MiniLM-L6-v2")
104
+ # Handle API return types (sometimes nested list)
105
+ return response[0] if isinstance(response[0], list) else response
106
+ except Exception: return [0.0] * 384
107
 
108
+ # =========================================================================
109
+ # πŸ““ NOTEBOOK
110
+ # =========================================================================
111
  def _save_local(self, notes: List[Dict]):
112
  self.memory_path.mkdir(parents=True, exist_ok=True)
113
  self.notebook_file.write_text(json.dumps(notes, indent=2), encoding='utf-8')
114
 
115
  def _save_notebook(self, notes: List[Dict]):
 
116
  self._save_local(notes)
117
  if self.token and self.dataset_id:
118
  try:
119
  api = HfApi(token=self.token)
120
  api.upload_file(
121
+ path_or_fileobj=self.notebook_file, path_in_repo="notebook.json",
122
+ repo_id=self.dataset_id, repo_type="dataset",
123
+ commit_message=f"Notebook Update: {len(notes)}"
 
 
124
  )
125
+ except Exception: pass
 
126
 
127
  def _load_notebook(self) -> List[Dict]:
128
  if not self.notebook_file.exists(): return []
129
  try: return json.loads(self.notebook_file.read_text(encoding='utf-8'))
130
  except: return []
131
 
 
 
 
132
  def notebook_read(self) -> str:
133
  notes = self._load_notebook()
134
  if not notes: return "Notebook is empty."
 
136
 
137
  def notebook_add(self, content: str) -> str:
138
  notes = self._load_notebook()
139
+ notes.append({"timestamp": time.strftime("%Y-%m-%d %H:%M"), "content": content})
 
140
  if len(notes) > 50: notes = notes[-50:]
141
  self._save_notebook(notes)
142
  return f"βœ… Note added & synced. ({len(notes)} items)"
 
150
  except IndexError: return "❌ Invalid index."
151
 
152
  # =========================================================================
153
+ # πŸ” SEARCH & MEMORY
154
  # =========================================================================
155
+ def save_conversation_turn(self, user_msg, assist_msg, turn_id):
156
+ if not self.xet_store: return
157
+ combined = f"USER: {user_msg}\n\nASSISTANT: {assist_msg}"
158
+ vector = self._get_embedding(combined)
159
+
160
+ self.xet_store.store_vector(
161
+ id=f"conv_{turn_id}_{int(time.time())}",
162
+ vector=vector,
163
+ metadata={
164
+ "type": "conversation",
165
+ "user": user_msg[:500],
166
+ "assistant": assist_msg[:500],
167
+ "content": combined,
168
+ "timestamp": time.time()
169
+ }
170
+ )
171
+ # Sync occasionally (Debounce could go here, for now sync on turn)
172
+ self._backup_xet_to_dataset()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
+ def search_conversations(self, query: str, n: int=5) -> List[Dict]:
175
+ if not self.xet_store: return []
176
+ query_vector = self._get_embedding(query)
177
+ results = self.xet_store.similarity_search(query_vector, n)
178
+
179
+ # Format strictly for app.py
180
+ return [{
181
+ "content": r.get("metadata", {}).get("content", ""),
182
+ "similarity": r.get("similarity", 0),
183
+ "id": r.get("id", "")
184
+ } for r in results]
185
 
 
 
 
186
  def search_code(self, query: str, n: int=5) -> List[Dict]:
187
  results = []
188
  try:
 
 
 
 
 
 
 
189
  for f in self.repo_path.rglob("*.py"):
190
+ if "venv" in str(f): continue
191
  txt = f.read_text(errors='ignore')
192
  if query in txt:
193
  results.append({"file": f.name, "snippet": txt[:300]})
194
  except: pass
195
  return results[:n]
196
+
 
 
 
 
 
197
  def search_testament(self, query: str, n: int=5) -> List[Dict]:
198
+ results = []
199
+ try:
200
+ for f in self.repo_path.rglob("*.md"):
201
+ txt = f.read_text(errors='ignore')
202
+ if query.lower() in txt.lower():
203
+ results.append({"file": f.name, "snippet": txt[:300]})
204
+ except: pass
205
+ return results[:n]
206
+
207
+ # =========================================================================
208
+ # πŸ› οΈ STANDARD TOOLS (Matched to Claude's Specs)
209
+ # =========================================================================
210
+ def read_file(self, path: str, start_line: int = None, end_line: int = None) -> str:
211
+ # Renamed params to match LLM output
212
+ try:
213
+ target = self.repo_path / path
214
+ content = target.read_text(encoding='utf-8', errors='ignore')
215
+ lines = content.splitlines()
216
+ if start_line is not None and end_line is not None:
217
+ lines = lines[start_line:end_line]
218
+ return "\n".join(lines)
219
+ except Exception as e: return str(e)
220
 
221
  def list_files(self, path: str = ".", max_depth: int = 3) -> str:
222
  try:
 
229
  return "\n".join(files[:50])
230
  except Exception as e: return str(e)
231
 
 
 
 
 
 
 
 
 
 
232
  def write_file(self, path: str, content: str) -> str:
233
  try:
234
  target = self.repo_path / path
 
244
  return f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
245
  except Exception as e: return f"Error: {e}"
246
 
247
+ def map_repository_structure(self) -> str:
248
+ graph = {"nodes": [], "edges": []}
 
 
 
 
 
 
 
 
 
 
249
  try:
250
+ file_count = 0
251
+ for file_path in self.repo_path.rglob('*.py'):
252
+ if 'venv' in str(file_path): continue
253
+ rel_path = str(file_path.relative_to(self.repo_path))
254
+ content = file_path.read_text(errors='ignore')
255
+ file_count += 1
256
+ graph["nodes"].append({"id": rel_path, "type": "file"})
257
+ try:
258
+ tree = ast.parse(content)
259
+ for node in ast.walk(tree):
260
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
261
+ node_id = f"{rel_path}::{node.name}"
262
+ graph["nodes"].append({"id": node_id, "type": "function"})
263
+ except SyntaxError: continue
264
+ return f"βœ… Map Generated: {file_count} files, {len(graph['nodes'])} nodes."
265
+ except Exception as e: return f"❌ Mapping failed: {e}"
266
 
267
+ def push_to_github(self, message: str) -> str: return "βœ… Push simulation."
268
+ def pull_from_github(self, branch: str) -> str: return "βœ… Pull simulation."
269
+ def create_shadow_branch(self) -> str: return "βœ… Shadow branch created."
 
 
 
 
270
  def get_stats(self) -> Dict:
271
  return {"total_files": len(list(self.repo_path.rglob("*"))), "conversations": 0}