SynergixAI commited on
Commit
45fcc7c
·
verified ·
1 Parent(s): 80818b3

Delete scripts

Browse files
Files changed (1) hide show
  1. scripts/fusion_brain.py +0 -360
scripts/fusion_brain.py DELETED
@@ -1,360 +0,0 @@
1
- import asyncio
2
- import hashlib
3
- import json
4
- import logging
5
- import os
6
- import sys
7
- import time
8
- from datetime import datetime
9
- from typing import Any
10
-
11
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
12
-
13
- from dotenv import load_dotenv
14
- load_dotenv(os.path.join(os.path.dirname(__file__), "..", "backend", ".env"))
15
- load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
16
-
17
- from backend.services.greenfield import greenfield_client
18
- from backend.services.groq_client import groq_wrapper
19
- from config.system_prompts import get_evolution_system
20
-
21
- logging.basicConfig(
22
- level=logging.INFO,
23
- format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
24
- )
25
- logger = logging.getLogger("synergix.fusion_brain")
26
-
27
- # ── Rutas de salida ───────────────────────────────────────────────────────────
28
- BRAIN_DIR = os.path.join(os.path.dirname(__file__), "..", "SYNERGIXAI")
29
- BRAIN_FILE = os.path.join(BRAIN_DIR, "Synergix_ia.txt")
30
- BRAIN_JSON = os.path.join(BRAIN_DIR, "Synergix_ia.json")
31
- FAISS_INDEX_FILE = os.path.join(BRAIN_DIR, "synergix_index.faiss")
32
- BACKUP_DIR = os.path.join(os.path.dirname(__file__), "..", "backups")
33
- DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
34
- HOT_CACHE_FILE = os.path.join(DATA_DIR, "hot_cache.json")
35
-
36
- os.makedirs(BRAIN_DIR, exist_ok=True)
37
- os.makedirs(BACKUP_DIR, exist_ok=True)
38
- os.makedirs(DATA_DIR, exist_ok=True)
39
-
40
-
41
- # ── Backup del cerebro actual ─────────────────────────────────────────────────
42
-
43
- def backup_brain() -> str:
44
- """Crea backup del cerebro actual con hash de integridad."""
45
- if not os.path.exists(BRAIN_FILE):
46
- return ""
47
-
48
- ts = datetime.now().strftime("%Y%m%d_%H%M%S")
49
- backup_path = os.path.join(BACKUP_DIR, f"snapshot_{ts}.bak")
50
-
51
- with open(BRAIN_FILE, "r", encoding="utf-8") as f:
52
- content = f.read()
53
-
54
- integrity_hash = hashlib.sha256(content.encode()).hexdigest()
55
-
56
- with open(backup_path, "w", encoding="utf-8") as f:
57
- f.write(content)
58
-
59
- # Guardar metadatos del backup
60
- meta_path = backup_path + ".meta.json"
61
- with open(meta_path, "w", encoding="utf-8") as f:
62
- json.dump({
63
- "timestamp": ts,
64
- "integrity_hash": integrity_hash,
65
- "size": len(content),
66
- "source": BRAIN_FILE,
67
- }, f, indent=2)
68
-
69
- logger.info("💾 Backup creado: %s (hash: %s...)", backup_path, integrity_hash[:16])
70
- return backup_path
71
-
72
-
73
- def restore_latest_backup() -> bool:
74
- """Restaura el último backup si el cerebro está corrupto."""
75
- backups = sorted([
76
- f for f in os.listdir(BACKUP_DIR)
77
- if f.startswith("snapshot_") and f.endswith(".bak")
78
- ], reverse=True)
79
-
80
- if not backups:
81
- logger.error("❌ No hay backups disponibles para restaurar")
82
- return False
83
-
84
- latest = os.path.join(BACKUP_DIR, backups[0])
85
- meta_path = latest + ".meta.json"
86
-
87
- with open(latest, "r", encoding="utf-8") as f:
88
- content = f.read()
89
-
90
- # Verificar integridad
91
- if os.path.exists(meta_path):
92
- with open(meta_path, "r", encoding="utf-8") as f:
93
- meta = json.load(f)
94
- current_hash = hashlib.sha256(content.encode()).hexdigest()
95
- if current_hash != meta.get("integrity_hash"):
96
- logger.error("❌ Backup corrupto: hash no coincide")
97
- return False
98
-
99
- with open(BRAIN_FILE, "w", encoding="utf-8") as f:
100
- f.write(content)
101
-
102
- logger.info("✅ Cerebro restaurado desde: %s", latest)
103
- return True
104
-
105
-
106
- # ── Descarga y procesamiento de aportes ──────────────────────────────────────
107
-
108
- async def fetch_all_aportes(max_items: int = 100) -> list[dict[str, Any]]:
109
- """
110
- Descarga aportes de alta calidad desde Greenfield.
111
- Incluye contenido completo + metadatos.
112
- """
113
- logger.info("📥 Descargando aportes de Greenfield...")
114
-
115
- metadata_list = await greenfield_client.get_aportes_metadata(
116
- prefix="aportes/",
117
- min_quality=5,
118
- max_items=max_items,
119
- )
120
-
121
- logger.info("📊 %d aportes de calidad encontrados", len(metadata_list))
122
-
123
- # Descargar contenido de los top aportes (máx 50 para no sobrecargar)
124
- sem = asyncio.Semaphore(5)
125
- aportes = []
126
-
127
- async def download_one(meta: dict) -> dict | None:
128
- async with sem:
129
- obj_name = meta.get("object_name", "")
130
- if not obj_name:
131
- return None
132
- try:
133
- content = await greenfield_client.get_object(obj_name)
134
- if content and len(content.strip()) > 10:
135
- return {
136
- "object_name": obj_name,
137
- "content": content,
138
- "summary": meta.get("summary", ""),
139
- "quality_score": meta.get("quality_score", 5),
140
- "knowledge_tag": meta.get("knowledge_tag", "general"),
141
- "user_id": meta.get("user_id", ""),
142
- "impact": meta.get("impact", 0),
143
- }
144
- except Exception as exc:
145
- logger.warning("⚠️ Error descargando %s: %s", obj_name, exc)
146
- return None
147
-
148
- tasks = [download_one(m) for m in metadata_list[:50]]
149
- results = await asyncio.gather(*tasks)
150
- aportes = [r for r in results if r is not None]
151
-
152
- logger.info("✅ %d aportes descargados con contenido", len(aportes))
153
- return aportes
154
-
155
-
156
- # ── Generación del cerebro fusionado ─────────────────────────────────────────
157
-
158
- async def generate_fused_brain(aportes: list[dict]) -> str:
159
- """
160
- Usa Groq para fusionar todos los aportes en un cerebro unificado.
161
- """
162
- if not aportes:
163
- return "Synergix Collective Brain - Sin aportes aún."
164
-
165
- # Construir contexto organizado por categoría
166
- by_tag: dict[str, list[str]] = {}
167
- for a in aportes:
168
- tag = a.get("knowledge_tag", "general")
169
- if tag not in by_tag:
170
- by_tag[tag] = []
171
- by_tag[tag].append(a.get("content", a.get("summary", "")))
172
-
173
- sections = []
174
- for tag, contents in by_tag.items():
175
- section = f"[Categoría: {tag}]\n" + "\n---\n".join(c[:300] for c in contents[:5])
176
- sections.append(section)
177
-
178
- context = "\n\n".join(sections[:10])[:4000] # Máx 4000 chars para el prompt
179
-
180
- evolution_system = get_evolution_system()
181
- logger.info("🧠 Generando cerebro fusionado con Groq...")
182
-
183
- fused = await groq_wrapper.federated_evolve(context, evolution_system)
184
- return fused
185
-
186
-
187
- # ── Actualización del índice FAISS ────────────────────────────────────────────
188
-
189
- def build_faiss_index(aportes: list[dict]) -> bool:
190
- """
191
- Construye índice FAISS con embeddings de los aportes.
192
- Usa sentence-transformers si disponible, sino TF-IDF simple.
193
- """
194
- try:
195
- import numpy as np
196
-
197
- try:
198
- # Intentar con sentence-transformers (calidad alta)
199
- from sentence_transformers import SentenceTransformer
200
- model = SentenceTransformer("all-MiniLM-L6-v2")
201
- texts = [a.get("content", a.get("summary", ""))[:512] for a in aportes]
202
- embeddings = model.encode(texts, show_progress_bar=False)
203
- logger.info("✅ Embeddings con sentence-transformers shape=%s", embeddings.shape)
204
- except ImportError:
205
- # Fallback: TF-IDF vectorization simple
206
- from sklearn.feature_extraction.text import TfidfVectorizer
207
- texts = [a.get("content", a.get("summary", ""))[:512] for a in aportes]
208
- vectorizer = TfidfVectorizer(max_features=256)
209
- embeddings = vectorizer.fit_transform(texts).toarray().astype(np.float32)
210
- logger.info("✅ Embeddings TF-IDF shape=%s", embeddings.shape)
211
-
212
- # Guardar vectorizer para búsquedas futuras
213
- import pickle
214
- with open(os.path.join(BRAIN_DIR, "tfidf_vectorizer.pkl"), "wb") as f:
215
- pickle.dump(vectorizer, f)
216
-
217
- import faiss
218
- dim = embeddings.shape[1]
219
- index = faiss.IndexFlatL2(dim)
220
- index.add(embeddings.astype(np.float32))
221
- faiss.write_index(index, FAISS_INDEX_FILE)
222
-
223
- # Guardar mapping índice → aporte
224
- mapping = [{"idx": i, "object_name": a.get("object_name", ""), "summary": a.get("summary", "")}
225
- for i, a in enumerate(aportes)]
226
- with open(os.path.join(BRAIN_DIR, "index_mapping.json"), "w", encoding="utf-8") as f:
227
- json.dump(mapping, f, ensure_ascii=False, indent=2)
228
-
229
- logger.info("✅ FAISS index construido: %d vectores dim=%d", index.ntotal, dim)
230
- return True
231
-
232
- except ImportError as exc:
233
- logger.warning("⚠️ FAISS/sklearn no disponible, saltando índice vectorial: %s", exc)
234
- return False
235
- except Exception as exc:
236
- logger.error("❌ Error construyendo FAISS: %s", exc)
237
- return False
238
-
239
-
240
- # ── Actualizar hot_cache.json ─────────────────────────────────────────────────
241
-
242
- def update_hot_cache(aportes: list[dict], fused_brain: str) -> None:
243
- """Actualiza el hot_cache.json con los aportes procesados."""
244
- try:
245
- cache_data = {
246
- "last_update_ts": time.time(),
247
- "last_update_iso": datetime.now().isoformat(),
248
- "count": len(aportes),
249
- "metadata_cache": {
250
- a["object_name"]: {
251
- "object_name": a["object_name"],
252
- "summary": a.get("summary", ""),
253
- "quality_score": a.get("quality_score", 5),
254
- "knowledge_tag": a.get("knowledge_tag", "general"),
255
- "user_id": a.get("user_id", ""),
256
- "impact": a.get("impact", 0),
257
- }
258
- for a in aportes if a.get("object_name")
259
- },
260
- "wisdom_snippets": [
261
- {
262
- "name": a.get("object_name", ""),
263
- "summary": a.get("summary", ""),
264
- "score": a.get("quality_score", 5),
265
- }
266
- for a in aportes[:20]
267
- ],
268
- "fused_wisdom_preview": fused_brain[:500],
269
- }
270
- with open(HOT_CACHE_FILE, "w", encoding="utf-8") as f:
271
- json.dump(cache_data, f, ensure_ascii=False, indent=2)
272
- logger.info("💾 hot_cache.json actualizado con %d aportes", len(aportes))
273
- except Exception as exc:
274
- logger.error("❌ Error actualizando hot_cache: %s", exc)
275
-
276
-
277
- # ── Main ───────────────────────────────────────────────────────────────────────
278
-
279
- async def run_fusion() -> None:
280
- """Ejecuta el ciclo completo de fusión del cerebro."""
281
- logger.info("🧠 === SYNERGIX FUSION BRAIN INICIADO ===")
282
- start = time.time()
283
-
284
- # 1. Backup del cerebro actual
285
- backup_path = backup_brain()
286
- if backup_path:
287
- logger.info("💾 Backup previo: %s", backup_path)
288
-
289
- # 2. Descargar aportes de Greenfield
290
- try:
291
- aportes = await fetch_all_aportes(max_items=100)
292
- except Exception as exc:
293
- logger.error("❌ Error descargando aportes: %s", exc)
294
- aportes = []
295
-
296
- if not aportes:
297
- logger.warning("📭 Sin aportes para procesar. Fusión cancelada.")
298
- return
299
-
300
- # 3. Generar cerebro fusionado con Groq
301
- try:
302
- fused_brain = await generate_fused_brain(aportes)
303
- except Exception as exc:
304
- logger.error("❌ Error generando cerebro fusionado: %s", exc)
305
- fused_brain = "\n".join(a.get("summary", "") for a in aportes[:20])
306
-
307
- # 4. Guardar cerebro actualizado
308
- brain_content = f"""=== SYNERGIX COLLECTIVE BRAIN ===
309
- Actualizado: {datetime.now().isoformat()}
310
- Aportes procesados: {len(aportes)}
311
- Vector count: {len(aportes)}
312
-
313
- === CONOCIMIENTO FUSIONADO ===
314
- {fused_brain}
315
-
316
- === INVENTARIO DE APORTES ===
317
- {chr(10).join(f"- [{a['knowledge_tag']}] {a['summary']}" for a in aportes[:50])}
318
- """
319
-
320
- with open(BRAIN_FILE, "w", encoding="utf-8") as f:
321
- f.write(brain_content)
322
-
323
- # 5. Guardar cerebro como JSON (para el bot)
324
- brain_json_data = {
325
- "last_sync": datetime.now().isoformat(),
326
- "x-amz-meta-last-sync": datetime.now().isoformat(),
327
- "x-amz-meta-vector-count": len(aportes),
328
- "total_aportes": len(aportes),
329
- "fused_wisdom": fused_brain,
330
- "categories": list({a.get("knowledge_tag", "general") for a in aportes}),
331
- "top_aportes": [
332
- {
333
- "object_name": a["object_name"],
334
- "summary": a["summary"],
335
- "quality_score": a["quality_score"],
336
- "knowledge_tag": a["knowledge_tag"],
337
- }
338
- for a in sorted(aportes, key=lambda x: -x.get("quality_score", 0))[:20]
339
- ],
340
- }
341
- with open(BRAIN_JSON, "w", encoding="utf-8") as f:
342
- json.dump(brain_json_data, f, ensure_ascii=False, indent=2)
343
-
344
- logger.info("✅ Synergix_ia.txt y .json actualizados (%d chars)", len(brain_content))
345
-
346
- # 6. Construir/actualizar índice FAISS
347
- faiss_ok = build_faiss_index(aportes)
348
- if faiss_ok:
349
- logger.info("✅ synergix_index.faiss actualizado")
350
-
351
- # 7. Actualizar hot_cache.json
352
- update_hot_cache(aportes, fused_brain)
353
-
354
- elapsed = time.time() - start
355
- logger.info("🌟 === FUSION BRAIN COMPLETO en %.1fs ===", elapsed)
356
- logger.info("📊 Stats: %d aportes | FAISS: %s", len(aportes), "✅" if faiss_ok else "⚠️ skip")
357
-
358
-
359
- if __name__ == "__main__":
360
- asyncio.run(run_fusion())