LLM migration testing for AI agents. Don't ship a model upgrade on vibes.
Read the diff. Ship with proof.
Know what breaks before you switch models. EvalShift runs your existing and candidate models against the same golden suite, compares outputs, tool calls, structured behavior, cost, and latency, then tells you whether the change is a real regression.
Push the result to EvalShift Cloud for shared history, baseline comparison, and CI gating.
@@ case 17 — "refund a duplicate charge" @@tool: lookup_order(order_id="A-339")tool: process_refund(order_id="A-339",− amount=29.99, # full charge− reason="customer_request")reply: "Refund issued for $29.99."✗ structural score 0.62 missed duplicate-detect✗ semantic score 0.71 tone: terse✗ tool-call score 0.50 skipped find_duplicate()
@@ case 17 — "refund a duplicate charge" @@tool: lookup_order(order_id="A-339")+ tool: find_duplicate(order_id="A-339") # NEWtool: process_refund(order_id="A-339",+ amount=29.99, # only the dup+ reason="duplicate_charge")reply: "Looks like you were charged twice — refund+ of $29.99 is on the way. Apologies for that."✓ structural score 0.91 +0.29✓ semantic score 0.86 +0.15✓ tool-call score 1.00 +0.50
+ Test what your agent does, not just what it says.
The SDK records model calls, tool calls, arguments, results, errors and the execution relationships between them, straight out of your own process. Representative agent behavior becomes the migration regression suite — no hand-written fixtures, no guessing at what production looks like.
$ uv add evalshift-sdkfrom evalshift import capture@capture.agent(suite="support_agent",redact=True, tools=[])def handle_ticket(query):...$ EVALSHIFT_CAPTURE=1 python -m appwrote 37 captures → .evalshift/captures/
$ evalshift run --suite golden/customer-support.jsonl◇ doctor ............................................. ok◇ estimated cost ..................................... $0.42▸ run ▰▰▰▰▰▰▰▰▰▰ 80/80 · cache 32/40▸ evaluate structural · semantic · judge · tool-call▸ analyze paired-t · cohen's d · BH-FDR✓ candidate is significantly betterΔ +0.064 · d 0.41 [0.18, 0.64] · p 0.003 · q 0.011⚠ 1 sub-metric regressed (tool-call · args; n.s. q=0.41)◇ report → ./.evalshift/reports/2026-05-09-1421.html◇ push → evalshift.dev/app/acme/support-agent/runs/r_8f2a✓ gate PR #481 check passed
evalshift[bot] commented on #482✗ candidate regressed — merge blockedclaude-sonnet-4-5 → claude-sonnet-5semantic Δ −0.031 d −0.28 [−0.49, −0.07] q 0.004tool_selection Δ −0.112 d −0.71 [−0.95, −0.47] q 0.001exact_match Δ 0.000 no change3 / 40 cases regressed · 0 improvedview the full diff ↗
+ two lines of workflow
The Action runs your golden suite on every pull request, pushes the run, keeps one comment updated, and fails the check when the candidate regresses. Nobody has to remember to look.
- uses: babaliauskas/evalshift-action@v1
with:
token: ${{ secrets.EVALSHIFT_TOKEN }}
fail-on: regressionThe same verdict the CLI printed on your laptop, enforced where it counts. Nothing is uploaded until you run evalshift push.
+ run it on your laptop
The CLI, the capture SDK and the HTML report are open source and free. Scaffold a project, run both models against your golden suite, and read the diff — without an account, and without sending us anything.
$ uv pip install evalshift$ evalshift init --ci
+ keep it after the run
Push a run and it stops being a file on one machine: history your team can open, trends across releases, share links for the people who do not run the CLI, and the PR gate. Free while it is just you.
- +1 seat
- +100 Cloud runs / month
- +7 days of run history
- +1 run at a time
no card · what the paid plans add →
