#!/usr/bin/env python3 # Trouve-KA — évaluation du ranking contre le dataset (§16) # Author: Simon-Pierre Boucher # Contact: contact@spboucher.ai """Exécute le dataset d'évaluation contre l'API de recherche. Usage : python3 scripts/eval/run-eval.py [--api http://localhost:8080] Un cas passe si au moins un domaine attendu apparaît dans le top-k. L'index grandit en continu : les scores s'améliorent avec la couverture. """ import argparse import json import pathlib import re import urllib.parse import urllib.request HERE = pathlib.Path(__file__).parent def load_cases() -> list[dict]: """Parse minimaliste du YAML du dataset (pas de dépendance PyYAML).""" cases, current = [], None for line in (HERE / "ranking-eval.yaml").read_text(encoding="utf-8").splitlines(): if m := re.match(r'\s*- query: "(.*)"', line): current = {"query": m.group(1)} cases.append(current) elif m := re.match(r"\s*expect_any_of: \[(.*)\]", line): current["expect_any_of"] = [d.strip() for d in m.group(1).split(",")] elif m := re.match(r"\s*top_k: (\d+)", line): current["top_k"] = int(m.group(1)) return cases def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--api", default="http://localhost:8080") args = ap.parse_args() passed = 0 cases = load_cases() for case in cases: q = urllib.parse.quote(case["query"]) url = f"{args.api}/api/search?q={q}&limit={case['top_k']}" try: with urllib.request.urlopen(url, timeout=10) as resp: data = json.load(resp) except Exception as exc: print(f"✗ {case['query']!r} — API injoignable : {exc}") continue domains = [r["domain"] for r in data["results"]] hit = next((d for d in domains if any(d == e or d.endswith("." + e) for e in case["expect_any_of"])), None) if hit: passed += 1 print(f"✓ {case['query']!r} → {hit} (rang {domains.index(hit) + 1}, {data['took_ms']} ms)") else: print(f"✗ {case['query']!r} → top-{case['top_k']}: {domains[:5]}") print(f"\n{passed}/{len(cases)} cas réussis") return 0 if passed == len(cases) else 1 if __name__ == "__main__": raise SystemExit(main())