ankit-pn's picture
Upload validate.py with huggingface_hub
45d142b verified
Raw History Blame Contribute Delete
3.5 kB
"""Validate dataset JSONL files against tools.json and SPEC.md conventions.
Usage: python3 validate.py file1.jsonl [file2.jsonl ...]
"""
import json
import re
import sys
from pathlib import Path
TOOLS = {t["function"]["name"]: t["function"] for t in json.loads((Path(__file__).parent / "tools.json").read_text())}
TIME_RE = re.compile(r"^([01]\d|2[0-3]):[0-5]\d$")
PY_TYPES = {"string": str, "integer": int, "boolean": bool}
def check_call(call):
errs = []
name = call.get("name")
if name not in TOOLS:
return [f"unknown function {name!r}"]
args = call.get("arguments")
if not isinstance(args, dict):
return ["arguments must be an object"]
params = TOOLS[name]["parameters"]
props = params["properties"]
for req in params["required"]:
if req not in args:
errs.append(f"{name}: missing required arg {req!r}")
for k, v in args.items():
if k not in props:
errs.append(f"{name}: unknown arg {k!r}")
continue
spec = props[k]
typ = PY_TYPES[spec["type"]]
if not isinstance(v, typ) or (typ is int and isinstance(v, bool)):
errs.append(f"{name}.{k}: expected {spec['type']}, got {v!r}")
continue
if "enum" in spec and v not in spec["enum"]:
errs.append(f"{name}.{k}: {v!r} not in {spec['enum']}")
if k == "time" and not TIME_RE.match(v):
errs.append(f"{name}.time: {v!r} is not HH:MM 24h")
if k == "level" and not 0 <= v <= 100:
errs.append(f"{name}.level out of range: {v}")
if k in ("duration_seconds", "duration_minutes", "delay_minutes") and v <= 0:
errs.append(f"{name}.{k} must be positive: {v}")
if typ is str and not v.strip():
errs.append(f"{name}.{k}: empty string")
if k == "date" and v != v.lower():
errs.append(f"{name}.date must be lowercase: {v!r}")
if name in ("set_volume", "set_brightness") and len(args) != 1:
errs.append(f"{name}: exactly one of level/action required")
if name == "make_call" and args.get("video") is False:
errs.append("make_call: omit video instead of false")
return errs
def validate(path):
errors, n = [], 0
for i, line in enumerate(Path(path).read_text().splitlines(), 1):
if not line.strip():
continue
n += 1
try:
rec = json.loads(line)
except json.JSONDecodeError as e:
errors.append(f"{path}:{i}: bad JSON: {e}")
continue
errs = []
if not isinstance(rec.get("text"), str) or not rec["text"].strip():
errs.append("missing text")
if rec.get("lang") not in ("en", "hi-en"):
errs.append(f"bad lang {rec.get('lang')!r}")
calls = rec.get("calls")
if not isinstance(calls, list):
errs.append("calls must be a list")
calls = []
if not calls and not (isinstance(rec.get("reply"), str) and rec["reply"].strip()):
errs.append("no-call record needs a reply")
for c in calls:
errs += check_call(c)
errors += [f"{path}:{i}: {e}" for e in errs]
return n, errors
if __name__ == "__main__":
total_err = 0
for p in sys.argv[1:]:
n, errs = validate(p)
total_err += len(errs)
print(f"{p}: {n} records, {len(errs)} errors")
for e in errs[:50]:
print(" " + e)
sys.exit(1 if total_err else 0)