Skip to content

Commit bd0e671

Browse files
fix(eval): smoke_eval resolves models via tiers; sonnet-5 baseline recorded (#94)
smoke_eval.py hardcoded sonnet-4-6/opus-4-6 — the rag-gate ATTUNE_MODEL_PREMIUM pin never reached it. Models now resolve via attune_author.model_tiers (answers=capable, judge=premium). Two live Claude 5 fixes: no temperature param (400 'deprecated for this model'), first-TEXT-block extraction (responses can lead with a ThinkingBlock). BASELINES.md records the first sonnet-5-judged run: faithfulness 100% (PASS), strict accuracy 60% advisory (was 40%). specs/fable-model-tiers task 10 (attune workspace repo). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 09a874f commit bd0e671

2 files changed

Lines changed: 52 additions & 6 deletions

File tree

benchmarks/BASELINES.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,30 @@ Five questions chosen for maximum discrimination in CI (lowest cost, highest sig
105105
With working RAG (treated condition): 5/5 correct → faithfulness 100%, accuracy 100%.
106106
With broken RAG (baseline condition): 2/5 hallucinated + 3/5 partial → faithfulness 60%, accuracy 0%.
107107
Both outcomes are far from the gates (95% / 85%), making the smoke set decisive.
108+
109+
## Sonnet-5 Judge Baseline (2026-07-10 — model tiers rollout)
110+
111+
`smoke_eval.py` now resolves its models via the attune tier contract
112+
(`attune_author.model_tiers`): answers = capable tier, judge = premium
113+
tier. The rag-gate CI pin (`ATTUNE_MODEL_PREMIUM=claude-sonnet-5`) makes
114+
CI runs sonnet-5-judged; previously the models were hardcoded
115+
(sonnet-4-6 answers / opus-4-6 judge) and the pin was decorative.
116+
117+
First sonnet-5/sonnet-5 run (local, dev key, 2026-07-10 — recorded for
118+
specs/fable-model-tiers task 10 and specs/rag-gate-accuracy-baseline in
119+
the attune workspace repo):
120+
121+
| Metric | Score | Gate | Result |
122+
| :----- | ----: | ---: | :----- |
123+
| Faithfulness (hard) | 100.0% | ≥95% | ✅ PASS |
124+
| Strict Accuracy (advisory) | 60.0% | ≥85% | ⚠️ advisory warn (was 40% under sonnet-4-6/opus-4-6) |
125+
126+
Per-question (treated): q2 correct, q9 correct, q12 correct,
127+
q27 partial, q39 partial. The enum (q27) and dataclass (q39) questions
128+
remain the accuracy stragglers — q39 template regeneration is the
129+
standing remediation candidate.
130+
131+
Claude 5 API notes baked into `smoke_eval.py` by this run: the
132+
`temperature` param is rejected ("deprecated for this model", 400), and
133+
responses may lead with a ThinkingBlock — read the first TEXT block,
134+
never `content[0].text`.

benchmarks/smoke_eval.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
from anthropic import Anthropic
3232
from dotenv import load_dotenv
3333

34+
from attune_author.model_tiers import resolve_model
35+
3436
load_dotenv()
3537

3638
# ---------------------------------------------------------------------------
@@ -41,8 +43,14 @@
4143
BENCH = REPO / "benchmarks" / "hallucination-v0.3.9"
4244

4345
SMOKE_QUESTION_IDS = {2, 9, 12, 27, 39}
44-
ANSWER_MODEL_ID = "claude-sonnet-4-6"
45-
JUDGE_MODEL_ID = "claude-opus-4-6"
46+
# Models resolve via the attune tier contract so the rag-gate CI pin
47+
# (ATTUNE_MODEL_PREMIUM=claude-sonnet-5, guard-stepped) actually reaches
48+
# the eval — previously these were hardcoded (sonnet-4-6 / opus-4-6) and
49+
# the pin was decorative. Judge = premium tier (CI pins sonnet-5 to keep
50+
# the $5/$20 ATTUNE_CI_EVAL_KEY caps valid); answers = capable tier.
51+
# specs/fable-model-tiers tasks 8/10 (attune workspace repo).
52+
ANSWER_MODEL_ID = resolve_model("capable")
53+
JUDGE_MODEL_ID = resolve_model("premium")
4654

4755
FAITHFULNESS_GATE = 0.95 # end_user persona — HARD gate (drives exit code)
4856
ACCURACY_GATE = 0.85 # developer persona — ADVISORY on smoke; full-25 enforces
@@ -151,16 +159,28 @@ def build_treated_context(baseline: str, feature: str) -> str:
151159
_JSON_RE = re.compile(r"\{[\s\S]*\}")
152160

153161

162+
def _first_text(response) -> str:
163+
"""First TEXT block — Claude 5 responses can lead with a ThinkingBlock
164+
(no ``.text``) even without requesting thinking. Hit live 2026-07-10."""
165+
for block in response.content or []:
166+
text = getattr(block, "text", None)
167+
if text:
168+
return text
169+
return ""
170+
171+
154172
def get_answer(client: Anthropic, context: str, question: str) -> str:
155173
user = f"{context}\n\n---\n\n# Question\n\n{question}"
174+
# No explicit temperature: Claude 5 models reject the param
175+
# ("`temperature` is deprecated for this model", 400) — hit live
176+
# 2026-07-10 on the first sonnet-5 baseline run.
156177
r = client.messages.create(
157178
model=ANSWER_MODEL_ID,
158179
max_tokens=1024,
159-
temperature=0,
160180
system=ANSWER_SYSTEM,
161181
messages=[{"role": "user", "content": user}],
162182
)
163-
return r.content[0].text
183+
return _first_text(r)
164184

165185

166186
def judge_answer(client: Anthropic, question: str, truth: str, answer: str) -> dict:
@@ -172,11 +192,10 @@ def judge_answer(client: Anthropic, question: str, truth: str, answer: str) -> d
172192
r = client.messages.create(
173193
model=JUDGE_MODEL_ID,
174194
max_tokens=512,
175-
temperature=0,
176195
system=JUDGE_SYSTEM,
177196
messages=[{"role": "user", "content": user}],
178197
)
179-
raw = r.content[0].text
198+
raw = _first_text(r)
180199
m = _JSON_RE.search(raw)
181200
if not m:
182201
return {"verdict": "error", "reasoning": f"No JSON in judge response: {raw[:200]}"}

0 commit comments

Comments
 (0)