File size: 2,408 Bytes
23ae461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
"""Metrique DETERMINISTE de qualite du code genere — sans LLM, donc STABLE d'un run
a l'autre (contrairement au jugement LLM, bruite a n=29).

Pour chaque item generate/fix d'un fichier de reponses, extrait le code, passe le
bug-finder, et compte : erreurs lexicales, commandes inventees (hors catalogue),
non-ASCII, lignes > 80, affectations nues. Agrege en taux de code PROPRE.

Usage : python -m eval.code_quality eval/runs/answers_xxx.json [autre.json ...]
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

from pcp import syntax, bugfinder


def analyze_file(path: Path) -> dict:
    data = json.loads(path.read_text(encoding="utf-8"))
    items = [it for it in data["items"] if it.get("category") in ("generate", "fix")]
    rows = []
    for it in items:
        code = "\n".join(syntax.extract_code_blocks(it.get("answer", "")))
        f = bugfinder.analyze(code) if code.strip() else []
        errs = sum(1 for x in f if x.severity == "error")
        unk = sum(1 for x in f if "non trouve dans le catalogue" in x.message)
        non_ascii = sum(1 for ln in code.splitlines() if any(ord(c) > 127 for c in ln))
        rows.append({"id": it["id"], "has_code": bool(code.strip()),
                     "errors": errs, "unknown": unk, "non_ascii": non_ascii})
    n = len(rows)
    with_code = sum(1 for r in rows if r["has_code"])
    clean = sum(1 for r in rows if r["has_code"] and r["errors"] == 0 and r["unknown"] == 0)
    return {
        "file": path.name,
        "provider": data.get("provider"), "model": data.get("model"),
        "n_code_items": n, "with_code": with_code,
        "clean_code_pct": round(100 * clean / n, 1) if n else 0.0,
        "total_errors": sum(r["errors"] for r in rows),
        "total_unknown_cmds": sum(r["unknown"] for r in rows),
        "total_non_ascii": sum(r["non_ascii"] for r in rows),
        "rows": rows,
    }


def main():
    paths = [Path(p) for p in sys.argv[1:]] or sorted(Path("eval/runs").glob("answers_*.json"))
    print(f"{'file':32} {'model':28} clean%  err  unk  n-ascii")
    for p in paths:
        if not p.exists():
            continue
        r = analyze_file(p)
        print(f"{r['file']:32} {str(r['model'])[:28]:28} {r['clean_code_pct']:5.1f}  "
              f"{r['total_errors']:3d}  {r['total_unknown_cmds']:3d}  {r['total_non_ascii']:5d}")


if __name__ == "__main__":
    main()