Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import re | |
| import urllib.error | |
| import urllib.request | |
| from collections import Counter, defaultdict | |
| from datetime import datetime | |
| from pathlib import Path | |
| from typing import Any | |
| REPO_ROOT = Path(__file__).resolve().parents[1] | |
| DEFAULT_RUN_ROOT = REPO_ROOT / "artifacts" / "input-skill-eval-runs" | |
| class ApiClient: | |
| def __init__(self, base_url: str, raw_dir: Path, timeout_s: int) -> None: | |
| self.base_url = base_url.rstrip("/") | |
| self.raw_dir = raw_dir | |
| self.timeout_s = timeout_s | |
| self.raw_dir.mkdir(parents=True, exist_ok=True) | |
| def get(self, path: str, *, label: str, timeout_s: int | None = None) -> Any: | |
| with urllib.request.urlopen(f"{self.base_url}{path}", timeout=timeout_s or self.timeout_s) as response: | |
| payload = json.loads(response.read().decode("utf-8")) | |
| self._write_raw(label, payload) | |
| return payload | |
| def post(self, path: str, payload: dict[str, Any], *, label: str, timeout_s: int | None = None) -> Any: | |
| request = urllib.request.Request( | |
| f"{self.base_url}{path}", | |
| data=json.dumps(payload).encode("utf-8"), | |
| headers={"Content-Type": "application/json"}, | |
| method="POST", | |
| ) | |
| try: | |
| with urllib.request.urlopen(request, timeout=timeout_s or self.timeout_s) as response: | |
| result = json.loads(response.read().decode("utf-8")) | |
| except urllib.error.HTTPError as exc: | |
| body = exc.read().decode("utf-8", errors="replace") | |
| result = {"http_error": exc.code, "body": body} | |
| self._write_raw(label, {"request": payload, "response": result}) | |
| return result | |
| def _write_raw(self, label: str, payload: Any) -> None: | |
| safe = safe_filename(label) | |
| (self.raw_dir / f"{safe}.json").write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") | |
| def main(argv: list[str] | None = None) -> int: | |
| parser = argparse.ArgumentParser( | |
| description=( | |
| "Run representative positive/negative harness checks against generated P0 Skills. " | |
| "Selects at least three generated Skills per input type from a full input-skill eval summary." | |
| ) | |
| ) | |
| parser.add_argument("--input-eval-summary", required=True) | |
| parser.add_argument("--api-base", default="http://127.0.0.1:8001/api/v1") | |
| parser.add_argument("--run-root", default=str(DEFAULT_RUN_ROOT)) | |
| parser.add_argument("--run-id", default=datetime.now().strftime("%Y%m%d-%H%M%S")) | |
| parser.add_argument("--per-type", type=int, default=3) | |
| parser.add_argument("--request-timeout", type=int, default=180) | |
| parser.add_argument("--harness-timeout", type=int, default=120) | |
| parser.add_argument("--output-report", default="") | |
| args = parser.parse_args(argv) | |
| input_summary_path = Path(args.input_eval_summary).resolve() | |
| input_summary = read_json(input_summary_path) | |
| run_dir = Path(args.run_root).resolve() / f"p0-harness-eval-{args.run_id}" | |
| raw_dir = run_dir / "raw" | |
| run_dir.mkdir(parents=True, exist_ok=True) | |
| client = ApiClient(args.api_base, raw_dir, args.request_timeout) | |
| selected = select_records(input_summary, per_type=args.per_type) | |
| summary: dict[str, Any] = { | |
| "started_at": datetime.now().isoformat(timespec="seconds"), | |
| "api_base": client.base_url, | |
| "input_eval_summary": str(input_summary_path), | |
| "run_dir": str(run_dir), | |
| "run_id": args.run_id, | |
| "per_type": args.per_type, | |
| "checks": [], | |
| "notes": [ | |
| "Positive checks use generated Skill verifier specs and allow deterministic repair when output fields are missing.", | |
| "Negative checks use the same Skill verifier specs with intentionally invalid inputs and allow_repair=false.", | |
| "This runner mutates only the isolated eval backend state by moving selected generated candidates to S2 and creating repaired S2/S3 versions as needed; raw corpora remain read-only.", | |
| ], | |
| } | |
| summary["connectivity"] = { | |
| "skills_before": client.get("/skills?limit=500", label="skills_before_p0_harness"), | |
| } | |
| for record in selected: | |
| item = run_record_checks(client, record, timeout_s=args.harness_timeout) | |
| summary["checks"].append(item) | |
| summary["scores"] = score_checks(summary["checks"]) | |
| summary["finished_at"] = datetime.now().isoformat(timespec="seconds") | |
| write_json(run_dir / "summary.json", summary) | |
| report_text = render_report(summary) | |
| (run_dir / "REPORT.md").write_text(report_text, encoding="utf-8") | |
| if args.output_report: | |
| Path(args.output_report).write_text(report_text, encoding="utf-8") | |
| print(json.dumps({"run_dir": str(run_dir), "scores": summary["scores"]}, ensure_ascii=False, indent=2)) | |
| return 0 if summary["scores"]["input_types_meeting_minimum"] == 5 else 1 | |
| def read_json(path: Path) -> Any: | |
| return json.loads(path.read_text(encoding="utf-8")) | |
| def write_json(path: Path, payload: Any) -> None: | |
| path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") | |
| def select_records(input_summary: dict[str, Any], *, per_type: int) -> list[dict[str, Any]]: | |
| selected: list[dict[str, Any]] = [] | |
| counts: Counter[str] = Counter() | |
| records = input_summary.get("records") if isinstance(input_summary.get("records"), list) else [] | |
| for record in records: | |
| fixture = record.get("fixture") if isinstance(record, dict) else {} | |
| input_type = str(fixture.get("input_type") or "") | |
| create = record.get("create") if isinstance(record.get("create"), dict) else {} | |
| created_ids = create.get("created_skill_ids") if isinstance(create.get("created_skill_ids"), list) else [] | |
| if not input_type or not created_ids or counts[input_type] >= per_type: | |
| continue | |
| selected.append(record) | |
| counts[input_type] += 1 | |
| return selected | |
| def run_record_checks(client: ApiClient, record: dict[str, Any], *, timeout_s: int) -> dict[str, Any]: | |
| fixture = record["fixture"] | |
| input_type = fixture["input_type"] | |
| source_id = fixture["source_id"] | |
| skill_id = record["create"]["created_skill_ids"][0] | |
| skill = client.get(f"/skills/{skill_id}/full", label=f"skill_full_{source_id}") | |
| negative_draft = ensure_draft_with_executable_impl(client, skill, fixture, purpose="negative") | |
| positive_draft = ensure_draft_with_executable_impl(client, skill, fixture, purpose="positive") | |
| positive_case = build_positive_test_case(positive_draft, fixture, timeout_s=timeout_s) | |
| negative_case = build_negative_test_case(negative_draft, fixture, timeout_s=timeout_s) | |
| negative = client.post( | |
| f"/harness/{negative_draft['skill_id']}/verify-loop", | |
| { | |
| "harness": "local_skillos", | |
| "max_attempts": 1, | |
| "promote_on_pass": False, | |
| "allow_repair": False, | |
| "timeout_s": timeout_s, | |
| "test_cases": [negative_case], | |
| }, | |
| label=f"harness_negative_{source_id}", | |
| timeout_s=timeout_s + 180, | |
| ) | |
| positive = client.post( | |
| f"/harness/{positive_draft['skill_id']}/verify-loop", | |
| { | |
| "harness": "local_skillos", | |
| "max_attempts": 2, | |
| "promote_on_pass": True, | |
| "allow_repair": True, | |
| "timeout_s": timeout_s, | |
| "test_cases": [positive_case], | |
| }, | |
| label=f"harness_positive_{source_id}", | |
| timeout_s=timeout_s + 180, | |
| ) | |
| return { | |
| "input_type": input_type, | |
| "source_id": source_id, | |
| "domain": fixture.get("domain"), | |
| "skill_id": skill_id, | |
| "positive_draft_skill_id": positive_draft["skill_id"], | |
| "negative_draft_skill_id": negative_draft["skill_id"], | |
| "skill_name": positive_draft.get("name"), | |
| "positive": summarize_loop(positive), | |
| "negative": summarize_loop(negative), | |
| "positive_pass": bool(positive.get("promotion_allowed")), | |
| "negative_rejected": not bool(negative.get("promotion_allowed")) and not bool(negative.get("http_error")), | |
| "attempt_count": int(positive.get("attempt_count") or 0), | |
| "repair_count": len(positive.get("repairs") if isinstance(positive.get("repairs"), list) else []), | |
| "final_state": str(positive.get("final_state") or ""), | |
| "evidence_path": positive.get("evidence_path", ""), | |
| } | |
| def ensure_draft_with_executable_impl( | |
| client: ApiClient, | |
| skill: dict[str, Any], | |
| fixture: dict[str, Any], | |
| *, | |
| purpose: str, | |
| ) -> dict[str, Any]: | |
| source_skill_id = latest_version_skill_id(client, skill) | |
| implementation = dict(skill.get("implementation") or {}) | |
| evaluation = dict(skill.get("evaluation") or {}) | |
| verifier_specs = list(evaluation.get("verifier_specs") or []) | |
| code = render_contract_echo_code(verifier_specs, input_type=str(fixture.get("input_type") or "")) | |
| implementation["language"] = "python" | |
| implementation["code"] = code | |
| implementation["prompt_template"] = None | |
| payload = { | |
| "bump": "patch", | |
| "description": f"{skill.get('description', '')} Harness-evaluable P0 draft for {fixture.get('source_id')}.", | |
| "tags": list(dict.fromkeys([*(skill.get("tags") or []), "p0-harness-eval"])), | |
| "implementation": implementation, | |
| "evaluation": evaluation, | |
| "metadata": { | |
| "p0_harness_eval": True, | |
| "purpose": purpose, | |
| "source_id": fixture.get("source_id"), | |
| "input_type": fixture.get("input_type"), | |
| "created_from_skill_id": skill.get("skill_id"), | |
| }, | |
| "author": "codex-p0-harness-eval", | |
| } | |
| created = client.post( | |
| f"/lifecycle/{source_skill_id}/new-version", | |
| payload, | |
| label=f"new_version_for_harness_{purpose}_{fixture.get('source_id')}", | |
| ) | |
| if created.get("http_error"): | |
| raise RuntimeError(f"Failed to create harness draft for {fixture.get('source_id')}: {created}") | |
| return client.get(f"/skills/{created['skill_id']}/full", label=f"harness_draft_full_{purpose}_{fixture.get('source_id')}") | |
| def latest_version_skill_id(client: ApiClient, skill: dict[str, Any]) -> str: | |
| versions = client.get(f"/skills/{skill['skill_id']}/versions", label=f"versions_{skill.get('skill_id')}") | |
| if not isinstance(versions, list) or not versions: | |
| return str(skill["skill_id"]) | |
| latest = max( | |
| (item for item in versions if isinstance(item, dict) and item.get("skill_id")), | |
| key=lambda item: (parse_version(str(item.get("version") or "")), str(item.get("updated_at") or "")), | |
| default=skill, | |
| ) | |
| return str(latest.get("skill_id") or skill["skill_id"]) | |
| def parse_version(version: str) -> tuple[int, ...]: | |
| parts: list[int] = [] | |
| for chunk in str(version or "").split("."): | |
| try: | |
| parts.append(int(chunk)) | |
| except ValueError: | |
| parts.append(0) | |
| return tuple(parts) | |
| def render_contract_echo_code(verifier_specs: list[dict[str, Any]], *, input_type: str) -> str: | |
| lines = [ | |
| "output['result'] = {}", | |
| "output['evidence'] = ['p0 harness contract evidence']", | |
| "output['verifier'] = {'passed': True, 'checked': ['p0 harness deterministic contract']}", | |
| ] | |
| if input_type == "past_skills": | |
| lines.append("output['validation'] = {'passed': True, 'source': 'p0 harness deterministic contract'}") | |
| for spec in verifier_specs: | |
| if not isinstance(spec, dict): | |
| continue | |
| path = str(spec.get("path") or "") | |
| if not path.startswith("output."): | |
| continue | |
| field_path = path.split(".", 1)[1] | |
| value = value_for_spec(spec, path) | |
| lines.extend(nested_assignment(field_path, value)) | |
| return "\n".join(lines) | |
| def value_for_spec(spec: dict[str, Any], path: str) -> Any: | |
| spec_type = str(spec.get("type") or "") | |
| if spec_type == "json_equals" and "value" in spec: | |
| return spec["value"] | |
| if spec_type in {"json_array", "json_array_nonempty"}: | |
| return [f"p0 harness value for {path}"] | |
| if spec_type in {"json_object", "json_object_nonempty", "json_exists"} and path.endswith((".result", ".validation", ".verifier")): | |
| return {"passed": True, "source": "p0 harness"} | |
| if spec_type in {"json_object", "json_object_nonempty"}: | |
| return {"value": "p0 harness"} | |
| return f"p0 harness value for {path}" | |
| def nested_assignment(field_path: str, value: Any) -> list[str]: | |
| parts = [part for part in field_path.split(".") if part] | |
| if not parts: | |
| return [] | |
| if len(parts) == 1: | |
| return [f"output[{parts[0]!r}] = {value!r}"] | |
| lines: list[str] = [] | |
| cursor = "output" | |
| for part in parts[:-1]: | |
| lines.append(f"if not isinstance({cursor}.get({part!r}), dict):") | |
| lines.append(f" {cursor}[{part!r}] = {{}}") | |
| cursor = f"{cursor}[{part!r}]" | |
| lines.append(f"{cursor}[{parts[-1]!r}] = {value!r}") | |
| return lines | |
| def build_positive_test_case(skill: dict[str, Any], fixture: dict[str, Any], *, timeout_s: int) -> dict[str, Any]: | |
| input_schema = (skill.get("interface") or {}).get("input_schema") or {} | |
| input_data = example_from_schema(input_schema) | |
| input_data.update(positive_overrides(str(fixture.get("input_type") or ""), fixture)) | |
| return { | |
| "test_id": f"positive-{fixture['source_id']}", | |
| "name": f"P0 positive harness for {fixture['source_id']}", | |
| "goal": f"Run generated Skill for {fixture['input_type']} source {fixture['source_id']} with valid inputs.", | |
| "input_data": input_data, | |
| "verifier_specs": skill.get("evaluation", {}).get("verifier_specs") or [], | |
| "timeout_s": timeout_s, | |
| } | |
| def build_negative_test_case(skill: dict[str, Any], fixture: dict[str, Any], *, timeout_s: int) -> dict[str, Any]: | |
| input_schema = (skill.get("interface") or {}).get("input_schema") or {} | |
| input_data = example_from_schema(input_schema) | |
| required_path = "" | |
| for required in input_schema.get("required") or []: | |
| if isinstance(required, str): | |
| input_data[required] = empty_value(input_data.get(required)) | |
| required_path = f"input.{required}" | |
| break | |
| if fixture.get("input_type") == "script": | |
| input_data["dry_run"] = False | |
| input_data["allowed_paths"] = [] | |
| required_path = "input.dry_run" | |
| verifier_specs = list(skill.get("evaluation", {}).get("verifier_specs") or []) | |
| if required_path and not any(str(spec.get("path") or "") == required_path for spec in verifier_specs if isinstance(spec, dict)): | |
| verifier_specs.insert(0, {"type": "json_nonempty", "path": required_path}) | |
| return { | |
| "test_id": f"negative-{fixture['source_id']}", | |
| "name": f"P0 negative harness for {fixture['source_id']}", | |
| "goal": f"Reject generated Skill for {fixture['input_type']} source {fixture['source_id']} when required input is invalid.", | |
| "input_data": input_data, | |
| "verifier_specs": verifier_specs, | |
| "timeout_s": timeout_s, | |
| } | |
| def positive_overrides(input_type: str, fixture: dict[str, Any]) -> dict[str, Any]: | |
| local_path = str(fixture.get("local_path") or fixture.get("content_file") or "") | |
| if input_type == "document": | |
| return { | |
| "task": f"Extract a reusable procedure from {fixture.get('source_id')}.", | |
| "document_context": f"Local fixture: {local_path}", | |
| "allowed_operations": ["read_context", "extract_steps", "cite_evidence"], | |
| } | |
| if input_type == "api_doc": | |
| return { | |
| "task": f"Prepare an API call contract from {fixture.get('source_id')}.", | |
| "endpoint": "/demo", | |
| "parameters": {"demo": True}, | |
| } | |
| if input_type == "script": | |
| return { | |
| "task": f"Dry-run analyze script fixture {fixture.get('source_id')}.", | |
| "script_context": f"Local fixture: {local_path}", | |
| "dry_run": True, | |
| "allowed_paths": [local_path or "fixture.sh"], | |
| } | |
| if input_type == "past_skills": | |
| return { | |
| "task": f"Normalize and execute imported legacy Skill {fixture.get('source_id')}.", | |
| "source_context": f"Local fixture: {local_path}", | |
| "artifact_type": "document", | |
| "source_files": [local_path] if local_path else [], | |
| "project_files": [], | |
| "target_runtime": "SkillOS local harness", | |
| } | |
| return { | |
| "task": f"Replay and summarize trajectory {fixture.get('source_id')}.", | |
| "context": {"source_id": fixture.get("source_id"), "local_path": local_path}, | |
| } | |
| def example_from_schema(schema: dict[str, Any]) -> dict[str, Any]: | |
| properties = schema.get("properties") if isinstance(schema.get("properties"), dict) else {} | |
| required = schema.get("required") if isinstance(schema.get("required"), list) else list(properties) | |
| return { | |
| name: example_value(properties.get(name, {})) | |
| for name in required | |
| if isinstance(name, str) | |
| } | |
| def example_value(schema: dict[str, Any]) -> Any: | |
| schema_type = schema.get("type") | |
| if schema_type == "boolean": | |
| return True | |
| if schema_type == "array": | |
| return ["demo"] | |
| if schema_type == "object": | |
| return {"demo": True} | |
| if schema_type in {"number", "integer"}: | |
| return 1 | |
| return "demo" | |
| def empty_value(value: Any) -> Any: | |
| if isinstance(value, list): | |
| return [] | |
| if isinstance(value, dict): | |
| return {} | |
| if isinstance(value, bool): | |
| return False | |
| if isinstance(value, (int, float)): | |
| return 0 | |
| return "" | |
| def summarize_loop(payload: dict[str, Any]) -> dict[str, Any]: | |
| attempts = payload.get("attempts") if isinstance(payload.get("attempts"), list) else [] | |
| return { | |
| "http_error": payload.get("http_error"), | |
| "status": payload.get("status"), | |
| "promotion_allowed": bool(payload.get("promotion_allowed")), | |
| "attempt_count": payload.get("attempt_count"), | |
| "repair_count": len(payload.get("repairs") if isinstance(payload.get("repairs"), list) else []), | |
| "final_state": payload.get("final_state"), | |
| "score": payload.get("score"), | |
| "loop_id": payload.get("loop_id"), | |
| "evidence_path": payload.get("evidence_path"), | |
| "failure_reasons": [ | |
| attempt.get("failure_reason") | |
| for attempt in attempts | |
| if isinstance(attempt, dict) and attempt.get("failure_reason") | |
| ], | |
| } | |
| def score_checks(checks: list[dict[str, Any]]) -> dict[str, Any]: | |
| by_type: dict[str, list[dict[str, Any]]] = defaultdict(list) | |
| for check in checks: | |
| by_type[str(check.get("input_type") or "")].append(check) | |
| positive_pass = sum(1 for item in checks if item.get("positive_pass")) | |
| negative_rejected = sum(1 for item in checks if item.get("negative_rejected")) | |
| total = len(checks) | |
| return { | |
| "sample_count": total, | |
| "positive_pass_rate": round(positive_pass / total, 3) if total else 0.0, | |
| "negative_rejection_rate": round(negative_rejected / total, 3) if total else 0.0, | |
| "input_types_meeting_minimum": sum(1 for items in by_type.values() if len(items) >= 3), | |
| "by_input_type": { | |
| input_type: { | |
| "count": len(items), | |
| "positive_pass_rate": round(sum(1 for item in items if item.get("positive_pass")) / len(items), 3), | |
| "negative_rejection_rate": round(sum(1 for item in items if item.get("negative_rejected")) / len(items), 3), | |
| } | |
| for input_type, items in sorted(by_type.items()) | |
| }, | |
| } | |
| def render_report(summary: dict[str, Any]) -> str: | |
| scores = summary.get("scores", {}) | |
| lines = [ | |
| "# SkillOS P0 Harness Positive / Negative Report", | |
| "", | |
| f"- Started: `{summary.get('started_at')}`", | |
| f"- Finished: `{summary.get('finished_at')}`", | |
| f"- API base: `{summary.get('api_base')}`", | |
| f"- Run dir: `{summary.get('run_dir')}`", | |
| f"- Sample count: `{scores.get('sample_count', 0)}`", | |
| f"- Positive pass rate: `{scores.get('positive_pass_rate', 0.0)}`", | |
| f"- Negative rejection rate: `{scores.get('negative_rejection_rate', 0.0)}`", | |
| f"- Input types with at least 3 samples: `{scores.get('input_types_meeting_minimum', 0)}/5`", | |
| "", | |
| "## By Input Type", | |
| "", | |
| "| input_type | count | positive pass | negative rejected |", | |
| "| --- | ---: | ---: | ---: |", | |
| ] | |
| for input_type, item in (scores.get("by_input_type") or {}).items(): | |
| lines.append( | |
| f"| {input_type} | {item['count']} | {item['positive_pass_rate']:.3f} | {item['negative_rejection_rate']:.3f} |" | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| "## Checks", | |
| "", | |
| "| input_type | source_id | positive | negative rejected | attempts | repairs | final_state | evidence_path |", | |
| "| --- | --- | --- | --- | ---: | ---: | --- | --- |", | |
| ] | |
| ) | |
| for item in summary.get("checks", []): | |
| lines.append( | |
| "| {input_type} | {source_id} | {positive} | {negative} | {attempts} | {repairs} | {state} | `{evidence}` |".format( | |
| input_type=item.get("input_type"), | |
| source_id=item.get("source_id"), | |
| positive=item.get("positive_pass"), | |
| negative=item.get("negative_rejected"), | |
| attempts=item.get("attempt_count"), | |
| repairs=item.get("repair_count"), | |
| state=item.get("final_state"), | |
| evidence=item.get("evidence_path") or "", | |
| ) | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| "## Boundary", | |
| "", | |
| "These checks prove that representative generated Skills can be put through the S2 -> harness -> verifier -> S3 gate with deterministic local contracts. They do not prove open-world semantic correctness or official SkillsBench sandbox scores.", | |
| "", | |
| ] | |
| ) | |
| return "\n".join(lines) | |
| def safe_filename(value: str) -> str: | |
| normalized = re.sub(r"[^A-Za-z0-9_.-]+", "_", str(value or "")).strip("._") | |
| return normalized[:120] or "artifact" | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |