Spaces:
Running
Running
sync: 157 file da Baida98/AI@eeb3e0e9 (2026-08-15 15:39 UTC) [deploy-all]
#40
by Baida07 - opened
- api/benchmark_handler.py +35 -14
- api/telegram_webhook.py +4 -3
- benchmark-extended.mjs +13 -2
- tests/test_telegram_extended_benchmark.py +32 -0
api/benchmark_handler.py
CHANGED
|
@@ -36,6 +36,11 @@ _BENCH_SCRIPT = next(
|
|
| 36 |
os.path.join(_REPO_ROOT, "benchmark-extended.mjs"),
|
| 37 |
)
|
| 38 |
_REPORT_V7 = "/tmp/agente-ai/benchmark-v5-latest.json"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
# 20 task seriali possono richiedere piΓΉ di 12 minuti con provider gratuiti.
|
| 40 |
_BENCH_TIMEOUT = float(os.getenv("BENCH_TIMEOUT_SECS", "3600"))
|
| 41 |
|
|
@@ -50,12 +55,26 @@ async def run_benchmark_task(chat_id: int, send_reply_fn, mode: str = "full") ->
|
|
| 50 |
"Il deployment non ha incluso <code>benchmark-extended.mjs</code>.")
|
| 51 |
return
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
env = {
|
| 60 |
**os.environ,
|
| 61 |
"INTERNAL_TOKEN": os.getenv("INTERNAL_TOKEN", ""),
|
|
@@ -96,25 +115,26 @@ async def run_benchmark_task(chat_id: int, send_reply_fn, mode: str = "full") ->
|
|
| 96 |
await send_reply_fn(chat_id, f"π₯ <b>Errore critico benchmark:</b> <code>{exc}</code>")
|
| 97 |
return
|
| 98 |
|
| 99 |
-
report_exists = await asyncio.to_thread(os.path.exists,
|
| 100 |
if not report_exists:
|
| 101 |
await send_reply_fn(chat_id, "β οΈ <b>Benchmark Extended terminato ma report non trovato.</b>")
|
| 102 |
return
|
| 103 |
try:
|
| 104 |
-
report: dict[str, Any] = await asyncio.to_thread(_read_json,
|
| 105 |
except Exception as exc:
|
| 106 |
await send_reply_fn(chat_id, f"β οΈ <b>Report Extended non leggibile:</b> <code>{exc}</code>")
|
| 107 |
return
|
| 108 |
|
| 109 |
categories = {str(task.get("cat", "")) for task in report.get("tasks", []) if task.get("cat")}
|
| 110 |
-
|
| 111 |
-
|
|
|
|
| 112 |
" Nessun risultato incompleto viene presentato come benchmark completo.")
|
| 113 |
return
|
| 114 |
-
await send_reply_fn(chat_id, _format_v7_report(report))
|
| 115 |
|
| 116 |
|
| 117 |
-
def _format_v7_report(report: dict[str, Any]) -> str:
|
| 118 |
"""Formatta il report v7 per Telegram HTML."""
|
| 119 |
s = report.get("summary", {})
|
| 120 |
ts = (report.get("timestamp") or "")[:16].replace("T", " ")
|
|
@@ -132,7 +152,8 @@ def _format_v7_report(report: dict[str, Any]) -> str:
|
|
| 132 |
lines: list[str] = [
|
| 133 |
f"π <b>Benchmark {ver} completato!</b>\n\n"
|
| 134 |
f"π <b>Score agente:</b> <code>{avg}/100</code>\n"
|
| 135 |
-
f"π
<b>Run:</b> <code>{ts}</code>\n
|
|
|
|
| 136 |
"π <b>Confronto vs riferimenti:</b>\n"
|
| 137 |
f" β’ Replit: <code>{repl}/100</code>\n"
|
| 138 |
f" β’ Cursor: <code>{curs}/100</code>\n"
|
|
@@ -160,7 +181,7 @@ def _format_v7_report(report: dict[str, Any]) -> str:
|
|
| 160 |
scored = s.get("scoredTaskCount", sum(len(v) for v in by_cat.values()))
|
| 161 |
skipped = s.get("skippedTaskCount", max(0, attempted - scored))
|
| 162 |
lines.append(
|
| 163 |
-
f"π§ͺ <b>Copertura:</b> <code>{len(attempted_categories)}/
|
| 164 |
f"{scored} valutabili Β· {skipped} non valutabili</code>\n"
|
| 165 |
)
|
| 166 |
if by_cat:
|
|
|
|
| 36 |
os.path.join(_REPO_ROOT, "benchmark-extended.mjs"),
|
| 37 |
)
|
| 38 |
_REPORT_V7 = "/tmp/agente-ai/benchmark-v5-latest.json"
|
| 39 |
+
_REPORT_V7_WEAK = "/tmp/agente-ai/benchmark-v5-weak-latest.json"
|
| 40 |
+
_WEAK_CATEGORIES = (
|
| 41 |
+
"sql", "context_window", "reasoning", "data_analysis", "research_synthesis",
|
| 42 |
+
"mmlu", "technical_writing", "code_correct", "feature", "security",
|
| 43 |
+
)
|
| 44 |
# 20 task seriali possono richiedere piΓΉ di 12 minuti con provider gratuiti.
|
| 45 |
_BENCH_TIMEOUT = float(os.getenv("BENCH_TIMEOUT_SECS", "3600"))
|
| 46 |
|
|
|
|
| 55 |
"Il deployment non ha incluso <code>benchmark-extended.mjs</code>.")
|
| 56 |
return
|
| 57 |
|
| 58 |
+
is_weak_run = mode == "weak"
|
| 59 |
+
if is_weak_run:
|
| 60 |
+
report_path = _REPORT_V7_WEAK
|
| 61 |
+
flags = [
|
| 62 |
+
f"--categories={','.join(_WEAK_CATEGORIES)}", "--json",
|
| 63 |
+
f"--output={report_path}", "--gap-analysis",
|
| 64 |
+
]
|
| 65 |
+
await send_reply_fn(
|
| 66 |
+
chat_id,
|
| 67 |
+
"π― <b>Benchmark Extended v5 mirato avviato</b>\n"
|
| 68 |
+
"<i>10 categorie piΓΉ deboli della baseline 39,1 Β· seed 1337 Β· task API moderna.</i>",
|
| 69 |
+
)
|
| 70 |
+
else:
|
| 71 |
+
report_path = _REPORT_V7
|
| 72 |
+
flags = ["--full", "--json", f"--output={report_path}", "--gap-analysis"]
|
| 73 |
+
await send_reply_fn(
|
| 74 |
+
chat_id,
|
| 75 |
+
"π <b>Benchmark Extended v5 avviato</b>\n"
|
| 76 |
+
"<i>20/20 categorie Β· seed 1337 Β· task API moderna Β· durata variabile fino a ~60 min.</i>",
|
| 77 |
+
)
|
| 78 |
env = {
|
| 79 |
**os.environ,
|
| 80 |
"INTERNAL_TOKEN": os.getenv("INTERNAL_TOKEN", ""),
|
|
|
|
| 115 |
await send_reply_fn(chat_id, f"π₯ <b>Errore critico benchmark:</b> <code>{exc}</code>")
|
| 116 |
return
|
| 117 |
|
| 118 |
+
report_exists = await asyncio.to_thread(os.path.exists, report_path)
|
| 119 |
if not report_exists:
|
| 120 |
await send_reply_fn(chat_id, "β οΈ <b>Benchmark Extended terminato ma report non trovato.</b>")
|
| 121 |
return
|
| 122 |
try:
|
| 123 |
+
report: dict[str, Any] = await asyncio.to_thread(_read_json, report_path)
|
| 124 |
except Exception as exc:
|
| 125 |
await send_reply_fn(chat_id, f"β οΈ <b>Report Extended non leggibile:</b> <code>{exc}</code>")
|
| 126 |
return
|
| 127 |
|
| 128 |
categories = {str(task.get("cat", "")) for task in report.get("tasks", []) if task.get("cat")}
|
| 129 |
+
expected_categories = len(_WEAK_CATEGORIES) if is_weak_run else 20
|
| 130 |
+
if len(categories) != expected_categories:
|
| 131 |
+
await send_reply_fn(chat_id, f"β οΈ <b>Run incompleta:</b> <code>{len(categories)}/{expected_categories}</code> categorie nel report."
|
| 132 |
" Nessun risultato incompleto viene presentato come benchmark completo.")
|
| 133 |
return
|
| 134 |
+
await send_reply_fn(chat_id, _format_v7_report(report, expected_categories=expected_categories, run_label="mirato Β· categorie deboli" if is_weak_run else None))
|
| 135 |
|
| 136 |
|
| 137 |
+
def _format_v7_report(report: dict[str, Any], *, expected_categories: int = 20, run_label: str | None = None) -> str:
|
| 138 |
"""Formatta il report v7 per Telegram HTML."""
|
| 139 |
s = report.get("summary", {})
|
| 140 |
ts = (report.get("timestamp") or "")[:16].replace("T", " ")
|
|
|
|
| 152 |
lines: list[str] = [
|
| 153 |
f"π <b>Benchmark {ver} completato!</b>\n\n"
|
| 154 |
f"π <b>Score agente:</b> <code>{avg}/100</code>\n"
|
| 155 |
+
f"π
<b>Run:</b> <code>{ts}</code>\n"
|
| 156 |
+
+ (f"π― <b>ModalitΓ :</b> <code>{run_label}</code>\n" if run_label else "") + "\n"
|
| 157 |
"π <b>Confronto vs riferimenti:</b>\n"
|
| 158 |
f" β’ Replit: <code>{repl}/100</code>\n"
|
| 159 |
f" β’ Cursor: <code>{curs}/100</code>\n"
|
|
|
|
| 181 |
scored = s.get("scoredTaskCount", sum(len(v) for v in by_cat.values()))
|
| 182 |
skipped = s.get("skippedTaskCount", max(0, attempted - scored))
|
| 183 |
lines.append(
|
| 184 |
+
f"π§ͺ <b>Copertura:</b> <code>{len(attempted_categories)}/{expected_categories} categorie tentate Β· "
|
| 185 |
f"{scored} valutabili Β· {skipped} non valutabili</code>\n"
|
| 186 |
)
|
| 187 |
if by_cat:
|
api/telegram_webhook.py
CHANGED
|
@@ -1623,8 +1623,9 @@ async def _cmd_bench(chat_id: int, mode: str = "default") -> None:
|
|
| 1623 |
"""Avvia il benchmark Extended su tutte le 20 categorie in background."""
|
| 1624 |
from .benchmark_handler import run_benchmark_task
|
| 1625 |
|
| 1626 |
-
|
| 1627 |
-
|
|
|
|
| 1628 |
task.add_done_callback(lambda completed: _logger.error(
|
| 1629 |
"[bench-extended] background task failed: %s", completed.exception()
|
| 1630 |
) if not completed.cancelled() and completed.exception() else None)
|
|
@@ -2058,7 +2059,7 @@ async def telegram_webhook(request: Request) -> dict:
|
|
| 2058 |
await _tg_reply(chat_id, "π§ Uso: <code>/ask <domanda></code>", keyboard=_MAIN_KB)
|
| 2059 |
elif cmd == "/bench":
|
| 2060 |
_mode = text[len(cmd):].strip() or "default"
|
| 2061 |
-
if _mode not in ("default","full","coding-only","noncode-only","agentic-only"):
|
| 2062 |
_mode = "default"
|
| 2063 |
_t=asyncio.create_task(_cmd_bench(chat_id, _mode)); _t.add_done_callback(_log_tg_exc)
|
| 2064 |
elif cmd == "/score":
|
|
|
|
| 1623 |
"""Avvia il benchmark Extended su tutte le 20 categorie in background."""
|
| 1624 |
from .benchmark_handler import run_benchmark_task
|
| 1625 |
|
| 1626 |
+
normalized_mode = "weak" if mode == "weak" else "full"
|
| 1627 |
+
_BENCH_CACHE[chat_id] = {"mode": "extended-weak" if normalized_mode == "weak" else "extended-20", "run_url": "", "started_at": time.time()}
|
| 1628 |
+
task = asyncio.create_task(run_benchmark_task(chat_id, _tg_reply, mode=normalized_mode))
|
| 1629 |
task.add_done_callback(lambda completed: _logger.error(
|
| 1630 |
"[bench-extended] background task failed: %s", completed.exception()
|
| 1631 |
) if not completed.cancelled() and completed.exception() else None)
|
|
|
|
| 2059 |
await _tg_reply(chat_id, "π§ Uso: <code>/ask <domanda></code>", keyboard=_MAIN_KB)
|
| 2060 |
elif cmd == "/bench":
|
| 2061 |
_mode = text[len(cmd):].strip() or "default"
|
| 2062 |
+
if _mode not in ("default","full","coding-only","noncode-only","agentic-only","weak"):
|
| 2063 |
_mode = "default"
|
| 2064 |
_t=asyncio.create_task(_cmd_bench(chat_id, _mode)); _t.add_done_callback(_log_tg_exc)
|
| 2065 |
elif cmd == "/score":
|
benchmark-extended.mjs
CHANGED
|
@@ -35,7 +35,7 @@
|
|
| 35 |
* Usage:
|
| 36 |
* node benchmark-extended.mjs [--seed N] [--compare N]
|
| 37 |
* [--coding-only] [--noncode-only] [--agentic-only]
|
| 38 |
-
* [--full] [--json] [--output=PATH] [--no-hf]
|
| 39 |
* [--gap-analysis] # stampa gap cards per i fail
|
| 40 |
* [--rotate] # seed casuale (default: 1337 canonico)
|
| 41 |
*
|
|
@@ -67,6 +67,10 @@ const F_GAP = _A.includes("--gap-analysis");
|
|
| 67 |
const _multi = _A.indexOf("--multi");
|
| 68 |
const MULTI = _multi !== -1 ? Math.max(2, Math.min(10, parseInt(_A[_multi+1])||3)) : 1;
|
| 69 |
const F_JUDGE = !_A.includes("--no-judge"); // semantic judge abilitato di default
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
// ββ Colors ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 72 |
const G="\x1b[32m",R="\x1b[31m",Y="\x1b[33m",B="\x1b[34m",
|
|
@@ -2001,8 +2005,15 @@ async function runOneSeed(seed,opts={}){
|
|
| 2001 |
ccTask,
|
| 2002 |
];
|
| 2003 |
|
|
|
|
| 2004 |
let selected;
|
| 2005 |
-
if(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2006 |
selected=rng.shuffle(codingTasks).slice(0,7);
|
| 2007 |
} else if(F_NONCODE){
|
| 2008 |
selected=[rsTask,daTask,twTask,ryTask,sqlTask,cwTask,mmluTask];
|
|
|
|
| 35 |
* Usage:
|
| 36 |
* node benchmark-extended.mjs [--seed N] [--compare N]
|
| 37 |
* [--coding-only] [--noncode-only] [--agentic-only]
|
| 38 |
+
* [--full] [--categories=a,b] [--json] [--output=PATH] [--no-hf]
|
| 39 |
* [--gap-analysis] # stampa gap cards per i fail
|
| 40 |
* [--rotate] # seed casuale (default: 1337 canonico)
|
| 41 |
*
|
|
|
|
| 67 |
const _multi = _A.indexOf("--multi");
|
| 68 |
const MULTI = _multi !== -1 ? Math.max(2, Math.min(10, parseInt(_A[_multi+1])||3)) : 1;
|
| 69 |
const F_JUDGE = !_A.includes("--no-judge"); // semantic judge abilitato di default
|
| 70 |
+
const _categoriesArg = _A.find(a=>a.startsWith("--categories="));
|
| 71 |
+
const TARGET_CATEGORIES = _categoriesArg
|
| 72 |
+
? new Set(_categoriesArg.slice("--categories=".length).split(",").map(c=>c.trim()).filter(Boolean))
|
| 73 |
+
: null;
|
| 74 |
|
| 75 |
// ββ Colors ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 76 |
const G="\x1b[32m",R="\x1b[31m",Y="\x1b[33m",B="\x1b[34m",
|
|
|
|
| 2005 |
ccTask,
|
| 2006 |
];
|
| 2007 |
|
| 2008 |
+
const allTasks = [...codingTasks,...[rsTask,daTask,twTask,ryTask,sqlTask,cwTask,mmluTask],...[orTask,mcTask,rcTask,rbTask,avTask]];
|
| 2009 |
let selected;
|
| 2010 |
+
if (TARGET_CATEGORIES) {
|
| 2011 |
+
const known = new Set(allTasks.map(task => task.category));
|
| 2012 |
+
const unknown = [...TARGET_CATEGORIES].filter(category => !known.has(category));
|
| 2013 |
+
if (unknown.length) throw new Error(`Categorie benchmark non valide: ${unknown.join(", ")}`);
|
| 2014 |
+
selected = allTasks.filter(task => TARGET_CATEGORIES.has(task.category));
|
| 2015 |
+
if (!selected.length) throw new Error("Nessuna categoria benchmark selezionata");
|
| 2016 |
+
} else if(F_CODING){
|
| 2017 |
selected=rng.shuffle(codingTasks).slice(0,7);
|
| 2018 |
} else if(F_NONCODE){
|
| 2019 |
selected=[rsTask,daTask,twTask,ryTask,sqlTask,cwTask,mmluTask];
|
tests/test_telegram_extended_benchmark.py
CHANGED
|
@@ -50,5 +50,37 @@ class ExtendedBenchmarkHandlerTests(unittest.IsolatedAsyncioTestCase):
|
|
| 50 |
self.assertIn("20/20 categorie", final_text)
|
| 51 |
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
if __name__ == "__main__":
|
| 54 |
unittest.main()
|
|
|
|
| 50 |
self.assertIn("20/20 categorie", final_text)
|
| 51 |
|
| 52 |
|
| 53 |
+
async def test_targeted_weak_runner_uses_only_low_score_categories_and_separate_report(self):
|
| 54 |
+
categories = list(handler._WEAK_CATEGORIES)
|
| 55 |
+
report = {
|
| 56 |
+
"timestamp": "2026-08-15T12:00:00Z",
|
| 57 |
+
"version": "extended-v5",
|
| 58 |
+
"summary": {"avgScore": 31, "avgReplit": 56, "avgCursor": 62, "avgDevin": 68, "avgManus": 71, "gapCount": 8, "verdict": "SOTTO_REPLIT"},
|
| 59 |
+
"tasks": [{"cat": category, "score": 31} for category in categories],
|
| 60 |
+
"gapCards": [],
|
| 61 |
+
}
|
| 62 |
+
replies = AsyncMock()
|
| 63 |
+
with tempfile.TemporaryDirectory() as directory:
|
| 64 |
+
script = Path(directory) / "benchmark-extended.mjs"
|
| 65 |
+
full_output = Path(directory) / "benchmark-v5-latest.json"
|
| 66 |
+
weak_output = Path(directory) / "benchmark-v5-weak-latest.json"
|
| 67 |
+
script.write_text("// runner")
|
| 68 |
+
full_output.write_text(json.dumps({"tasks": []}))
|
| 69 |
+
weak_output.write_text(json.dumps(report))
|
| 70 |
+
with patch.object(handler, "_BENCH_SCRIPT", str(script)), \
|
| 71 |
+
patch.object(handler, "_REPORT_V7", str(full_output)), \
|
| 72 |
+
patch.object(handler, "_REPORT_V7_WEAK", str(weak_output)), \
|
| 73 |
+
patch("asyncio.create_subprocess_exec", AsyncMock(return_value=_Process())) as create_process:
|
| 74 |
+
await handler.run_benchmark_task(123, replies, mode="weak")
|
| 75 |
+
|
| 76 |
+
command = create_process.await_args.args
|
| 77 |
+
self.assertNotIn("--full", command)
|
| 78 |
+
self.assertIn("--categories=" + ",".join(handler._WEAK_CATEGORIES), command)
|
| 79 |
+
self.assertIn("--output=" + str(weak_output), command)
|
| 80 |
+
final_text = replies.await_args_list[-1].args[1]
|
| 81 |
+
self.assertIn("10/10 categorie", final_text)
|
| 82 |
+
self.assertIn("categorie deboli", final_text)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
if __name__ == "__main__":
|
| 86 |
unittest.main()
|