Repository navigation
feat(evals): Record and replay eval model calls with Roach - #1972
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
dcramer
approved these changes
Nov 5, 2026
dcramer
approved these changes
Nov 5, 2026
dcramer
marked this pull request as ready for review
November 5, 2026 02:51
dcramer
added a commit
that referenced
this pull request
Nov 5, 2026
The Eval workflow on `main` reports green, but no evals have run. `eval:ci` fails at startup: ``` Failed to load custom Reporter from vitest-evals/reporter cause: Package subpath './reporters' is not defined by "exports" (ERR_PACKAGE_PATH_NOT_EXPORTED) ``` The cause is that vitest-evals 0.4.0 imports `vitest/reporters`, which vitest 5 removed. `continue-on-error` on "Run evals" hides the failure, so no `eval-results.json` gets written and the status check step throws. Example run: https://github.com/getsentry/toolkit/actions/runs/41951775819 This PR moves the eval package to vitest-evals 0.18.0, which supports vitest 5 (getsentry/vitest-evals#113, peer range `vitest >=5 <6`). The whole repo stays on vitest 5; there is no vitest 4 pin. ## Changes - **Bump `vitest-evals` to `^0.18.0`.** I added `vitest-evals@0.18.0` and its `@vitest-evals/core@0.18.0` and `@vitest-evals/report-ui@0.18.0` dependencies to `minimumReleaseAgeExclude`, because they were published today. Drop those entries once they are past the 24h window. - **Import from `vitest-evals/legacy`.** 0.18 keeps the scorer-first API (`describeEval` with `data`/`task`/`scorers`, `ToolCallScorer`, `Score`, `ScoreFn`, `BaseScorerOptions`) under `vitest-evals/legacy`. This is an import-path change only, in the 33 eval imports and the example in `docs/testing/overview.md`. The eval code is unchanged. - **Remove the unused root `vitest-evals` dev dependency.** Nothing at the root imports it. - **Set `SENTRY_CLI_NO_TELEMETRY=1` for the scorer's `sentry mcp` process.** - `ToolPredictionScorer` starts `pnpm --dir packages/cli run cli mcp` from source. On `main`, that process exits immediately with ``Package subpath './client' is not defined by "exports"``, and every scorer then fails with `MCPClientError: Connection closed`. - The exit comes from `lazyTelemetry()` in `src/lib/db/index.ts`. It loads `../telemetry.js` with `createRequire`, so its imports resolve `@sentry/api/client` under the `require` condition, but `@sentry/api` exports `./client` only for `import`. - Evals shouldn't send CLI telemetry anyway. The CLI bug itself is not fixed here: `pnpm --filter sentry run cli help` also fails from source. It needs its own CLI fix. `continue-on-error` and the eval status check are unchanged. ## Verification - **Local, no LLM key:** with no key in the sandbox, `pnpm eval:ci` on vitest 5.0.5 loads the reporter, collects and runs all 27 files and 121 evals, and writes `eval-results.json`. On `main` it stops at startup. Every eval failed, and the only cause was the missing provider key. No `Connection closed` errors. - **Checks:** `pnpm run tsc`, `pnpm run lint`, and `pnpm install --frozen-lockfile` pass. The full `pnpm run test` didn't finish inside my sandbox's time limit, so I'm relying on CI for it. ## Related - #1132 (draft, "Migrate to vitest-evals harness") moves these evals to the harness API. It touches the same eval files, so expect import conflicts there. This PR keeps the current scorer-first suite running until then. - #1972 (Roach recordings for evals) depends on this, because Roach records nothing while the suite doesn't run. Both PRs change `pnpm-lock.yaml` and `pnpm-workspace.yaml`, so #1972 needs a rebase after this merges. <!-- junior-request-attribution:start --> via @dcramer. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent%3A1de0f02c391e347294cc7ce28353bcfe --> -- [View Session](https://junior-prod.sentry.dev/conversations/agent%3A1de0f02c391e347294cc7ce28353bcfe) [[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A1de0f02c391e347294cc7ce28353bcfe/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: David Cramer <23610+dcramer@users.noreply.github.com>
The MCP evals call OpenRouter on every run. Roach, Sentry's shared recording proxy, now sits in front of those calls: it replays a call it has a recording for, and records the rest when the run has ROACH_TOKEN. The setup is config only: roach.json names the OpenRouter host and one rule for its API, vitest.config.ts adds the Roach global setup and setup file, and the Eval workflow passes ROACH_TOKEN and moves to Node 24, which Roach needs. Fork PRs get no secrets, so they only replay. The Roach setup file loads before setup-env.ts so that MSW does not catch Roach's own control calls. That needs getsentry/roach#30. Co-Authored-By: David Cramer <23610+dcramer@users.noreply.github.com>
sentry-junior
Bot
force-pushed
the
feat/roach-evals
branch
from
November 5, 2026 04:47
c44947f to
bbc5c92
Compare
0.3.0 has the client changes the evals need, so drop the git dependency on unreleased roach main.
sentry-junior
Bot
force-pushed
the
feat/roach-evals
branch
from
November 5, 2026 19:56
bbc5c92 to
b34c56c
Compare
dcramer
approved these changes
Nov 5, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The MCP evals (
packages/mcp-server-evals) call OpenRouter on every run. This sends those calls through Roach, Sentry's shared record/replay proxy. CI replays recorded calls and records only the new ones.The change is config only, in the same shape as getsentry/warden#564:
roach.jsonsets tenantgetsentry/toolkit, allowshttps://openrouter.ai, and has onemodelrule forPOST https://openrouter.ai/api/v1/. It has novaluespatterns: two CI runs sent byte-identical requests, and no miss came from a value that changes between runs. The API key travels inAuthorization, which Roach redacts on its own, so there is nosecretEnv.vitest.config.tsadds the Roach global setup and setup file. The Roach setup file goes beforesrc/setup-env.ts, so MSW, which throws on unhandled requests, does not catch Roach's own session calls.eval.ymlpassesROACH_TOKENto the eval step and moves the job to Node 24, because Roach needs 24.14+. The workflow runs onpull_request, notpull_request_target, so fork PRs get no secrets and stay replay-only. The fork policy does not change.@sentry/roach0.3.0 is in the catalog, and inminimumReleaseAgeExcludebecause it was published today. Remove that entry once 0.3.0 is older than the minimum release age. 0.3.0 includes fix(client): Keep control calls out of in-process HTTP mocks roach#30 (Roach's session calls get past MSW) and Initial work on stdio #31 (ROACH_MODE=offruns on Node 22). The lockfile change touches only Roach entries.docs/testing/overview.mdhas a short "Eval Recordings" section.Record and replay in CI
Tested on unreleased Roach
mainbefore 0.3.0 shipped (run 41970024566):All 30 live calls in attempt 2 came from the tests that failed in attempt 1. Roach drops the recordings of failed tests, so an eval that keeps failing calls the model live on every run.
The
ROACH_TOKENorg secret already reaches this workflow (***in both runs).via @dcramer.
--
View Session [Sentry]