Skip to content

Live Anthropic + OpenAI judges + canary preflight - #2

Merged
bGOATnote merged 1 commit into
mainfrom
feat/live-judges-and-canary
May 25, 2026
Merged

bGOATnote merged 1 commit into
mainfrom
feat/live-judges-and-canary

Conversation

@bGOATnote

Copy link
Copy Markdown
Member

Summary

  • Two adapter classes (`AnthropicJudge`, `OpenAIJudge`) wrapping the official SDKs to the `Judge` Protocol. Both return parsed `JudgeVerdict` with `label ∈ {0, 1}` and a 1–2 sentence rationale.
  • Real canary preflight that catches 401s before a multi-hour run starts (the binding lesson from past evals). Length-only secret logging.
  • Lenient JSON extraction that tolerates strict JSON, fenced JSON, and JSON embedded in prose — so an occasional verbose judge response doesn't tank a batch.
  • CI installs `.[dev,judges]` so the import path is tested even though no live API calls run.

Why this lands now

`DualJudge` shipped last PR but had no real judges to drive — it could only be exercised with scripted in-memory stubs. Both downstream model repos (`medimage-model`, `medimage-model-research`) need live judges to do any real eval. This is the unblock.

Provider-specific gotchas baked into the implementations

  • Opus 4.7: no `temperature` / `top_p` / `top_k` / `budget_tokens` (would 400); no assistant prefills (would 400) — structured output via `output_config.format` instead.
  • GPT-5.4: `max_completion_tokens`, not the legacy `max_tokens` (per scribegoat2 incident memory).
  • Anthropic prompt caching: `cache_control: {"type": "ephemeral"}` on the system prompt. Below the 4096-token Opus 4.7 cache minimum, the marker is silently inert — costs nothing and starts paying off the moment the rubric grows.
  • OpenAI prompt caching: automatic for prefixes ≥1024 tokens, no explicit marker needed.
  • bool-as-int trap: `isinstance(True, int) is True` in Python — decision parser rejects `label: true` explicitly.

Test plan

  • 51 hermetic tests pass locally (decision parser, both judges with stub clients, canary mocking, auth-error detection across SDK shapes)
  • No live API calls in any test; both judges accept an injected `client`
  • `ruff check` + `ruff format --check` clean
  • CI: live judges only exercised via stubs; canary integration test deferred to PR Honest README, strict-by-default preflight, scrubbed internals, full Apache-2.0 #3 once an integration-test gate is in place

🤖 Generated with Claude Code

The substrate now has working judges, not just a runner contract. Both judges
implement the Judge Protocol, return JudgeVerdicts with parsed labels, and
ship with the right per-provider conventions:

AnthropicJudge (default: claude-opus-4-7)
  - No temperature/top_p/top_k (would 400 on Opus 4.7)
  - No budget_tokens (also removed on 4.7) — adaptive thinking left off
    for grading (deterministic-ish, short prompts)
  - System prompt sent with cache_control: {"type": "ephemeral"} so prompt
    caching activates once the rubric grows past 4096 tokens; below that
    the marker is silently inert (no cost, no error)
  - Structured output via output_config.format (NOT prefill — prefills
    400 on 4.7)
  - Lazy SDK import so the module imports cleanly without the optional
    [judges] extra

OpenAIJudge (default: gpt-5.4)
  - Uses max_completion_tokens, not the legacy max_tokens
    (per the scribegoat2 incident memory)
  - Strict response_format with the same JSON schema as Anthropic for
    apples-to-apples comparison

Canary preflight (judges/preflight.py)
  - Real tiny request to each provider whose key is set; fails fast on 401
  - Length-only secret logging — never echoes the value
    (per feedback_no_secret_value_dumps)
  - --strict makes missing keys an error (exit 3); default mode is warning-
    only so CI without keys still passes
  - run_preflight() accepts an injected canaries dict for hermetic tests

Decision parsing (judges/decision.py)
  - Lenient JSON extraction: strict JSON, fenced JSON, or embedded in prose
  - Validates label ∈ {0, 1}, rationale is str
  - Rejects bool labels explicitly (bool is a subclass of int in Python)

Tests: 51 hermetic tests covering all paths above. No live API calls; both
judges accept an injected client.

CI: unit job now installs .[dev,judges] so the new tests can import the
SDKs (no SDK calls in the test path).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bGOATnote
bGOATnote merged commit ca77d55 into main May 25, 2026
4 checks passed
@bGOATnote
bGOATnote deleted the feat/live-judges-and-canary branch May 25, 2026 21:30
bGOATnote added a commit that referenced this pull request May 26, 2026
PRs #1 (dual-judge core) and #2 (live Anthropic + OpenAI judges + canary)
are merged. Update the continuation contract so a future agent picks up at
the shift-gauntlet / CSGG work, not at the (already-done) scaffold.

Notes branch protection is NOT yet enabled on this repo — follow-up once
the shift gauntlet ships.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant