Skip to main content
A tool that works is not the same as a tool the model decides to call. DevTools and the playground let you check that by hand; evals check it in a test. @skybridge/test runs a real conversation against your app, in process, and hands you the tool calls the model made so you can assert on them.

Set Up

Add the test runtime (published on the beta dist-tag while the API settles), vitest, the AI SDK, and the provider you want to drive the conversation:
Turn on evals in the Vite plugin. It registers the expect.chat matchers, picks up evals/**/*.eval.ts, raises the per-scenario timeout to two minutes, and loads your .env so the provider key is available:
vite.config.ts
Then add a script to run them:
package.json

Write a Scenario

A scenario opens a conversation against your app, sends a prompt, and asserts on the tools the model called:
evals/search.eval.ts
start serves the app in process: no port, no fixture. Each send is one turn, during which the model can call several tools before it answers. The session closes when the test finishes. This is why the app lives in src/server.ts and run() in src/index.ts: importing the app starts nothing.

Assert on Tool Calls

expect.chat(chat) is typed against your app, so tool names autocomplete and arguments are checked against each tool’s input schema. Every matcher supports .not. On failure, the message lists the calls the model actually made, arguments included. chat.toolCalls and chat.assistantTurns are also available for custom assertions.

Test Authenticated Tools

For an app behind OAuth, claim an identity for the session:
evals/checkout.eval.ts
Only token verification is skipped. Per-tool schemes and scope checks run for real against those claims, and extra reaches your handlers as extra.http.authInfo.extra. Omit authInfo to exercise the anonymous path, auth challenges included.
setup and oauth still resolve on the first request, so an app wired to an identity provider needs its .env to run evals, the same as dev.

Tune the Run

Defaults every scenario starts from go in the plugin option, and all but timeout can be overridden per start:
vite.config.ts
Evals are live model calls: they cost money and the wording varies from run to run. Keep the temperature at 0, assert on the calls rather than on exact phrasing, and reach for toHaveSaid with a loose pattern when the answer itself matters.

Go Further

Skybridge

The app your scenarios import

Playground

Chat with a real model running your app

Register Tools

Write descriptions the model can act on