Repository navigation
fix(core): pass only MCP arguments as Codex tool-call input - #366
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Measured: on main's own configuration, this fix cuts Codex judge spend per full benchmark pass by 85–88%, with 0 fix-related verdict regressions.
The drop comes from judges that read tool-call inputs: median input per call went from 48,919 → 2,453 tokens (luna) and 31,822 → 2,419 (sol). Judges that don't read tool inputs stayed flat. After the fix, 0 of 381 Codex MCP calls carry the old whole-item input; before, all 358 did. An offline replay of the before transcripts through the fixed parser agrees with the live run to within 0.2 points. Verdicts: 19 checks flipped, none attributable to the fix. 16 were deterministic checks, 2 were on a judge that never sees tool inputs, and 1 was a FAIL → PASS on luna investigate-auth-001, where a deterministic check flipped the same way. Caveats:
Base cc479f2 (origin/main) + local usage logging; before = base, after = base + this PR's two commits; judge gpt-6-sol; Codex CLI 0.154.0; experiments codex-gpt-6-luna / codex-gpt-6-sol; 14 evals (every judged eval included); n=1 per eval, side and experiment; prices from the OpenAI pricing page, 2026-10-05. |
mattrossman
left a comment
There was a problem hiding this comment.
Thanks for finding and fixing! Judge input looks better in my local testing.
### Motivation We want to understand how our scorers are judging eval outputs. Today we capture the final output of our judge, but not the input / usage details. We don't know how much $ we spend on judge inference or where inefficiencies are in our judge context (e.g. #366) ### Changes Scorers now ask for verdicts through `ctx.judge()` instead of plain `judge()`, which records each judge call's model, full prompt, output, tokens and timing in `result.json`. The uploader logs each call as an LLM span under the score span: ``` passed (score, purpose: scorer) ├─ gpt-6-sol (llm, purpose: scorer) └─ gpt-6-sol (llm, purpose: scorer) ``` > [!NOTE] > 23 of the changed eval files are just swapping `judge()` for `ctx.judge()` `purpose: "scorer"` is how Braintrust [keeps judge cost out of preset cost charts](https://braintrust.dev/docs/kb/total-llm-cost-preset-requirements#what-is-happening). Core no longer exports `judge()` so folks are encouraged to use `ctx.judge` as the preferred way to access the judge in scorers similar to `ctx.query`. As an incidental fix, the root eval span no longer logs agent tokens, since its LLM spans carry them. Braintrust [sums metrics across every span in a trace](https://braintrust.dev/docs/reference/sql/query-structure#summary), so with the initial approach in #351 the experiment table double counts tokens ([example](https://www.braintrust.dev/app/supabase.io/p/Evals/experiments/claude-code-sonnet-5%400a50211-20261002T1454Z)). Note `aiSdkAgent` has no per-request usage so its rows now show no tokens, but it's unused and slated for removal anyway. ### Verification See Braintrust [experiment](https://www.braintrust.dev/app/supabase.io/p/Evals/experiments/codex-gpt-6-luna%408c23101-20261006T1429Z) of the regression suite and this specific sample [trace](https://www.braintrust.dev/app/supabase.io/p/Evals/experiments/codex-gpt-6-luna%408c23101-20261006T1429Z?r=6450ec155246034da058a5e621834383) with LLM judge calls now visible under the scorer span. | Timeline | Messages | Span | |--------|--------|-----| | <img width="2056" height="1704" alt="CleanShot 2026-10-06 at 10 33 09 AM@2x" src="https://github.com/user-attachments/assets/e1f7a100-45ca-4716-8427-3427a133af86" /> | <img width="1196" height="1668" alt="CleanShot 2026-10-06 at 10 33 33 AM@2x" src="https://github.com/user-attachments/assets/e5d711db-d7da-4132-bf51-b982ba160067" /> | <img width="874" height="320" alt="CleanShot 2026-10-06 at 10 33 46 AM@2x" src="https://github.com/user-attachments/assets/fbe862b2-186c-4ce5-b0f3-8f678750f6b4" /> > [!TIP] > Braintrust hides scorer spans by default in the Timeline view, to see them enable "Include score spans in timeline" from the overflow menu: > <img width="200" alt="CleanShot 2026-10-06 at 9 53 33 AM@2x" src="https://github.com/user-attachments/assets/c21fd180-8df1-4da8-ba99-b29e820efcfb" /> ### Future improvements I'd like to more formally pair checks with `ctx.judge()` calls so we can attach nice labels to these spans, e.g. `judge: wrote safe RLS policy`. Today, we don't have that structural pairing so I've left it out of this PR. Closes AI-1271 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Motivation We have unit / e2e tests across `apps/*` and `packages/*` but don't run them or type checks in CI, so they quietly regress (e.g. #351 broke a Codex parser test that #366 added the same day). ## Changes - Code Quality gets a `typecheck` job next to `format` - A new Tests workflow runs `test`, `test-platform-lite` (which is slow enough for its own parallel job), and `test-docker`. The Docker tests take ~2.5 min, so they only run when a PR touches relevant files ([skipped example](https://github.com/supabase/evals/actions/runs/37680288402/job/112994246780)). - Refresh results commits drop `[skip ci]` (added in #332) and push with our GH App token instead, so required checks run on them without approval. A `run-evals-changed` refresh's [results commit](b57f997) ran CI as the app with no approval prompt and didn't start another refresh. - Upgraded to Vitest v5 so we get per-package labels on the job summary (compare [before](https://github.com/supabase/evals/actions/runs/37533147821) and [after](https://github.com/supabase/evals/actions/runs/37630783292)). | [Tests](https://github.com/supabase/evals/actions/runs/37679563185) | [Code Quality](https://github.com/supabase/evals/actions/runs/37679562944) | | --- | --- | | <img alt="CleanShot 2026-10-07 at 4 16 00 PM@2x" src="https://github.com/user-attachments/assets/8593f06b-d45a-4576-b346-163caec4689f" /> | <img alt="CleanShot 2026-10-07 at 3 37 37 PM@2x" src="https://github.com/user-attachments/assets/5600cda5-208d-432c-8b18-534c6f06f7f5" /> | Example with tests failing ([run](https://github.com/supabase/evals/actions/runs/37666155427)): <img alt="CleanShot 2026-10-07 at 2 24 36 PM@2x" src="https://github.com/user-attachments/assets/47b7d635-616c-47bc-806a-428fe851c78d" /> I added `OPENAI_API_KEY` to [Dependabot secrets](https://github.com/supabase/evals/settings/secrets/dependabot) so Tests runs on Dependabot PRs. Fork PRs don't get secrets as is the case on [supabase/mcp](https://github.com/supabase/mcp). ## Follow-ups - After merge, require `format`, `typecheck`, `test`, `test-platform-lite`, and `test-docker` on the [`main: review required`](https://github.com/supabase/evals/settings/rules/18775100) ruleset. - AI-934: replace the framework [smoke tests](https://github.com/supabase/evals/blob/5f8de50a5e8c8ef40d983d9aff9030dd25080a89/apps/framework/scripts/smoke-framework.ts) with per-scorer Vitest files. - AI-1295: cut platform-lite setup time with PGlite snapshots. Closes AI-1121 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The Codex parser put the whole MCP item, tool result included, into
tool_call.input. Judges that useserializeTranscript(ctx.transcript, { includeToolCallInputs: true })therefore got full MCP outputs (e.g.search_docsresults up to ~150k chars) as input, for Codex runs only. Input is now just the call arguments, matching the Claude Code, Grok and OpenCode parsers. The result stays on the tool result.Changes
packages/core/src/agents/codex/parser.ts: input isitem.argumentswhen it is an object, else{}. Result isitem.result ?? item.output ?? item.error.message. The error fallback matters because when Codex fails a call itself it sendsresult: nullwitherror: {message}, and that message used to reach judges only through the whole-item input. Normalized args (path/command/url) stay{}for MCP calls, so this only changes the input/result split.packages/core/src/docs-results.ts: drop thebody.arguments.graphql_queryfallback, which only existed for the old shape.Effects
resolve-performance-001-slow-query-cpu-spikeexecutedSqlnow sees Codexexecute_sqlqueries.results/keep the old shape. Nothing re-derives from them.The before/after judge-vs-agent spend measurement shows an 85–88% cut in Codex judge spend per full pass; see the measurement comment.
Testing
pnpm --filter @supabase-evals/core test: 170/170 passpnpm typecheck: passpnpm format:check: passpnpm checknot run as a gate: its framework smoke test is already red onmain.Linear: AI-1269