import argparse import json from pathlib import Path def main(): parser = argparse.ArgumentParser(description="Evaluate saved AI outputs offline") parser.add_argument("cases") parser.add_argument("answers") args = parser.parse_args() try: cases = json.loads(Path(args.cases).read_text(encoding="utf-8-sig")) answers = json.loads(Path(args.answers).read_text(encoding="utf-8-sig")) if not isinstance(cases, list) or not cases or not isinstance(answers, dict): raise ValueError("Use a non-empty case list and an answer object.") seen = set() results = [] for case in cases: if not isinstance(case, dict): raise ValueError("Each case must be an object.") key = case.get("id") expected = case.get("expected") forbidden = case.get("forbidden", []) if not isinstance(key, str) or not key.strip() or key in seen: raise ValueError("Case IDs must be unique non-empty strings.") if not isinstance(expected, list) or not expected: raise ValueError("Each case needs expected phrases.") if not isinstance(forbidden, list): raise ValueError("Forbidden phrases must be a list.") if any(not isinstance(s, str) or not s.strip() for s in expected + forbidden): raise ValueError("Phrases must be non-empty strings.") seen.add(key) answer = answers.get(key) text = answer.casefold() if isinstance(answer, str) else "" missing = [s for s in expected if s.casefold() not in text] violations = [s for s in forbidden if s.casefold() in text] passed = isinstance(answer, str) and not missing and not violations results.append({"id": key, "passed": passed, "missing": missing, "forbiddenFound": violations}) except (OSError, ValueError, TypeError, UnicodeError) as error: parser.exit(2, f"Invalid evaluation input: {error}\n") passed = sum(row["passed"] for row in results) print(json.dumps({"total": len(results), "passed": passed, "passRate": passed / len(results), "results": results}, indent=2)) return 0 if passed == len(results) else 1 if __name__ == "__main__": raise SystemExit(main())