Spaces:
Running
Running
File size: 2,704 Bytes
380a7fc | 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 60 61 62 63 64 65 66 67 68 | """Ancrage pour du code : les modules et les noms importés existent-ils vraiment dans l'environnement ?
L'hallucination typique d'un petit modèle de code n'est pas une fausse citation mais une API inventée
(`from math import sqrtt`). La vérification se fait dans le bac à sable, jamais dans le processus du Space.
"""
from __future__ import annotations
import ast
import json
from mouh.skills.sandbox import SandboxLimits, run_in_sandbox
_PROBE = (
"import importlib, json, sys\n"
"bad = []\n"
"for mod, names in json.loads(sys.argv[1]):\n"
" try:\n"
" m = importlib.import_module(mod)\n"
" except Exception:\n"
" bad.append(mod)\n"
" continue\n"
" for n in names:\n"
" if not hasattr(m, n):\n"
" try:\n"
" importlib.import_module(mod + '.' + n)\n"
" except Exception:\n"
" bad.append(mod + '.' + n)\n"
"print(json.dumps(bad))\n"
)
def imported_names(files: dict[str, str]) -> list[tuple[str, list[str]]]:
"""[(module, [noms])] des imports absolus, hors modules fournis par la proposition elle-même."""
local = {name[:-3].replace("/", ".") for name in files if name.endswith(".py")}
wanted: list[tuple[str, list[str]]] = []
for code in files.values():
try:
tree = ast.parse(code)
except SyntaxError:
return [] # la porte de compilation et pytest le signalent mieux
for node in ast.walk(tree):
if isinstance(node, ast.Import):
wanted += [(a.name, []) for a in node.names]
elif isinstance(node, ast.ImportFrom) and node.module and node.level == 0:
wanted.append((node.module, [a.name for a in node.names if a.name != "*"]))
return [(m, n) for m, n in wanted if m.split(".")[0] not in local]
class ApiCheck:
def __init__(self, timeout_s: float = 10.0) -> None:
self.timeout_s = timeout_s
self._memo: dict[str, list[str]] = {}
def check(self, files, notes=None) -> list[str]:
wanted = imported_names(files)
if not wanted:
return []
key = json.dumps(wanted, sort_keys=True)
if key not in self._memo:
run = run_in_sandbox(["probe.py", key], {"probe.py": _PROBE}, timeout_s=self.timeout_s,
limits=SandboxLimits(cpu_seconds=int(self.timeout_s)))
try:
bad = json.loads(run.stdout.strip().splitlines()[-1])
except (ValueError, IndexError):
bad = []
self._memo[key] = [f"API introuvable : {b}" for b in bad]
return list(self._memo[key])
|