sync: 141 file da Baida98/AI@35be4001 (2026-08-07 14:07 UTC)

#14
by Baida07 - opened
Files changed (1) hide show
  1. api/state.py +122 -4
api/state.py CHANGED
@@ -5,8 +5,8 @@ TTL constants, prune helpers. Extracted from main.py β€” zero behaviour change.
5
  """
6
  import os, time, asyncio as _asyncio_mod, json as _json, re as _re
7
  import logging
8
- from typing import Optional, Any
9
- from fastapi import HTTPException, APIRouter, Request
10
  from pydantic import BaseModel, field_validator
11
 
12
  _logger = logging.getLogger("api.state")
@@ -32,7 +32,6 @@ _current_client_idx = 0
32
 
33
  try:
34
  from supabase import create_client
35
-
36
  # S-FIX: Preferisce SERVICE_ROLE_KEY per bypassare RLS nelle operazioni di sistema
37
  def _get_key(p):
38
  return os.getenv(f"SUPABASE_SERVICE_ROLE_KEY_{p}") or os.getenv(f"SUPABASE_SERVICE_ROLE_{p}") or \
@@ -46,7 +45,6 @@ try:
46
  {"id": "D", "url": os.getenv("SUPABASE_URL_4") or os.getenv("SUPABASE_URL_D"), "key": _get_key("D")},
47
  {"id": "E", "url": os.getenv("SUPABASE_URL_5") or os.getenv("SUPABASE_URL_E"), "key": _get_key("E")},
48
  ]
49
-
50
  for cfg in PROJECT_CONFIGS:
51
  if cfg["url"] and cfg["key"]:
52
  try:
@@ -54,6 +52,126 @@ try:
54
  _clients.append({"client": c, "id": cfg["id"], "status": "connected"})
55
  _logger.info(f"BOOT: Supabase #{cfg['id']} connected OK")
56
  except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  health["status"] = "degraded"
58
  health["database"] = f"RAW_ERROR: {str(e)}"
59
  return health
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  """
6
  import os, time, asyncio as _asyncio_mod, json as _json, re as _re
7
  import logging
8
+ from typing import Optional, Any, AsyncIterator, List, Tuple
9
+ from fastapi import HTTPException, APIRouter, Request, Body
10
  from pydantic import BaseModel, field_validator
11
 
12
  _logger = logging.getLogger("api.state")
 
32
 
33
  try:
34
  from supabase import create_client
 
35
  # S-FIX: Preferisce SERVICE_ROLE_KEY per bypassare RLS nelle operazioni di sistema
36
  def _get_key(p):
37
  return os.getenv(f"SUPABASE_SERVICE_ROLE_KEY_{p}") or os.getenv(f"SUPABASE_SERVICE_ROLE_{p}") or \
 
45
  {"id": "D", "url": os.getenv("SUPABASE_URL_4") or os.getenv("SUPABASE_URL_D"), "key": _get_key("D")},
46
  {"id": "E", "url": os.getenv("SUPABASE_URL_5") or os.getenv("SUPABASE_URL_E"), "key": _get_key("E")},
47
  ]
 
48
  for cfg in PROJECT_CONFIGS:
49
  if cfg["url"] and cfg["key"]:
50
  try:
 
52
  _clients.append({"client": c, "id": cfg["id"], "status": "connected"})
53
  _logger.info(f"BOOT: Supabase #{cfg['id']} connected OK")
54
  except Exception as e:
55
+ _logger.error(f"BOOT: Supabase #{cfg['id']} init failed: {e}")
56
+ except ImportError:
57
+ _logger.error("BOOT: Supabase init module failed: create_client not found.")
58
+
59
+ def _get_sb() -> Any:
60
+ """Ritorna il client Supabase corrente dal pool (round-robin)."""
61
+ global _current_client_idx
62
+ if not _clients: return None
63
+ # S-FIX: Salta i client marcati come "failed" (semplice circuit breaker)
64
+ for _ in range(len(_clients)):
65
+ entry = _clients[_current_client_idx]
66
+ _current_client_idx = (_current_client_idx + 1) % len(_clients)
67
+ if entry["status"] != "failed":
68
+ return entry["client"]
69
+ return _clients[0]["client"] if _clients else None
70
+
71
+ _sb = _get_sb()
72
+
73
+ @router.get("/health")
74
+ async def health_check(request: Request):
75
+ health = {
76
+ "status": "ok",
77
+ "timestamp": time.time(),
78
+ "version": "1.5.5",
79
+ "database": "unknown",
80
+ "pool_size": len(_clients)
81
+ }
82
+ try:
83
+ if _sb:
84
+ try:
85
+ res = _sb.table("agent_memory").select("key").limit(1).execute()
86
+ health["database"] = "connected"
87
+ except Exception as inner_e:
88
+ for entry in _clients:
89
+ if entry["client"] == _sb:
90
+ entry["status"] = "failed"
91
+ break
92
+ raise inner_e
93
+ else:
94
+ health["database"] = "disconnected"
95
+ except Exception as e:
96
  health["status"] = "degraded"
