Skip to content

Commit d6aba45

Browse files
authored
Merge pull request #18 from avalyset/feat/skatteetaten-scenarios
Add skatteetaten scenario pack: 8 scenarios on Norwegian tax administration
2 parents a3d0c90 + 07cd3ac commit d6aba45

11 files changed

Lines changed: 2678 additions & 5 deletions
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env python3
2+
"""
3+
run_skatteetaten_baseline.py
4+
5+
Baseline evaluation of the skatteetaten scenario pack.
6+
Methodology identical to nav_aap baseline run (2026-04-29):
7+
- max_turns: 3
8+
- language: Norsk
9+
- judge: claude-opus-4-7 (anthropic)
10+
- targets: claude-sonnet-4-6, claude-haiku-4-5-20251001
11+
12+
Usage (from repo root, with .env loaded):
13+
.venv/bin/python results/run_skatteetaten_baseline.py
14+
"""
15+
16+
import asyncio
17+
import json
18+
import os
19+
import sys
20+
from datetime import datetime
21+
from pathlib import Path
22+
23+
# Load .env manually (no python-dotenv in venv path)
24+
# Force-set so even empty env vars are overwritten.
25+
env_path = Path(__file__).parent.parent / ".env"
26+
if env_path.exists():
27+
with open(env_path) as f:
28+
for line in f:
29+
line = line.strip()
30+
if "=" in line and not line.startswith("#"):
31+
k, v = line.split("=", 1)
32+
os.environ[k.strip()] = v.strip().strip('"').strip("'")
33+
34+
sys.path.insert(0, str(Path(__file__).parent.parent))
35+
36+
from simpleaudit import ModelAuditor, get_scenarios
37+
38+
JUDGE_MODEL = "claude-opus-4-7"
39+
JUDGE_PROVIDER = "anthropic"
40+
TARGET_CONFIGS = [
41+
{"label": "sonnet46", "model": "claude-sonnet-4-6", "provider": "anthropic"},
42+
{"label": "haiku45", "model": "claude-haiku-4-5-20251001", "provider": "anthropic"},
43+
]
44+
45+
MAX_TURNS = 3
46+
LANGUAGE = "Norsk"
47+
PACK = "skatteetaten"
48+
TODAY = datetime.now().strftime("%Y%m%d")
49+
RESULTS_DIR = Path(__file__).parent
50+
51+
52+
def run_one_target(label: str, model: str, provider: str) -> dict:
53+
print(f"\n{'='*65}")
54+
print(f" TARGET : {provider} / {model}")
55+
print(f" JUDGE : {JUDGE_PROVIDER} / {JUDGE_MODEL}")
56+
print(f" PACK : {PACK} | max_turns={MAX_TURNS} | language={LANGUAGE}")
57+
print(f"{'='*65}\n")
58+
59+
auditor = ModelAuditor(
60+
model=model,
61+
provider=provider,
62+
judge_model=JUDGE_MODEL,
63+
judge_provider=JUDGE_PROVIDER,
64+
max_turns=MAX_TURNS,
65+
verbose=True,
66+
show_progress=True,
67+
)
68+
69+
scenarios = get_scenarios(PACK)
70+
results = auditor.run(scenarios, language=LANGUAGE)
71+
72+
out_path = RESULTS_DIR / f"skatteetaten_baseline_{label}_{TODAY}.json"
73+
results.save(str(out_path))
74+
print(f"\n Saved: {out_path}")
75+
76+
return {
77+
"label": label,
78+
"model": model,
79+
"provider": provider,
80+
"score": results.score,
81+
"passed": results.passed,
82+
"failed": results.failed,
83+
"total": len(results.results),
84+
"severity": dict(results.severity_distribution),
85+
"results": [
86+
{
87+
"scenario_name": r.scenario_name,
88+
"severity": r.severity,
89+
"summary": r.summary,
90+
"issues_found": r.issues_found,
91+
"positive_behaviors": r.positive_behaviors,
92+
"probe_text": r.conversation[0]["content"] if r.conversation else "",
93+
}
94+
for r in results.results
95+
],
96+
"output_file": str(out_path),
97+
}
98+
99+
100+
def main():
101+
all_summaries = []
102+
for cfg in TARGET_CONFIGS:
103+
try:
104+
summary = run_one_target(**cfg)
105+
all_summaries.append(summary)
106+
except Exception as exc:
107+
print(f"\n[ERROR] {cfg['label']}: {exc}")
108+
raise
109+
110+
# Print comparison
111+
print("\n\n" + "═"*65)
112+
print(" SKATTEETATEN BASELINE — COMPARISON")
113+
print("═"*65)
114+
sev_order = ["pass", "low", "medium", "high", "critical"]
115+
for s in all_summaries:
116+
dist = s["severity"]
117+
print(f"\n {s['label']} ({s['model']})")
118+
print(f" Score : {s['score']:.1f}")
119+
print(f" Passed : {s['passed']} / {s['total']}")
120+
for sev in sev_order:
121+
cnt = dist.get(sev, 0)
122+
if cnt:
123+
print(f" {sev:8} : {cnt}")
124+
125+
# Per-scenario table
126+
print("\n\n PER-SCENARIO:")
127+
print(f" {'Scenario':<52} {'Sonnet46':>10} {'Haiku45':>10}")
128+
print(" " + "─"*74)
129+
if len(all_summaries) == 2:
130+
s46 = {r["scenario_name"]: r for r in all_summaries[0]["results"]}
131+
h45 = {r["scenario_name"]: r for r in all_summaries[1]["results"]}
132+
names = [r["scenario_name"] for r in all_summaries[0]["results"]]
133+
for name in names:
134+
sev46 = s46.get(name, {}).get("severity", "?")
135+
sev45 = h45.get(name, {}).get("severity", "?")
136+
print(f" {name[:52]:<52} {sev46:>10} {sev45:>10}")
137+
print("═"*65)
138+
139+
# Save combined summary data for report generation
140+
combined = {
141+
"timestamp": datetime.now().isoformat(),
142+
"pack": PACK,
143+
"max_turns": MAX_TURNS,
144+
"language": LANGUAGE,
145+
"judge_model": JUDGE_MODEL,
146+
"summaries": all_summaries,
147+
}
148+
combined_path = RESULTS_DIR / f"skatteetaten_baseline_combined_{TODAY}.json"
149+
combined_path.write_text(json.dumps(combined, indent=2, ensure_ascii=False))
150+
print(f"\n Combined data: {combined_path}")
151+
152+
return combined
153+
154+
155+
if __name__ == "__main__":
156+
main()

0 commit comments

Comments
 (0)