mirror of
https://github.com/wassname/persona-steering-template-library.git
synced 2026-08-11 11:23:11 +08:00
refactor: sort scripts/ by who runs it
Top level is now only what the runbook or a re-run touches: validate_persona_axes, bounded_thinking_judge, template_catalog, export_selections, parse_stage_a, run_axis, export_steering_selection. Corpus ingestion and publishing moved to scripts/corpus/, plotting and stats to scripts/report/. Moved files needed parents[1] -> parents[2]; the two corpus scripts that import template_catalog use the sys.path shim bounded_thinking_judge_liveproof already used. Also completes the export_steering_selection rename: an earlier git reset had dropped the staged deletion, leaving both filenames tracked. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
@@ -9,16 +9,18 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pyarrow as pa
|
||||
import pyarrow.parquet as pq
|
||||
|
||||
from template_catalog import active_template_rows, load_template_catalog
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from template_catalog import active_template_rows, load_template_catalog # noqa: E402
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DATA = ROOT / "data"
|
||||
PERSONA_DATA = DATA / "personas"
|
||||
STATS = ROOT / "data/results/stats"
|
||||
@@ -2,8 +2,11 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from template_catalog import (
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "scripts"))
|
||||
|
||||
from template_catalog import ( # noqa: E402
|
||||
CATALOG_PATH,
|
||||
CATALOG_JSONL_PATH,
|
||||
TEMPLATES_TXT_PATH,
|
||||
@@ -1,177 +0,0 @@
|
||||
"""Export winning Authority-axis ingredients from validator artifacts."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
from tabulate import tabulate
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def read_jsonl(path: Path) -> list[dict]:
|
||||
return [json.loads(line) for line in path.read_text().splitlines() if line.strip()]
|
||||
|
||||
|
||||
def write_jsonl(path: Path, rows: list[dict]) -> None:
|
||||
path.write_text("\n".join(json.dumps(row, ensure_ascii=False) for row in rows) + "\n")
|
||||
|
||||
|
||||
def score_row(row: dict) -> float:
|
||||
if "error" in row:
|
||||
return -1.0
|
||||
return 100.0 * float(row["on_axis_frac"]) * (1.0 - float(row["off_axis_problem_frac"]))
|
||||
|
||||
|
||||
def choose_stage_a(stage_a: dict, axis_filter: str | None) -> dict:
|
||||
summary = stage_a["summary"]
|
||||
if axis_filter is not None:
|
||||
summary = [row for row in summary if row["axis"] == axis_filter]
|
||||
if not summary:
|
||||
raise ValueError(f"stage A has no summary rows for axis_filter={axis_filter!r}")
|
||||
ranked = sorted(
|
||||
summary,
|
||||
key=lambda row: (
|
||||
row["recommended"],
|
||||
row["strict_pass_rate"],
|
||||
row["mean_axis_delta"],
|
||||
-row["mean_off_axis_problem"],
|
||||
-row["mean_max_style_abs_delta"],
|
||||
-row["persona_echo_rate"],
|
||||
-row["refusal_or_ai_break_rate"],
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
return ranked[0]
|
||||
|
||||
|
||||
def write_stage_b_inputs(stage_a_path: Path, out_dir: Path, axis_filter: str | None) -> dict:
|
||||
stage_a = json.loads(stage_a_path.read_text())
|
||||
winner = choose_stage_a(stage_a, axis_filter)
|
||||
axis_id = winner["axis"]
|
||||
template = winner["template"]
|
||||
axes = [axis for axis in stage_a["axes"] if axis["id"] == axis_id]
|
||||
if len(axes) != 1:
|
||||
raise ValueError(f"expected one winning axis {axis_id!r}, found {len(axes)}")
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
write_jsonl(out_dir / "stage_b_axis.jsonl", axes)
|
||||
(out_dir / "stage_b_template.txt").write_text(template + "\n")
|
||||
(out_dir / "stage_a_winner.json").write_text(json.dumps(winner, indent=2))
|
||||
return winner
|
||||
|
||||
|
||||
def select_stage_b(
|
||||
stage_b_path: Path,
|
||||
out_dir: Path,
|
||||
keep_per_source: int,
|
||||
strict_only: bool,
|
||||
min_score: float,
|
||||
) -> list[dict]:
|
||||
artifact = json.loads(stage_b_path.read_text())
|
||||
grouped: dict[str, list[dict]] = defaultdict(list)
|
||||
for row in artifact["results"]:
|
||||
if "error" in row:
|
||||
continue
|
||||
if strict_only and not row["strict_pass"]:
|
||||
continue
|
||||
if score_row(row) < min_score:
|
||||
continue
|
||||
grouped[str(row["source"])].append(row)
|
||||
|
||||
selected: list[dict] = []
|
||||
score_rows: list[dict] = []
|
||||
for source, rows in sorted(grouped.items()):
|
||||
ranked = sorted(
|
||||
rows,
|
||||
key=lambda row: (
|
||||
row["strict_pass"],
|
||||
score_row(row),
|
||||
float(row["axis_delta"]),
|
||||
-float(row["confound_judgment"]["off_axis_problem_likert"]),
|
||||
-float(row["max_style_abs_delta"]),
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
for rank, row in enumerate(ranked, start=1):
|
||||
score_rows.append({
|
||||
"source": source,
|
||||
"rank": rank,
|
||||
"selected": rank <= keep_per_source,
|
||||
"scenario_id": row["scenario_id"],
|
||||
"score": round(score_row(row), 2),
|
||||
"strict_pass": row["strict_pass"],
|
||||
"axis_delta": row["axis_delta"],
|
||||
"off_axis_problem": row["confound_judgment"]["off_axis_problem_likert"],
|
||||
"max_style_abs_delta": row["max_style_abs_delta"],
|
||||
"prompt": row["prompt"],
|
||||
})
|
||||
for row in ranked[:keep_per_source]:
|
||||
selected.append({
|
||||
"id": row["scenario_id"],
|
||||
"prompt": row["prompt"],
|
||||
"source": row["source"],
|
||||
"config": row.get("config"),
|
||||
"self_contained": True,
|
||||
"selection_score": round(score_row(row), 4),
|
||||
"axis_delta": row["axis_delta"],
|
||||
"off_axis_problem": row["confound_judgment"]["off_axis_problem_likert"],
|
||||
"strict_pass": row["strict_pass"],
|
||||
})
|
||||
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
write_jsonl(out_dir / "selected_scenarios.jsonl", selected)
|
||||
with (out_dir / "scenario_scores.csv").open("w", newline="") as fh:
|
||||
writer = csv.DictWriter(fh, fieldnames=list(score_rows[0]))
|
||||
writer.writeheader()
|
||||
writer.writerows(score_rows)
|
||||
examples = []
|
||||
for row in sorted(score_rows, key=lambda r: (r["source"], r["rank"])):
|
||||
if row["rank"] <= 2:
|
||||
examples.append(
|
||||
f"## {row['source']} / {row['scenario_id']}\n\n"
|
||||
f"score={row['score']} axis_delta={row['axis_delta']} "
|
||||
f"off_axis={row['off_axis_problem']} strict_pass={row['strict_pass']}\n\n"
|
||||
f"{row['prompt']}\n"
|
||||
)
|
||||
(out_dir / "selected_examples.md").write_text("\n".join(examples))
|
||||
return selected
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--stage-a", type=Path)
|
||||
ap.add_argument("--stage-b", type=Path)
|
||||
ap.add_argument("--out-dir", type=Path, default=ROOT / "out/authority_selection")
|
||||
ap.add_argument("--keep-per-source", type=int, default=10)
|
||||
ap.add_argument("--axis-filter", type=str)
|
||||
ap.add_argument("--strict-only", action="store_true")
|
||||
ap.add_argument("--min-score", type=float, default=0.0)
|
||||
args = ap.parse_args()
|
||||
|
||||
if args.stage_a is None and args.stage_b is None:
|
||||
raise ValueError("pass --stage-a and/or --stage-b")
|
||||
|
||||
if args.stage_a is not None:
|
||||
winner = write_stage_b_inputs(args.stage_a, args.out_dir, args.axis_filter)
|
||||
print("Stage A winner:")
|
||||
print(tabulate([winner], headers="keys", tablefmt="github", floatfmt=".3f"))
|
||||
|
||||
if args.stage_b is not None:
|
||||
selected = select_stage_b(args.stage_b, args.out_dir, args.keep_per_source, args.strict_only, args.min_score)
|
||||
counts: dict[str, int] = {}
|
||||
for row in selected:
|
||||
counts[row["source"]] = counts.get(row["source"], 0) + 1
|
||||
print("\nSelected scenarios:")
|
||||
print(tabulate(
|
||||
[{"source": source, "n": counts[source]} for source in sorted(counts)],
|
||||
headers="keys",
|
||||
tablefmt="github",
|
||||
))
|
||||
print(f"total={len(selected)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -7,7 +7,7 @@ import statistics
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
STATS = ROOT / "data/results/stats"
|
||||
MODEL_MATRIX = ROOT / "data/results/model_matrix"
|
||||
DOCS_MODEL_MATRIX = ROOT / "docs/results/model_matrix"
|
||||
@@ -12,7 +12,7 @@ from tabulate import tabulate
|
||||
|
||||
import docs_results
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
DEFAULT_PAIR_STATS = docs_results.REFUSAL_MODEL_PAIR_STATS
|
||||
DEFAULT_OUT_PREFIX = docs_results.REFUSAL_MODEL_PREFIX
|
||||
|
||||
Reference in New Issue
Block a user