97
  health["database"] = f"RAW_ERROR: {str(e)}"
98
  return health
99
+
100
+ # ── SENSITIVE keys set (Z-GAP-4) ──────────────────────────────────────────────
101
+ SENSITIVE = {
102
+ 'OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY', 'GROQ_API_KEY',
103
+ 'HF_TOKEN', 'HUGGINGFACE_API_KEY', 'GH_TOKEN', 'GITHUB_TOKEN',
104
+ 'QDRANT_API_KEY', 'DATABASE_URL', 'SESSION_SECRET', 'SECRET_KEY',
105
+ 'RAILWAY_TOKEN', 'SUPABASE_KEY', 'SUPABASE_ANON_KEY',
106
+ 'TELEGRAM_BOT_TOKEN', 'TELEGRAM_CHAT_ID',
107
+ 'CF_API_TOKEN', 'CLOUDFLARE_API_TOKEN', 'CF_ACCOUNT_ID',
108
+ 'CF_API_TOKEN_B', 'CF_ACCOUNT_ID_B',
109
+ 'CEREBRAS_API_KEY', 'SAMBANOVA_API_KEY',
110
+ 'VAULT_KEY', 'INTERNAL_TOKEN', 'DEPLOY_SECRET', 'WEBHOOK_TOKEN',
111
+ 'TERMINAL_SECRET', 'EXEC_TOKEN', 'VITE_INTERNAL_TOKEN', 'VITE_TERMINAL_SECRET',
112
+ 'VITE_OPENROUTER_API_KEY', 'VITE_HF_TOKEN', 'VITE_GROQ_API_KEY',
113
+ 'GH_PAGES_TOKEN', 'VERCEL_TOKEN',
114
+ }
115
+
116
+ # ── In-memory stores ──────────────────────────────────────────────────────────
117
+ _mem_fallback: dict[str, dict] = {}
118
+ _agent_tasks: dict[str, dict] = {}
119
+ _run_stream_tasks: dict[str, dict] = {}
120
+ _loop_registry: dict[str, dict] = {}
121
+ _LOOP_REGISTRY_TTL_S: float = 10 * 60
122
+ _task_checkpoints: dict[str, dict] = {}
123
+ _CHECKPOINT_TTL_MS = 2 * 60 * 60 * 1000
124
+ _CHECKPOINT_MAX = 100
125
+ _AGENT_TASK_TTL_MS = 2 * 60 * 60 * 1000
126
+ _AGENT_TASK_MAX = 200
127
+
128
+ # ── Telemetry & Health ────────────────────────────────────────────────────────
129
+ _ai_health_cache: dict = {"data": None, "at": 0.0}
130
+ _AI_HEALTH_TTL = 60.0
131
+ _heartbeat_state: dict = {
132
+ "last_run_at": None,
133
+ "next_run_at": None,
134
+ "best_provider": None,
135
+ "best_latency_ms": None,
136
+ "providers": [],
137
+ "runs": 0,
138
+ }
139
+
140
+ # ── Singleton Getters ──────────────────────────────────────────���──────────────
141
+ def get_supabase() -> Optional[Any]:
142
+ return _sb
143
+
144
+ _mem_manager: Any = None
145
+ _mem_manager_inited = False
146
+ def _get_mem_manager() -> Any:
147
+ global _mem_manager, _mem_manager_inited
148
+ if _mem_manager_inited: return _mem_manager
149
+ try:
150
+ from memory.memory_manager import MemoryManager
151
+ _mem_manager = MemoryManager()
152
+ _mem_manager_inited = True
153
+ except Exception: _mem_manager = None
154
+ return _mem_manager
155
+
156
+ _executor: Any = None
157
+ def _get_executor() -> Any:
158
+ global _executor
159
+ if _executor is not None: return _executor
160
+ try:
161
+ from agents.executor import Executor
162
+ _executor = Executor(memory=_get_mem_manager())
163
+ except Exception: _executor = None
164
+ return _executor
165
+
166
+ _ai_client: Any = None
167
+ def _get_ai_client() -> Any:
168
+ global _ai_client
169
+ if _ai_client is not None: return _ai_client
170
+ try:
171
+ from models.ai_client import AIClient
172
+ _ai_client = AIClient()
173
+ except Exception: _ai_client = None
174
+ return _ai_client
175
+
176
+ async def _get_mem_manager_async() -> Any:
177
+ return _get_mem_manager()