Spaces:
Configuration error
Configuration error
Baida—-
fix(registry): unterminated string literal line 692 — force re-sync [skip ci]
889ea3f verified | """ | |
| global_state_sync_with_oracle.py — Sincronizzazione Globale con Nodo E (Oracle) | |
| Integra il Nodo E (Oracle Cloud) nella logica di sincronizzazione del cluster. | |
| Architettura: | |
| B (PRIMARY): Stato globale, transazioni critiche | |
| C (Memory/RAG): Embeddings, conversazioni | |
| D (Audit): Event log, compliance | |
| E (Compute): Task distribuiti, carichi pesanti | |
| A (Cache): Read-only, analytics | |
| Flussi di Sincronizzazione: | |
| - B ↔ C: Memoria bidirezionale | |
| - B → D: Audit unidirezionale | |
| - B → E: Task distribuiti | |
| - E → B: Risultati task | |
| - A: Nessuna (read-only) | |
| """ | |
| import os | |
| import asyncio | |
| import logging | |
| from typing import Dict, List, Any, Optional | |
| from datetime import datetime, timedelta | |
| from enum import Enum | |
| import json | |
| _logger = logging.getLogger("global_state_sync_oracle") | |
| # ── Configurazione Nodi ──────────────────────────────────────────────────── | |
| ORACLE_NODE_E_ENABLED = os.getenv("ORACLE_NODE_E_ENABLED", "true").lower() == "true" | |
| ORACLE_NODE_E_IP = os.getenv("ORACLE_NODE_E_IP", "80.225.89.217") | |
| ORACLE_NODE_E_PORT = int(os.getenv("ORACLE_NODE_E_PORT", "8080")) | |
| SYNC_INTERVAL_SECONDS = int(os.getenv("SYNC_INTERVAL_SECONDS", "30")) | |
| SYNC_BATCH_SIZE = int(os.getenv("SYNC_BATCH_SIZE", "100")) | |
| SYNC_TIMEOUT_SECONDS = int(os.getenv("SYNC_TIMEOUT_SECONDS", "60")) | |
| # ── Enumerazione Tipo Sincronizzazione ───────────────────────────────────── | |
| class SyncType(str, Enum): | |
| MEMORY = "memory" # B ↔ C | |
| AUDIT = "audit" # B → D | |
| TASK = "task" # B → E | |
| RESULT = "result" # E → B | |
| CACHE = "cache" # A (read-only) | |
| # ── Classe Sincronizzatore ──────────────────────────────────────────────── | |
| class GlobalStateSyncWithOracle: | |
| """Gestore sincronizzazione globale con Nodo E.""" | |
| def __init__(self): | |
| self.sync_history = [] | |
| self.pending_tasks = [] | |
| self.last_sync_time = {} | |
| self.sync_stats = { | |
| "total_syncs": 0, | |
| "successful_syncs": 0, | |
| "failed_syncs": 0, | |
| "total_items_synced": 0, | |
| } | |
| async def sync_memory(self, session_id: str) -> Dict[str, Any]: | |
| """Sincronizza memoria tra B e C.""" | |
| try: | |
| _logger.info(f"Sincronizzazione memoria: {session_id}") | |
| # Recupera memoria da B | |
| memory_b = await self._fetch_from_node_b(f"agent_memory:{session_id}") | |
| # Sincronizza su C | |
| await self._sync_to_node_c(f"agent_memory:{session_id}", memory_b) | |
| # Sincronizza su E (se abilitato e ha task correlati) | |
| if ORACLE_NODE_E_ENABLED: | |
| await self._sync_to_node_e(f"memory:{session_id}", memory_b) | |
| self.sync_stats["successful_syncs"] += 1 | |
| self.sync_stats["total_items_synced"] += 1 | |
| return { | |
| "ok": True, | |
| "session_id": session_id, | |
| "synced_to": ["B", "C", "E"] if ORACLE_NODE_E_ENABLED else ["B", "C"], | |
| } | |
| except Exception as exc: | |
| _logger.error(f"Errore sincronizzazione memoria: {exc}") | |
| self.sync_stats["failed_syncs"] += 1 | |
| return {"ok": False, "error": str(exc)} | |
| async def sync_audit(self, event: Dict[str, Any]) -> Dict[str, Any]: | |
| """Sincronizza evento audit da B a D.""" | |
| try: | |
| _logger.info(f"Sincronizzazione audit: {event.get('event_type')}") | |
| # Sincronizza su D (append-only) | |
| await self._sync_to_node_d("audit_event", event) | |
| # Log anche su E se è un evento critico | |
| if ORACLE_NODE_E_ENABLED and event.get("severity") in ["critical", "error"]: | |
| await self._sync_to_node_e("audit_log", event) | |
| self.sync_stats["successful_syncs"] += 1 | |
| self.sync_stats["total_items_synced"] += 1 | |
| return { | |
| "ok": True, | |
| "event_id": event.get("id"), | |
| "synced_to": ["D", "E"] if ORACLE_NODE_E_ENABLED else ["D"], | |
| } | |
| except Exception as exc: | |
| _logger.error(f"Errore sincronizzazione audit: {exc}") | |
| self.sync_stats["failed_syncs"] += 1 | |
| return {"ok": False, "error": str(exc)} | |
| async def distribute_task_to_oracle(self, task: Dict[str, Any]) -> Dict[str, Any]: | |
| """Distribuisce un task computazionale al Nodo E (Oracle).""" | |
| if not ORACLE_NODE_E_ENABLED: | |
| return {"ok": False, "error": "Oracle Node E disabilitato"} | |
| try: | |
| _logger.info(f"Distribuzione task a Nodo E: {task.get('id')}") | |
| # Salva task in B (PRIMARY) | |
| task_id = await self._save_task_to_node_b(task) | |
| # Invia task a E | |
| result = await self._send_task_to_node_e(task_id, task) | |
| if result.get("ok"): | |
| self.pending_tasks.append({ | |
| "task_id": task_id, | |
| "node": "E", | |
| "created_at": datetime.now().isoformat(), | |
| "status": "pending", | |
| }) | |
| self.sync_stats["successful_syncs"] += 1 | |
| else: | |
| self.sync_stats["failed_syncs"] += 1 | |
| return result | |
| except Exception as exc: | |
| _logger.error(f"Errore distribuzione task: {exc}") | |
| self.sync_stats["failed_syncs"] += 1 | |
| return {"ok": False, "error": str(exc)} | |
| async def collect_task_results(self) -> Dict[str, Any]: | |
| """Raccoglie i risultati dei task dal Nodo E.""" | |
| if not ORACLE_NODE_E_ENABLED: | |
| return {"ok": False, "error": "Oracle Node E disabilitato"} | |
| try: | |
| _logger.info("Raccolta risultati task da Nodo E...") | |
| results = [] | |
| for task_info in self.pending_tasks[:]: | |
| if task_info["node"] == "E": | |
| result = await self._fetch_task_result_from_node_e(task_info["task_id"]) | |
| if result.get("ok") and result.get("status") == "completed": | |
| # Sincronizza risultato su B | |
| await self._sync_result_to_node_b(task_info["task_id"], result) | |
| results.append(result) | |
| self.pending_tasks.remove(task_info) | |
| self.sync_stats["successful_syncs"] += 1 | |
| return { | |
| "ok": True, | |
| "results_collected": len(results), | |
| "pending_tasks": len(self.pending_tasks), | |
| "results": results, | |
| } | |
| except Exception as exc: | |
| _logger.error(f"Errore raccolta risultati: {exc}") | |
| return {"ok": False, "error": str(exc)} | |
| async def sync_all(self) -> Dict[str, Any]: | |
| """Sincronizzazione completa tra tutti i nodi.""" | |
| try: | |
| _logger.info("Sincronizzazione completa iniziata...") | |
| start_time = datetime.now() | |
| # 1. Sincronizza memoria (B ↔ C) | |
| memory_sync = await self._sync_all_memory() | |
| # 2. Sincronizza audit (B → D) | |
| audit_sync = await self._sync_all_audit() | |
| # 3. Raccoglie risultati task da E | |
| task_results = None | |
| if ORACLE_NODE_E_ENABLED: | |
| task_results = await self.collect_task_results() | |
| elapsed = (datetime.now() - start_time).total_seconds() | |
| self.sync_stats["total_syncs"] += 1 | |
| self.last_sync_time["all"] = datetime.now().isoformat() | |
| return { | |
| "ok": True, | |
| "memory_synced": memory_sync.get("count", 0), | |
| "audit_synced": audit_sync.get("count", 0), | |
| "task_results_collected": task_results.get("results_collected", 0) if task_results else 0, | |
| "elapsed_seconds": elapsed, | |
| "stats": self.sync_stats, | |
| } | |
| except Exception as exc: | |
| _logger.error(f"Errore sincronizzazione completa: {exc}") | |
| self.sync_stats["failed_syncs"] += 1 | |
| return {"ok": False, "error": str(exc)} | |
| # ── Metodi Privati ──────────────────────────────────────────────────── | |
| async def _fetch_from_node_b(self, key: str) -> Any: | |
| """Recupera dato da Nodo B (PRIMARY).""" | |
| # Implementazione: Query Supabase B | |
| _logger.debug(f"Fetch da B: {key}") | |
| return {} # Placeholder | |
| async def _sync_to_node_c(self, key: str, data: Any) -> bool: | |
| """Sincronizza dato a Nodo C (Memory/RAG).""" | |
| # Implementazione: Insert/Update Supabase C | |
| _logger.debug(f"Sync a C: {key}") | |
| return True # Placeholder | |
| async def _sync_to_node_d(self, key: str, data: Any) -> bool: | |
| """Sincronizza evento audit a Nodo D (Audit).""" | |
| # Implementazione: Append-only insert Supabase D | |
| _logger.debug(f"Sync a D: {key}") | |
| return True # Placeholder | |
| async def _sync_to_node_e(self, key: str, data: Any) -> bool: | |
| """Sincronizza dato a Nodo E (Oracle Compute).""" | |
| if not ORACLE_NODE_E_ENABLED: | |
| return False | |
| try: | |
| import httpx | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| f"http://{ORACLE_NODE_E_IP}:{ORACLE_NODE_E_PORT}/api/sync/receive", | |
| json={"key": key, "data": data}, | |
| timeout=SYNC_TIMEOUT_SECONDS, | |
| ) | |
| return response.status_code == 200 | |
| except Exception as exc: | |
| _logger.error(f"Errore sync a E: {exc}") | |
| return False | |
| async def _save_task_to_node_b(self, task: Dict[str, Any]) -> str: | |
| """Salva task in B.""" | |
| # Implementazione: Insert Supabase B | |
| _logger.debug(f"Save task a B: {task.get('id')}") | |
| return task.get("id", "task_unknown") # Placeholder | |
| async def _send_task_to_node_e(self, task_id: str, task: Dict[str, Any]) -> Dict[str, Any]: | |
| """Invia task a Nodo E.""" | |
| if not ORACLE_NODE_E_ENABLED: | |
| return {"ok": False, "error": "Oracle Node E disabilitato"} | |
| try: | |
| import httpx | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| f"http://{ORACLE_NODE_E_IP}:{ORACLE_NODE_E_PORT}/api/tasks/submit", | |
| json={"task_id": task_id, "task": task}, | |
| timeout=SYNC_TIMEOUT_SECONDS, | |
| ) | |
| return response.json() if response.status_code == 200 else {"ok": False} | |
| except Exception as exc: | |
| _logger.error(f"Errore invio task a E: {exc}") | |
| return {"ok": False, "error": str(exc)} | |
| async def _fetch_task_result_from_node_e(self, task_id: str) -> Dict[str, Any]: | |
| """Recupera risultato task da Nodo E.""" | |
| if not ORACLE_NODE_E_ENABLED: | |
| return {"ok": False} | |
| try: | |
| import httpx | |
| async with httpx.AsyncClient() as client: | |
| response = await client.get( | |
| f"http://{ORACLE_NODE_E_IP}:{ORACLE_NODE_E_PORT}/api/tasks/{task_id}/result", | |
| timeout=SYNC_TIMEOUT_SECONDS, | |
| ) | |
| return response.json() if response.status_code == 200 else {"ok": False} | |
| except Exception as exc: | |
| _logger.error(f"Errore fetch risultato da E: {exc}") | |
| return {"ok": False} | |
| async def _sync_result_to_node_b(self, task_id: str, result: Dict[str, Any]) -> bool: | |
| """Sincronizza risultato task a B.""" | |
| # Implementazione: Update Supabase B | |
| _logger.debug(f"Sync risultato a B: {task_id}") | |
| return True # Placeholder | |
| async def _sync_all_memory(self) -> Dict[str, Any]: | |
| """Sincronizza tutta la memoria.""" | |
| _logger.debug("Sincronizzazione memoria completa...") | |
| return {"count": 0} # Placeholder | |
| async def _sync_all_audit(self) -> Dict[str, Any]: | |
| """Sincronizza tutti gli audit.""" | |
| _logger.debug("Sincronizzazione audit completa...") | |
| return {"count": 0} # Placeholder | |
| # ── Istanza Globale ─────────────────────────────────────────────────────── | |
| _sync_manager = GlobalStateSyncWithOracle() | |
| # ── Scheduler Sincronizzazione ──────────────────────────────────────────── | |
| async def start_sync_scheduler(): | |
| """Avvia lo scheduler di sincronizzazione periodica.""" | |
| _logger.info(f"Avvio scheduler sincronizzazione (intervallo: {SYNC_INTERVAL_SECONDS}s)...") | |
| while True: | |
| try: | |
| await asyncio.sleep(SYNC_INTERVAL_SECONDS) | |
| result = await _sync_manager.sync_all() | |
| if result.get("ok"): | |
| _logger.info(f"Sincronizzazione completata: {result}") | |
| else: | |
| _logger.warning(f"Sincronizzazione parziale: {result}") | |
| except Exception as exc: | |
| _logger.error(f"Errore scheduler: {exc}") | |
| # ── Funzioni Pubbliche ──────────────────────────────────────────────────── | |
| async def sync_memory(session_id: str) -> Dict[str, Any]: | |
| """Sincronizza memoria.""" | |
| return await _sync_manager.sync_memory(session_id) | |
| async def sync_audit(event: Dict[str, Any]) -> Dict[str, Any]: | |
| """Sincronizza audit.""" | |
| return await _sync_manager.sync_audit(event) | |
| async def distribute_task(task: Dict[str, Any]) -> Dict[str, Any]: | |
| """Distribuisce task a Nodo E.""" | |
| return await _sync_manager.distribute_task_to_oracle(task) | |
| async def get_sync_stats() -> Dict[str, Any]: | |
| """Ritorna statistiche sincronizzazione.""" | |
| return _sync_manager.sync_stats | |