feat: expand family suite and publish eval dashboard
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
PYTHON ?= python3
|
||||
|
||||
.PHONY: eval eval-suite failure-regression-check package-check package-failure-check snapshot-check validate lint test clean
|
||||
.PHONY: eval eval-suite results-panel failure-regression-check package-check package-failure-check snapshot-check validate lint test clean
|
||||
|
||||
eval:
|
||||
$(PYTHON) scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt
|
||||
@@ -8,6 +8,9 @@ eval:
|
||||
eval-suite:
|
||||
$(PYTHON) scripts/run_eval_suite.py
|
||||
|
||||
results-panel:
|
||||
$(PYTHON) scripts/render_eval_dashboard.py
|
||||
|
||||
failure-regression-check:
|
||||
$(PYTHON) tests/verify_failure_regressions.py
|
||||
|
||||
|
||||
@@ -46,9 +46,46 @@ make test
|
||||
|
||||
## Results
|
||||
|
||||
- `make test` passes locally
|
||||
- semantic trigger eval: `0` false positives, `0` false negatives on the current regression set
|
||||
- eval suite: train / dev / holdout all pass, with family-based reporting
|
||||
The homepage panel below is generated from the current eval suite so the family-level outcome is visible without opening raw JSON.
|
||||
|
||||
<!-- BEGIN:EVAL_RESULTS -->
|
||||
- regression corpus: `66` prompts across `21` families
|
||||
- aggregate result: `0` false positives, `0` false negatives, average precision `1.0`, average recall `1.0`
|
||||
- suite status:
|
||||
|
||||
| Suite | Cases | FP | FN | Precision | Recall |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| train | 31 | 0 | 0 | 1.0 | 1.0 |
|
||||
| dev | 22 | 0 | 0 | 1.0 | 1.0 |
|
||||
| holdout | 13 | 0 | 0 | 1.0 | 1.0 |
|
||||
|
||||
| Family | Cases | Pass Rate |
|
||||
| --- | ---: | ---: |
|
||||
| `brainstorm_only` | 2 | 1.0 |
|
||||
| `brainstorm_vs_build` | 1 | 1.0 |
|
||||
| `complex_multi_asset` | 3 | 1.0 |
|
||||
| `document_export_vs_agent_skill` | 4 | 1.0 |
|
||||
| `document_only` | 3 | 1.0 |
|
||||
| `explain_not_package` | 1 | 1.0 |
|
||||
| `explain_only` | 5 | 1.0 |
|
||||
| `future_outline_vs_build` | 4 | 1.0 |
|
||||
| `iterate_existing_skill` | 5 | 1.0 |
|
||||
| `long_context_document_only` | 3 | 1.0 |
|
||||
| `long_context_near_neighbor` | 3 | 1.0 |
|
||||
| `long_context_summary_only` | 2 | 1.0 |
|
||||
| `long_context_trigger` | 4 | 1.0 |
|
||||
| `meta_skill_creation` | 1 | 1.0 |
|
||||
| `one_off_vs_reusable` | 2 | 1.0 |
|
||||
| `package_for_team` | 2 | 1.0 |
|
||||
| `paraphrase_trigger` | 5 | 1.0 |
|
||||
| `partial_scaffold_not_full_skill` | 4 | 1.0 |
|
||||
| `summary_only` | 3 | 1.0 |
|
||||
| `translate_only` | 4 | 1.0 |
|
||||
| `workflow_to_skill` | 5 | 1.0 |
|
||||
|
||||
Full reports: [reports/eval_suite.json](reports/eval_suite.json) and [reports/family_summary.md](reports/family_summary.md)
|
||||
<!-- END:EVAL_RESULTS -->
|
||||
|
||||
- packaging validation: `openai`, `claude`, and `generic` targets pass contract checks
|
||||
- packaging failure fixtures: invalid metadata, invalid YAML, and unsupported targets fail as expected
|
||||
- failure library regressions: anti-pattern families pass automated checks
|
||||
|
||||
@@ -12,6 +12,7 @@ Contents:
|
||||
- `sample_trigger_report.json`: example comparison output using the current recommended threshold
|
||||
- `failure-cases.md`: current weak spots and regression targets
|
||||
- `packaging_expectations.json`: required packaging behaviors for supported targets
|
||||
- `../reports/`: generated suite JSON plus the homepage-visible family summary panel source
|
||||
|
||||
Use:
|
||||
|
||||
@@ -19,6 +20,7 @@ Use:
|
||||
python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json
|
||||
python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt
|
||||
python3 scripts/run_eval_suite.py
|
||||
python3 scripts/render_eval_dashboard.py
|
||||
python3 tests/verify_failure_regressions.py
|
||||
python3 scripts/cross_packager.py . --platform openai --platform claude --expectations evals/packaging_expectations.json --zip
|
||||
python3 tests/verify_packager_failures.py
|
||||
@@ -33,5 +35,7 @@ Regression scope now includes:
|
||||
- mixed-intent negatives
|
||||
- explicit "do not build a skill" negatives
|
||||
- semantic exclusion cases such as one-off, document-only, and future-outline prompts
|
||||
- paraphrase families that avoid the original wording while preserving the same trigger intent
|
||||
- long-context contamination cases where build intent or no-build intent appears after unrelated setup text
|
||||
- family-based reporting across workflow-to-skill, iterate-existing-skill, document-only, one-off, and future-outline cases
|
||||
- holdout verification
|
||||
|
||||
@@ -29,6 +29,18 @@
|
||||
{
|
||||
"text": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.",
|
||||
"family": "iterate_existing_skill"
|
||||
},
|
||||
{
|
||||
"text": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Codify this workflow into a shared capability package and validate the route boundary before distribution.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.",
|
||||
"family": "long_context_trigger"
|
||||
}
|
||||
],
|
||||
"should_not_trigger": [
|
||||
@@ -51,6 +63,14 @@
|
||||
{
|
||||
"text": "Summarize this workflow and list the main points only.",
|
||||
"family": "summary_only"
|
||||
},
|
||||
{
|
||||
"text": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.",
|
||||
"family": "long_context_summary_only"
|
||||
}
|
||||
],
|
||||
"near_neighbor": [
|
||||
@@ -73,6 +93,14 @@
|
||||
{
|
||||
"text": "Polish these notes into a reusable-looking document without turning them into an agent skill.",
|
||||
"family": "document_export_vs_agent_skill"
|
||||
},
|
||||
{
|
||||
"text": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.",
|
||||
"family": "long_context_near_neighbor"
|
||||
},
|
||||
{
|
||||
"text": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.",
|
||||
"family": "future_outline_vs_build"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
{
|
||||
"text": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.",
|
||||
"family": "workflow_to_skill"
|
||||
},
|
||||
{
|
||||
"text": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.",
|
||||
"family": "long_context_trigger"
|
||||
}
|
||||
],
|
||||
"should_not_trigger": [
|
||||
@@ -31,6 +39,14 @@
|
||||
{
|
||||
"text": "Explain this release checklist in simpler language; do not package it.",
|
||||
"family": "explain_only"
|
||||
},
|
||||
{
|
||||
"text": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "Translate this long runbook into Russian and keep it as documentation only with no skill package.",
|
||||
"family": "translate_only"
|
||||
}
|
||||
],
|
||||
"near_neighbor": [
|
||||
@@ -45,6 +61,10 @@
|
||||
{
|
||||
"text": "Turn this into a document package for the wiki, not an agent skill.",
|
||||
"family": "document_export_vs_agent_skill"
|
||||
},
|
||||
{
|
||||
"text": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.",
|
||||
"family": "long_context_near_neighbor"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+1220
-17
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,13 @@
|
||||
"meta skill",
|
||||
"meta-skill",
|
||||
"turn this into a skill",
|
||||
"turn all of that into one reusable skill package"
|
||||
"turn all of that into one reusable skill package",
|
||||
"codify this workflow",
|
||||
"codify this routine",
|
||||
"operationalize this workflow as a reusable skill",
|
||||
"formalize this process into a reusable capability",
|
||||
"turn this playbook into an agent capability package",
|
||||
"convert this into a maintained skill"
|
||||
]
|
||||
},
|
||||
"transform_workflow": {
|
||||
@@ -33,7 +39,15 @@
|
||||
"operations checklist",
|
||||
"internal process note",
|
||||
"existing skill draft",
|
||||
"rough notes"
|
||||
"rough notes",
|
||||
"standard operating procedure",
|
||||
"sop",
|
||||
"transcript",
|
||||
"workflow fragments",
|
||||
"release notes",
|
||||
"onboarding flow",
|
||||
"support escalation routine",
|
||||
"quarterly release routine"
|
||||
]
|
||||
},
|
||||
"reuse_package": {
|
||||
@@ -47,7 +61,13 @@
|
||||
"share it with the team",
|
||||
"team reuse",
|
||||
"internal workflows",
|
||||
"production-ready"
|
||||
"production-ready",
|
||||
"library entry",
|
||||
"shared capability",
|
||||
"team library",
|
||||
"ops library",
|
||||
"maintained",
|
||||
"operationalize"
|
||||
]
|
||||
},
|
||||
"eval_optimize": {
|
||||
@@ -60,7 +80,12 @@
|
||||
"benchmark",
|
||||
"validation",
|
||||
"validate",
|
||||
"packaging checks"
|
||||
"packaging checks",
|
||||
"false positives",
|
||||
"false negatives",
|
||||
"stress test",
|
||||
"route boundary",
|
||||
"hardening"
|
||||
]
|
||||
},
|
||||
"iterate_existing_skill": {
|
||||
@@ -73,7 +98,11 @@
|
||||
"optimize skill triggering",
|
||||
"package this skill",
|
||||
"sharing it with the team",
|
||||
"before sharing it with the team"
|
||||
"before sharing it with the team",
|
||||
"tighten the boundary",
|
||||
"revise this skill",
|
||||
"draft skill",
|
||||
"harden this skill for library reuse"
|
||||
]
|
||||
},
|
||||
"multi_asset": {
|
||||
@@ -172,7 +201,12 @@
|
||||
"not a skill yet",
|
||||
"plain note",
|
||||
"keep it as a plain note",
|
||||
"no packaging needed"
|
||||
"no packaging needed",
|
||||
"not for agent execution",
|
||||
"no skill package",
|
||||
"do not convert it into a reusable capability",
|
||||
"keep it as documentation only",
|
||||
"discussion only"
|
||||
]
|
||||
},
|
||||
"document_only": {
|
||||
@@ -187,7 +221,10 @@
|
||||
"article",
|
||||
"blog title",
|
||||
"headline",
|
||||
"landing page"
|
||||
"landing page",
|
||||
"reference document",
|
||||
"knowledge base",
|
||||
"template only"
|
||||
]
|
||||
},
|
||||
"future_outline": {
|
||||
@@ -197,7 +234,10 @@
|
||||
"future skill",
|
||||
"possible future skill",
|
||||
"before we decide whether to build",
|
||||
"outline for a possible future skill"
|
||||
"outline for a possible future skill",
|
||||
"maybe later",
|
||||
"for later",
|
||||
"future-ready outline"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,22 @@
|
||||
{
|
||||
"text": "Standardize this recurring workflow as a shareable skill package with references.",
|
||||
"family": "package_for_team"
|
||||
},
|
||||
{
|
||||
"text": "Codify this recurring support escalation workflow into a maintained skill for the ops library.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Operationalize this workflow as a reusable skill with validation notes and team library metadata.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Below are release notes, rough notes, and a transcript from the last handoff. Ignore the noise and turn this runbook into a reusable agent skill with references and scripts so the team can reuse it every quarter.",
|
||||
"family": "long_context_trigger"
|
||||
},
|
||||
{
|
||||
"text": "We have a standard operating procedure, checklist, and prompt history scattered across docs. Formalize this process into a reusable capability, add validation, and package it for team reuse.",
|
||||
"family": "long_context_trigger"
|
||||
}
|
||||
],
|
||||
"should_not_trigger": [
|
||||
@@ -75,6 +91,18 @@
|
||||
{
|
||||
"text": "Explain the difference between a runbook and a workflow.",
|
||||
"family": "explain_only"
|
||||
},
|
||||
{
|
||||
"text": "Below is a long SOP, but I only want a summary for leadership and a recap of the main points. Do not build anything reusable.",
|
||||
"family": "long_context_summary_only"
|
||||
},
|
||||
{
|
||||
"text": "Keep this as documentation only for the knowledge base and rewrite it as a reference document.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "Translate this long onboarding flow into French and keep it as documentation only with no skill package.",
|
||||
"family": "translate_only"
|
||||
}
|
||||
],
|
||||
"near_neighbor": [
|
||||
@@ -101,6 +129,14 @@
|
||||
{
|
||||
"text": "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.",
|
||||
"family": "partial_scaffold_not_full_skill"
|
||||
},
|
||||
{
|
||||
"text": "We may convert this workflow later, but for now keep it as documentation only and produce a reference document.",
|
||||
"family": "long_context_near_neighbor"
|
||||
},
|
||||
{
|
||||
"text": "Draft a template only from this process and stop short of making a full skill package.",
|
||||
"family": "partial_scaffold_not_full_skill"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -73,6 +73,42 @@
|
||||
{
|
||||
"text": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.",
|
||||
"family": "workflow_to_skill"
|
||||
},
|
||||
{
|
||||
"text": "Codify this recurring support escalation workflow into a maintained skill for the ops library.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Operationalize this workflow as a reusable skill with validation notes and team library metadata.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Codify this workflow into a shared capability package and validate the route boundary before distribution.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.",
|
||||
"family": "paraphrase_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Below are release notes, rough notes, and a transcript from the last handoff. Ignore the noise and turn this runbook into a reusable agent skill with references and scripts so the team can reuse it every quarter.",
|
||||
"family": "long_context_trigger"
|
||||
},
|
||||
{
|
||||
"text": "We have a standard operating procedure, checklist, and prompt history scattered across docs. Formalize this process into a reusable capability, add validation, and package it for team reuse.",
|
||||
"family": "long_context_trigger"
|
||||
},
|
||||
{
|
||||
"text": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.",
|
||||
"family": "long_context_trigger"
|
||||
},
|
||||
{
|
||||
"text": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.",
|
||||
"family": "long_context_trigger"
|
||||
}
|
||||
],
|
||||
"should_not_trigger": [
|
||||
@@ -135,6 +171,34 @@
|
||||
{
|
||||
"text": "Explain this release checklist in simpler language; do not package it.",
|
||||
"family": "explain_only"
|
||||
},
|
||||
{
|
||||
"text": "Below is a long SOP, but I only want a summary for leadership and a recap of the main points. Do not build anything reusable.",
|
||||
"family": "long_context_summary_only"
|
||||
},
|
||||
{
|
||||
"text": "Keep this as documentation only for the knowledge base and rewrite it as a reference document.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "Translate this long onboarding flow into French and keep it as documentation only with no skill package.",
|
||||
"family": "translate_only"
|
||||
},
|
||||
{
|
||||
"text": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.",
|
||||
"family": "long_context_summary_only"
|
||||
},
|
||||
{
|
||||
"text": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.",
|
||||
"family": "long_context_document_only"
|
||||
},
|
||||
{
|
||||
"text": "Translate this long runbook into Russian and keep it as documentation only with no skill package.",
|
||||
"family": "translate_only"
|
||||
}
|
||||
],
|
||||
"near_neighbor": [
|
||||
@@ -193,6 +257,26 @@
|
||||
{
|
||||
"text": "Turn this into a document package for the wiki, not an agent skill.",
|
||||
"family": "document_export_vs_agent_skill"
|
||||
},
|
||||
{
|
||||
"text": "We may convert this workflow later, but for now keep it as documentation only and produce a reference document.",
|
||||
"family": "long_context_near_neighbor"
|
||||
},
|
||||
{
|
||||
"text": "Draft a template only from this process and stop short of making a full skill package.",
|
||||
"family": "partial_scaffold_not_full_skill"
|
||||
},
|
||||
{
|
||||
"text": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.",
|
||||
"family": "long_context_near_neighbor"
|
||||
},
|
||||
{
|
||||
"text": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.",
|
||||
"family": "future_outline_vs_build"
|
||||
},
|
||||
{
|
||||
"text": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.",
|
||||
"family": "long_context_near_neighbor"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
# Eval Family Summary
|
||||
|
||||
- total cases: `66`
|
||||
- families: `21`
|
||||
- false positives: `0`
|
||||
- false negatives: `0`
|
||||
- average precision: `1.0`
|
||||
- average recall: `1.0`
|
||||
|
||||
## Family Results
|
||||
|
||||
| Family | Cases | Passed | FP | FN | Pass Rate |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| `brainstorm_only` | 2 | 2 | 0 | 0 | 1.0 |
|
||||
| `brainstorm_vs_build` | 1 | 1 | 0 | 0 | 1.0 |
|
||||
| `complex_multi_asset` | 3 | 3 | 0 | 0 | 1.0 |
|
||||
| `document_export_vs_agent_skill` | 4 | 4 | 0 | 0 | 1.0 |
|
||||
| `document_only` | 3 | 3 | 0 | 0 | 1.0 |
|
||||
| `explain_not_package` | 1 | 1 | 0 | 0 | 1.0 |
|
||||
| `explain_only` | 5 | 5 | 0 | 0 | 1.0 |
|
||||
| `future_outline_vs_build` | 4 | 4 | 0 | 0 | 1.0 |
|
||||
| `iterate_existing_skill` | 5 | 5 | 0 | 0 | 1.0 |
|
||||
| `long_context_document_only` | 3 | 3 | 0 | 0 | 1.0 |
|
||||
| `long_context_near_neighbor` | 3 | 3 | 0 | 0 | 1.0 |
|
||||
| `long_context_summary_only` | 2 | 2 | 0 | 0 | 1.0 |
|
||||
| `long_context_trigger` | 4 | 4 | 0 | 0 | 1.0 |
|
||||
| `meta_skill_creation` | 1 | 1 | 0 | 0 | 1.0 |
|
||||
| `one_off_vs_reusable` | 2 | 2 | 0 | 0 | 1.0 |
|
||||
| `package_for_team` | 2 | 2 | 0 | 0 | 1.0 |
|
||||
| `paraphrase_trigger` | 5 | 5 | 0 | 0 | 1.0 |
|
||||
| `partial_scaffold_not_full_skill` | 4 | 4 | 0 | 0 | 1.0 |
|
||||
| `summary_only` | 3 | 3 | 0 | 0 | 1.0 |
|
||||
| `translate_only` | 4 | 4 | 0 | 0 | 1.0 |
|
||||
| `workflow_to_skill` | 5 | 5 | 0 | 0 | 1.0 |
|
||||
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BEGIN_MARKER = "<!-- BEGIN:EVAL_RESULTS -->"
|
||||
END_MARKER = "<!-- END:EVAL_RESULTS -->"
|
||||
|
||||
|
||||
def run_eval_suite(args: argparse.Namespace) -> dict:
|
||||
proc = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/run_eval_suite.py",
|
||||
"--eval-dir",
|
||||
args.eval_dir,
|
||||
"--description-file",
|
||||
args.description_file,
|
||||
"--baseline-description-file",
|
||||
args.baseline_description_file,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def render_readme_panel(report: dict) -> str:
|
||||
summary = report["summary"]
|
||||
suites = report["suites"]
|
||||
families = report["family_summary"]
|
||||
|
||||
lines = [
|
||||
BEGIN_MARKER,
|
||||
f"- regression corpus: `{summary['total_cases']}` prompts across `{summary['family_count']}` families",
|
||||
f"- aggregate result: `{summary['false_positives']}` false positives, `{summary['false_negatives']}` false negatives, average precision `{summary['average_precision']}`, average recall `{summary['average_recall']}`",
|
||||
"- suite status:",
|
||||
"",
|
||||
"| Suite | Cases | FP | FN | Precision | Recall |",
|
||||
"| --- | ---: | ---: | ---: | ---: | ---: |",
|
||||
]
|
||||
for suite_name in ("train", "dev", "holdout"):
|
||||
suite = suites[suite_name]
|
||||
cases = sum(bucket["total"] for bucket in suite["bucket_stats"].values())
|
||||
lines.append(
|
||||
f"| {suite_name} | {cases} | {suite['false_positives']} | {suite['false_negatives']} | {suite['precision']} | {suite['recall']} |"
|
||||
)
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
"| Family | Cases | Pass Rate |",
|
||||
"| --- | ---: | ---: |",
|
||||
]
|
||||
)
|
||||
for family, stats in sorted(families.items()):
|
||||
lines.append(f"| `{family}` | {stats['total']} | {stats['pass_rate']} |")
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
"Full reports: [reports/eval_suite.json](reports/eval_suite.json) and [reports/family_summary.md](reports/family_summary.md)",
|
||||
END_MARKER,
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def render_family_summary(report: dict) -> str:
|
||||
summary = report["summary"]
|
||||
lines = [
|
||||
"# Eval Family Summary",
|
||||
"",
|
||||
f"- total cases: `{summary['total_cases']}`",
|
||||
f"- families: `{summary['family_count']}`",
|
||||
f"- false positives: `{summary['false_positives']}`",
|
||||
f"- false negatives: `{summary['false_negatives']}`",
|
||||
f"- average precision: `{summary['average_precision']}`",
|
||||
f"- average recall: `{summary['average_recall']}`",
|
||||
"",
|
||||
"## Family Results",
|
||||
"",
|
||||
"| Family | Cases | Passed | FP | FN | Pass Rate |",
|
||||
"| --- | ---: | ---: | ---: | ---: | ---: |",
|
||||
]
|
||||
for family, stats in sorted(report["family_summary"].items()):
|
||||
lines.append(
|
||||
f"| `{family}` | {stats['total']} | {stats['passed']} | {stats['false_positives']} | {stats['false_negatives']} | {stats['pass_rate']} |"
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def update_readme(readme_path: Path, panel: str) -> None:
|
||||
text = readme_path.read_text(encoding="utf-8")
|
||||
if BEGIN_MARKER not in text or END_MARKER not in text:
|
||||
raise SystemExit(f"README markers not found in {readme_path}")
|
||||
start = text.index(BEGIN_MARKER)
|
||||
end = text.index(END_MARKER) + len(END_MARKER)
|
||||
updated = text[:start] + panel + text[end:]
|
||||
readme_path.write_text(updated, encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Render README-visible eval results and full family report.")
|
||||
parser.add_argument("--eval-dir", default="evals")
|
||||
parser.add_argument("--description-file", default="evals/improved_description.txt")
|
||||
parser.add_argument("--baseline-description-file", default="evals/baseline_description.txt")
|
||||
parser.add_argument("--output-dir", default="reports")
|
||||
parser.add_argument("--readme", default="README.md")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = run_eval_suite(args)
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
(output_dir / "eval_suite.json").write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
(output_dir / "family_summary.md").write_text(render_family_summary(report), encoding="utf-8")
|
||||
update_readme(Path(args.readme), render_readme_panel(report))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -33,6 +33,7 @@ def main() -> None:
|
||||
parser.add_argument("--eval-dir", default="evals", help="Root eval directory")
|
||||
parser.add_argument("--description-file", default="evals/improved_description.txt")
|
||||
parser.add_argument("--baseline-description-file", default="evals/baseline_description.txt")
|
||||
parser.add_argument("--output-file", help="Optional path to write the combined JSON report")
|
||||
args = parser.parse_args()
|
||||
|
||||
root = Path(args.eval_dir).resolve()
|
||||
@@ -65,15 +66,21 @@ def main() -> None:
|
||||
for family, stats in family_summary.items():
|
||||
stats["pass_rate"] = round(stats["passed"] / stats["total"], 3) if stats["total"] else None
|
||||
|
||||
total_cases = sum(stats["total"] for stats in family_summary.values())
|
||||
summary = {
|
||||
"suite_count": len(suites),
|
||||
"total_cases": total_cases,
|
||||
"family_count": len(family_summary),
|
||||
"false_positives": aggregate["false_positives"],
|
||||
"false_negatives": aggregate["false_negatives"],
|
||||
"average_precision": round(sum(aggregate["precision"]) / len(aggregate["precision"]), 3) if aggregate["precision"] else None,
|
||||
"average_recall": round(sum(aggregate["recall"]) / len(aggregate["recall"]), 3) if aggregate["recall"] else None,
|
||||
}
|
||||
output = {"summary": summary, "family_summary": family_summary, "suites": suites}
|
||||
print(json.dumps(output, ensure_ascii=False, indent=2))
|
||||
rendered = json.dumps(output, ensure_ascii=False, indent=2)
|
||||
if args.output_file:
|
||||
Path(args.output_file).write_text(rendered + "\n", encoding="utf-8")
|
||||
print(rendered)
|
||||
if summary["false_positives"] > 0 or summary["false_negatives"] > 0:
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user