sync: 194 file da Baida98/AI@4c1d2f92 (2026-08-30 07:22 UTC)

#149
by Baida07 - opened
api/public_snapshot.py CHANGED
@@ -8,49 +8,9 @@ from __future__ import annotations
8
 
9
  import asyncio
10
  import logging
11
- import os
12
  from typing import Any
13
 
14
- from .state import _agent_tasks, _loop_registry
15
-
16
- try:
17
- from supabase import create_client
18
- except ImportError: # pragma: no cover - deployment dependency guard
19
- create_client = None
20
-
21
- _snapshot_client: Any | None = None
22
- _snapshot_client_initialized = False
23
-
24
-
25
- def get_snapshot_client() -> Any | None:
26
- """Return one stable Supabase client for the canonical snapshot project.
27
-
28
- Unlike the general round-robin pool, this client never changes project
29
- between a write and a read. Dedicated variables can be used when the
30
- snapshot lives in a separate project; otherwise project A is canonical.
31
- """
32
- global _snapshot_client, _snapshot_client_initialized
33
- if _snapshot_client_initialized:
34
- return _snapshot_client
35
- _snapshot_client_initialized = True
36
- if create_client is None:
37
- _logger.warning("BOOT: public snapshot skipped — supabase package unavailable")
38
- return None
39
- url = os.getenv("PUBLIC_SNAPSHOT_SUPABASE_URL") or os.getenv("SUPABASE_URL")
40
- key = (
41
- os.getenv("PUBLIC_SNAPSHOT_SUPABASE_SERVICE_ROLE_KEY")
42
- or os.getenv("SUPABASE_SERVICE_ROLE_KEY")
43
- or os.getenv("SUPABASE_KEY")
44
- )
45
- if not url or not key:
46
- _logger.warning("BOOT: public snapshot skipped — canonical Supabase URL/key unavailable")
47
- return None
48
- try:
49
- _snapshot_client = create_client(url, key)
50
- _logger.info("BOOT: canonical Supabase snapshot client initialized")
51
- except Exception as exc:
52
- _logger.warning("BOOT: canonical Supabase snapshot client init failed type=%s", type(exc).__name__)
53
- return _snapshot_client
54
  from .version import RUNTIME_VERSION
55
 
56
  _logger = logging.getLogger("agente_ai.public_snapshot")
@@ -77,7 +37,7 @@ async def write_public_dashboard_snapshot() -> bool:
77
  public status route remains available with its degraded response while the
78
  service continues booting; the exact exception is kept in backend logs.
79
  """
80
- client = get_snapshot_client()
81
  if client is None:
82
  _logger.warning("BOOT: public snapshot skipped — Supabase client unavailable")
83
  return False
 
8
 
9
  import asyncio
10
  import logging
 
11
  from typing import Any
12
 
13
+ from .state import _agent_tasks, _loop_registry, sb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  from .version import RUNTIME_VERSION
15
 
16
  _logger = logging.getLogger("agente_ai.public_snapshot")
 
37
  public status route remains available with its degraded response while the
38
  service continues booting; the exact exception is kept in backend logs.
39
  """
40
+ client = sb()
41
  if client is None:
42
  _logger.warning("BOOT: public snapshot skipped — Supabase client unavailable")
43
  return False
api/public_snapshot_diagnostics.py CHANGED
@@ -12,7 +12,7 @@ from typing import Any, Optional
12
  from fastapi import APIRouter, Depends
13
 
14
  from .auth_guard import AuthRole, require_role
15
- from .public_snapshot import get_snapshot_client
16
 
17
  router = APIRouter(
18
  prefix="/api/diagnostics/public-snapshot",
@@ -40,7 +40,7 @@ def _safe_error(exc: Exception) -> dict[str, Optional[str]]:
40
  @router.get("")
41
  async def public_snapshot_diagnostics() -> dict[str, Any]:
42
  """Return snapshot presence and a sanitized exact Supabase error, if any."""
43
- client = get_snapshot_client()
44
  if client is None:
45
  return {
46
  "ok": False,
 
12
  from fastapi import APIRouter, Depends
13
 
14
  from .auth_guard import AuthRole, require_role
15
+ from .state import sb
16
 
17
  router = APIRouter(
18
  prefix="/api/diagnostics/public-snapshot",
 
40
  @router.get("")
41
  async def public_snapshot_diagnostics() -> dict[str, Any]:
42
  """Return snapshot presence and a sanitized exact Supabase error, if any."""
43
+ client = sb()
44
  if client is None:
45
  return {
46
  "ok": False,
api/public_status.py CHANGED
@@ -12,7 +12,7 @@ from typing import Any
12
 
13
  from fastapi import APIRouter
14
 
15
- from .public_snapshot import get_snapshot_client
16
 
17
  _logger = logging.getLogger("agente_ai.api.public_status")
18
  router = APIRouter(prefix="/api/public", tags=["public"])
@@ -26,7 +26,7 @@ _PUBLIC_FIELDS = (
26
  @router.get("/status")
27
  async def public_status() -> dict[str, Any]:
28
  """Restituisce esclusivamente lo snapshot deliberatamente pubblico."""
29
- client = get_snapshot_client()
30
  if client is None:
31
  return _degraded_snapshot("database_unavailable")
32
 
 
12
 
13
  from fastapi import APIRouter
14
 
15
+ from .state import sb
16
 
17
  _logger = logging.getLogger("agente_ai.api.public_status")
18
  router = APIRouter(prefix="/api/public", tags=["public"])
 
26
  @router.get("/status")
27
  async def public_status() -> dict[str, Any]:
28
  """Restituisce esclusivamente lo snapshot deliberatamente pubblico."""
29
+ client = sb()
30
  if client is None:
31
  return _degraded_snapshot("database_unavailable")
32
 
benchmark-extended.mjs ADDED
The diff for this file is too large to render. See raw diff
 
main.py CHANGED
@@ -253,9 +253,7 @@ async def startup_event():
253
  _logger.info("✅ BOOT: apply_rls_fix_sync() eseguito con successo.")
254
  except Exception as e:
255
  _logger.warning(f"⚠️ BOOT: apply_rls_fix_sync() fallito (non bloccante): {e}")
256
- # Schema e RLS sono gestiti dalle migrazioni versionate Supabase.
257
- # Non avviare auto-migrazioni in background: il boot deve restare
258
- # deterministico e non può dichiarare successo prima della migrazione.
259
  try:
260
  from api.public_snapshot import write_public_dashboard_snapshot
261
  snapshot_ok = await asyncio.wait_for(write_public_dashboard_snapshot(), timeout=12.0)
 
253
  _logger.info("✅ BOOT: apply_rls_fix_sync() eseguito con successo.")
254
  except Exception as e:
255
  _logger.warning(f"⚠️ BOOT: apply_rls_fix_sync() fallito (non bloccante): {e}")
256
+ asyncio.create_task(_run_auto_migration())
 
 
257
  try:
258
  from api.public_snapshot import write_public_dashboard_snapshot
259
  snapshot_ok = await asyncio.wait_for(write_public_dashboard_snapshot(), timeout=12.0)
scripts/lib/report-engine.mjs ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+
4
+ const DEFAULT_REPORT_DIRECTORY = "/tmp/agente-ai";
5
+
6
+ function safeSegment(value) {
7
+ return String(value ?? "benchmark")
8
+ .trim()
9
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
10
+ .replace(/^-+|-+$/g, "")
11
+ .slice(0, 96) || "benchmark";
12
+ }
13
+
14
+ /**
15
+ * Persiste un artefatto diagnostico del runner senza incidere sui calcoli.
16
+ * Il write-then-rename evita file JSON parziali in caso di interruzione.
17
+ */
18
+ export function saveBenchmarkReport(reportName, payload) {
19
+ const reportDirectory = resolve(process.env.BENCHMARK_REPORT_DIR || DEFAULT_REPORT_DIRECTORY);
20
+ mkdirSync(reportDirectory, { recursive: true });
21
+
22
+ const safeName = safeSegment(reportName);
23
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
24
+ const finalPath = join(reportDirectory, `${safeName}-${stamp}.json`);
25
+ const temporaryPath = `${finalPath}.${process.pid}.tmp`;
26
+ const document = {
27
+ schemaVersion: "benchmark-report-v1",
28
+ generatedAt: new Date().toISOString(),
29
+ reportName: safeName,
30
+ ...payload,
31
+ };
32
+
33
+ writeFileSync(temporaryPath, `${JSON.stringify(document, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
34
+ renameSync(temporaryPath, finalPath);
35
+ return finalPath;
36
+ }
tests/test_public_snapshot_writer.py CHANGED
@@ -4,14 +4,10 @@ from api import public_snapshot
4
 
5
 
6
  class _FakeTable:
7
- def __init__(self, calls, failures=0):
8
  self.calls = calls
9
- self.failures = failures
10
 
11
  def upsert(self, row, on_conflict=None):
12
- if self.failures:
13
- self.failures -= 1
14
- raise RuntimeError("temporary Supabase failure")
15
  self.calls.append((row, on_conflict))
16
  return self
17
 
@@ -20,22 +16,17 @@ class _FakeTable:
20
 
21
 
22
  class _FakeClient:
23
- def __init__(self, failures=0):
24
  self.calls = []
25
- self.failures = failures
26
 
27
  def table(self, name):
28
  assert name == "public_dashboard_snapshot"
29
- return _FakeTable(self.calls, self.failures)
30
-
31
-
32
- def _run(coro):
33
- return asyncio.run(coro)
34
 
35
 
36
  def test_snapshot_writer_is_idempotent_and_counts_runtime_state(monkeypatch):
37
  fake = _FakeClient()
38
- monkeypatch.setattr(public_snapshot, "get_snapshot_client", lambda: fake)
39
  monkeypatch.setattr(
40
  public_snapshot,
41
  "_agent_tasks",
@@ -47,8 +38,8 @@ def test_snapshot_writer_is_idempotent_and_counts_runtime_state(monkeypatch):
47
  )
48
  monkeypatch.setattr(public_snapshot, "_loop_registry", {"session-1": object()})
49
 
50
- assert _run(public_snapshot.write_public_dashboard_snapshot()) is True
51
- assert _run(public_snapshot.write_public_dashboard_snapshot()) is True
52
 
53
  assert len(fake.calls) == 2
54
  first, second = fake.calls
@@ -62,34 +53,6 @@ def test_snapshot_writer_is_idempotent_and_counts_runtime_state(monkeypatch):
62
  assert first[0]["service_status"] == "operational"
63
 
64
 
65
- def test_snapshot_writer_retries_transient_failure(monkeypatch):
66
- calls = []
67
-
68
- class _RetryTable:
69
- def upsert(self, row, on_conflict=None):
70
- calls.append((row, on_conflict))
71
- if len(calls) < 3:
72
- raise RuntimeError("temporary Supabase failure")
73
- return self
74
-
75
- def execute(self):
76
- return object()
77
-
78
- class _RetryClient:
79
- def table(self, name):
80
- assert name == "public_dashboard_snapshot"
81
- return _RetryTable()
82
-
83
- monkeypatch.setattr(public_snapshot, "get_snapshot_client", lambda: _RetryClient())
84
- async def _no_sleep(_seconds):
85
- return None
86
-
87
- monkeypatch.setattr(public_snapshot.asyncio, "sleep", _no_sleep)
88
-
89
- assert _run(public_snapshot.write_public_dashboard_snapshot()) is True
90
- assert len(calls) == 3
91
-
92
-
93
- def test_snapshot_writer_returns_false_when_supabase_is_unavailable(monkeypatch):
94
- monkeypatch.setattr(public_snapshot, "get_snapshot_client", lambda: None)
95
- assert _run(public_snapshot.write_public_dashboard_snapshot()) is False
 
4
 
5
 
6
  class _FakeTable:
7
+ def __init__(self, calls):
8
  self.calls = calls
 
9
 
10
  def upsert(self, row, on_conflict=None):
 
 
 
11
  self.calls.append((row, on_conflict))
12
  return self
13
 
 
16
 
17
 
18
  class _FakeClient:
19
+ def __init__(self):
20
  self.calls = []
 
21
 
22
  def table(self, name):
23
  assert name == "public_dashboard_snapshot"
24
+ return _FakeTable(self.calls)
 
 
 
 
25
 
26
 
27
  def test_snapshot_writer_is_idempotent_and_counts_runtime_state(monkeypatch):
28
  fake = _FakeClient()
29
+ monkeypatch.setattr(public_snapshot, "sb", lambda: fake)
30
  monkeypatch.setattr(
31
  public_snapshot,
32
  "_agent_tasks",
 
38
  )
39
  monkeypatch.setattr(public_snapshot, "_loop_registry", {"session-1": object()})
40
 
41
+ assert asyncio.run(public_snapshot.write_public_dashboard_snapshot()) is True
42
+ assert asyncio.run(public_snapshot.write_public_dashboard_snapshot()) is True
43
 
44
  assert len(fake.calls) == 2
45
  first, second = fake.calls
 
53
  assert first[0]["service_status"] == "operational"
54
 
55
 
56
+ def test_snapshot_writer_is_non_blocking_when_supabase_is_unavailable(monkeypatch):
57
+ monkeypatch.setattr(public_snapshot, "sb", lambda: None)
58
+ assert asyncio.run(public_snapshot.write_public_dashboard_snapshot()) is False