SPB Git

spb/trouve-ka Public

Trouve-KA — moteur de recherche web indépendant, Québec-first. Crawler distribué, index OpenSearch, ranking bilingue, galerie d'images. En prod : www.trouve-ka.com

Python 76.8% TypeScript 15.7% SQL 3.9% Shell 1.4% CSS 1.3% Dockerfile 0.7%
2.3 KB · 66 lines python
Raw Blame History
1#!/usr/bin/env python32# Trouve-KA — évaluation du ranking contre le dataset (§16)3# Author: Simon-Pierre Boucher4# Contact: contact@spboucher.ai56"""Exécute le dataset d'évaluation contre l'API de recherche.78Usage : python3 scripts/eval/run-eval.py [--api http://localhost:8080]9Un cas passe si au moins un domaine attendu apparaît dans le top-k.10L'index grandit en continu : les scores s'améliorent avec la couverture.11"""1213import argparse14import json15import pathlib16import re17import urllib.parse18import urllib.request1920HERE = pathlib.Path(__file__).parent212223def load_cases() -> list[dict]:24    """Parse minimaliste du YAML du dataset (pas de dépendance PyYAML)."""25    cases, current = [], None26    for line in (HERE / "ranking-eval.yaml").read_text(encoding="utf-8").splitlines():27        if m := re.match(r'\s*- query: "(.*)"', line):28            current = {"query": m.group(1)}29            cases.append(current)30        elif m := re.match(r"\s*expect_any_of: \[(.*)\]", line):31            current["expect_any_of"] = [d.strip() for d in m.group(1).split(",")]32        elif m := re.match(r"\s*top_k: (\d+)", line):33            current["top_k"] = int(m.group(1))34    return cases353637def main() -> int:38    ap = argparse.ArgumentParser()39    ap.add_argument("--api", default="http://localhost:8080")40    args = ap.parse_args()4142    passed = 043    cases = load_cases()44    for case in cases:45        q = urllib.parse.quote(case["query"])46        url = f"{args.api}/api/search?q={q}&limit={case['top_k']}"47        try:48            with urllib.request.urlopen(url, timeout=10) as resp:49                data = json.load(resp)50        except Exception as exc:51            print(f"✗ {case['query']!r} — API injoignable : {exc}")52            continue53        domains = [r["domain"] for r in data["results"]]54        hit = next((d for d in domains if any(d == e or d.endswith("." + e) for e in case["expect_any_of"])), None)55        if hit:56            passed += 157            print(f"✓ {case['query']!r}{hit} (rang {domains.index(hit) + 1}, {data['took_ms']} ms)")58        else:59            print(f"✗ {case['query']!r} → top-{case['top_k']}: {domains[:5]}")60    print(f"\n{passed}/{len(cases)} cas réussis")61    return 0 if passed == len(cases) else 1626364if __name__ == "__main__":65    raise SystemExit(main())66