EvalShift
−+LLM migration testing for AI agents

LLM migration testing for AI agents. Don't ship a model upgrade on vibes.

Read the diff. Ship with proof.

Compare your current and candidate models on real production behavior. Catch regressions before they reach users. Upgrade, downgrade, or switch LLM providers without guessing whether your agent will break. EvalShift runs both models against the same golden suite of captured agent traces, 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.

The full report is a copy of the report the CLI writes to your machine — reports stay local and are never uploaded, even when you push.

● local-first

Your eval data stays on your machine unless you explicitly push a run to EvalShift Cloud.

Migration verdictmain_chat · gemini-3.7-flash → gemini-3.5-flash-lite
16 captured examples replayed on both models
FAIL5 of 7 budgets within policy
Confirms actions it never performedAsked to book coffee, it says it's on the calendar — without calling any tool.
Routes to the wrong toolA weather question gains a display_info call; the answer becomes “it's on your screen”.
Searches instead of answeringAsked for an opinion, it issues search_web and returns no text at all.
RecommendationDo not migrate. −58.8% cost does not cover 25% tool divergence.
Cost−58.8%
Latency−75.2%
Tool divergence25.0%ceiling 10%
Equivalence61.4%floor 75%

Works with Anthropic, OpenAI and Google models.

How it works

Your agent's real behavior becomes the test suite. Two models run it. You read one verdict.

step 01

Capture

One decorator. The SDK records what your agent really does — prompts, tool calls, arguments — while it runs.

from evalshift import capture

@capture.agent(suite="main_chat",
               tools=TOOLS)
def handle(message):
    ...
step 02

Compare

Replay those cases on the model you have and the model you want. Same inputs, both models, scored side by side.

sourcegemini-3.7-flash
16 captured examples, replayed on both
targetgemini-3.5-flash-lite
step 03

Decide

A verdict — pass, conditional, or fail — with the reasons, the numbers and the cost difference.

PASSCONDITIONALFAIL
5 of 7 budgets within policy · cost −58.8%

It tells you what broke, in plain English.

The same run as above. Every figure is measured; the prose is generated from the figures, never the other way round.

Score change per example

16 examples · target minus source
worst −1.00median −0.7111 of 16 examples have a lower worst-case score

Scores run 0–1, so −1.00 means the target scored a full point lower than the source on that example's worst-hit evaluator.

What changed
Confirms actions it never performedAsked to book coffee with Marcus, the target replies that it is on the calendar without calling any tool. Asked to start a groceries list, it creates the list, skips add_to_list, and still reports the three items as added.
Routes to the wrong toolA weather question gains a display_info call and the answer becomes “it's on your screen” instead of the spoken forecast. A café request drops the opening-hours search and adds an unrequested add_note.
Searches instead of answeringAsked for an opinion on working late, the target model issues a search_web call and returns no text at all, where the source model answered in two sentences.
RecommendationDo not migrate to gemini/gemini-3.5-flash-lite. A −58.8% cost saving does not cover a tool divergence of 25.0%: fix the 5 tool-selection drift and 7 semantic regression cases before re-evaluating.
Examples16
Cost$0.1799 → $0.0741−58.8%
Latency p956.0 s → 932 ms−75.2%
Regression rate13.6%max 30%
Tool divergence25.0%ceiling 10%
Equivalence61.4%floor 75%

Then read the diff, one example at a time.

Every finding points at a real conversation: what the user asked, what each model did, and why it was flagged. This is the example behind “Searches instead of answering”.

Input · turn 1cap_8f474907dc8a4925851ab27fdf45a16a · Routing — divergence

“Cool. What's your take on whether working late is worth it?”

Why flaggedsource 1.00 → target 0.00 (0–1)TOOL_SELECTION_DRIFT

The source called no tools; the target called search_web.

Source tracescore 1.000
gemini-3.7-flash(no tool calls)

final text: It's occasionally necessary during crunch times or when you're in a great flow state, but making it a habit usually costs more in focus and recovery than it delivers in output. Sustainable momentum during your core hours almost always wins out long term.

Target tracescore 0.000
gemini-3.5-flash-lite

1. search_web ({"query": "is working late worth it productivity well-being"})

final text: (none — the model returned no text)

Tool diffs

extra tool: Target added search_web at position 1.

All 16 examples in the full report ↗

Runs on your laptop. Blocks the merge in CI.

The Action runs your golden suite on every pull request, keeps one comment updated, and fails the check when the candidate regresses. Nobody has to remember to look.

pull request · swap gemini-3.7-flash for gemini-3.5-flash-lite● checks failed
  • buildSuccessful
  • EvalShift / migration2 budgets breached
evalshift[bot] commented✗ FAIL — merge blockedgemini-3.7-flash → gemini-3.5-flash-lite · 16 examples5 / 7 budgets · tool divergence 25.0% (ceiling 10%) · equivalence 61.4% (floor 75%)regression rate 13.6% · cost −58.8% · latency −75.2%view the full report ↗
- uses: babaliauskas/evalshift-action@v0
  with:
    token: ${{ secrets.EVALSHIFT_TOKEN }}
    fail-on: regression

When it's safe, it says so.

A pass is not silence. The report still lists what moved, so a non-blocking drop in similarity is a note to read before promoting, not a surprise later.

Migration verdictproject_insights
PASS6 of 6 budgets within policy
gemini-3.1-flash-lite-preview → gpt-5.4-mini · 21 examples

Safe to migrate under the configured policy. 0 blocking regressions, 0 critical regressions.

Semantic similarity fell on every prompt (d = −2.51, p < 0.0001). Non-blocking, but worth a look before promoting.

How gating decides →

Start in one command.

The CLI, the capture SDK and the HTML report are open source and free. Run locally without an account; push when you want history, trends, share links and the PR gate.

Run it on your laptop
$ uv pip install evalshift
$ evalshift init --ci

Python 3.11+ · Apache-2.0 · reports stay on your machine

Keep it after the run
  • 1 seat
  • 100 Cloud runs / month
  • 7 days of run history
  • 1 run at a time
Start free →

no card · what the paid plans add →

Read before you migrate.

Field notes from real model swaps: what breaks, how many cases you need, and when to trust the judge.