Skip to main content

Providers

Intelliwright talks to Jev through its own small client, so you set one API key and nothing else. Pick a provider with ai.provider in the config, or leave it out and Intelliwright uses the first provider whose key it finds in the environment.

Presets​

intelliwright.config.ts
export default defineConfig({
ai: { provider: "typesafe" },
});
PresetEnvironment variablesDefault modelTokens per request
typesafeTYPESAFE_API_KEYjev-latest64,000, of which the page and the longest question may use 32,000
vercelAI_GATEWAY_API_KEY, or VERCEL_OIDC_TOKENtypesafe-ai/jev32,000
openrouterOPENROUTER_API_KEY~typesafe/jev-latest32,000
cloudflareCLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_IDtypesafe/jev32,000

With no provider set, Intelliwright checks for keys in the order of this table and uses the first preset whose key is set. Keys are read from the environment, which includes .env.local and .env in the config's folder. Pass apiKey to set one directly.

Settings​

These go next to provider in ai:

SettingDefaultWhat it does
modelThe preset's modelOverrides the model, for example to pin jev-1.13.0.
apiKeyFrom the environmentOverrides the key.
baseURLThe preset's URLSends requests somewhere else, for example through a proxy.
fetchNode's fetchA custom fetch, for example one that goes through a corporate proxy.
timeout30,000Milliseconds per attempt.
maxRetries3Retries for timeouts, network errors, 408, 429 and 5xx responses.
maxConcurrency4The most requests in flight per worker.

Aliases such as jev-latest move when TypeSafe ships a new version. Reports record the version that answered, such as jev-1.13.0; pin it with model if you tune thresholds.

intelliwright.config.ts
export default defineConfig({
ai: {
provider: "typesafe",
model: "jev-1.13.0",
maxConcurrency: 2,
},
});

Another host​

Any host that implements TypeSafe's System One API works: POST {baseURL}/v1/systemone with { model, state, questions } and a Bearer key.

intelliwright.config.ts
export default defineConfig({
ai: {
provider: {
baseURL: "https://jev.internal.example.com",
apiKey: process.env.JEV_API_KEY,
model: "jev-latest",
name: "Internal Jev",
headers: { "x-team": "qa" },
},
},
});

name is shown in errors and reports, and defaults to the host name. Requests to a custom host may use up to 32,000 tokens.

Your own function​

For anything else, pass an object with an evaluate function. It receives the page state and the questions, and returns the answers:

intelliwright.config.ts
export default defineConfig({
ai: {
provider: {
name: "my-evaluator",
maxInputTokens: 32_000,
async evaluate({ state, questions }, { signal }) {
const response = await fetch("https://evaluator.example.com/answer", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ state, questions }),
signal,
});
return await response.json(); // { answers, model?, usage?, costUsd? }
},
},
},
});

Answers use Intelliwright's shapes: { type: "boolean", probability }, { type: "choice", choice, probabilities } and { type: "score", score, probabilities }. They're checked: every question needs an answer of its type, probabilities must be between 0 and 1, and a choice must be one of the options sent. model, usage (inputTokens and outputTokens) and costUsd are optional.

The AI SDK​

Projects that already use the Vercel AI SDK can pass any AI SDK evaluation model. Install ai 7.x first; Intelliwright doesn't load it unless you import the adapter.

npm install --save-dev ai
intelliwright.config.ts
import { defineConfig } from "intelliwright";
import { fromAiSdk } from "intelliwright/ai-sdk";

export default defineConfig({
ai: { provider: fromAiSdk("typesafe-ai/jev") },
});

A model id string goes through the AI SDK's default provider, which is the Vercel AI Gateway unless you set another. You can also pass an evaluation model object from an AI SDK provider package.

OptionDefaultWhat it does
nameThe model's provider and idShown in errors and reports.
maxInputTokens32,000The most input tokens one request may use.
maxRetries2Retries, made by the AI SDK.
headersExtra request headers.
providerOptionsPassed through to the AI SDK.

A model that answers a choice or a score without probabilities counts as certain of its answer.

A mock for offline runs​

createMockEvaluator(handler) from intelliwright/testing answers questions locally, with no key and no network. The handler receives each request, { state, questions }, and returns an answer for every question key. Shorthands keep handlers short: true, false or a probability for a boolean, an option key for a choice, and a level number for a score. Full answer objects work too. The mock's calls lists every request it answered, and reset() clears it.

intelliwright.config.ts
import { defineConfig } from "intelliwright";
import { createMockEvaluator } from "intelliwright/testing";

const offline = createMockEvaluator(({ questions }) =>
Object.fromEntries(
Object.entries(questions).map(([key, question]) => {
if (question.type === "boolean") return [key, key !== "loading"];
if (question.type === "choice") return [key, Object.keys(question.criteria)[0]];
return [key, question.criteria.length - 1];
}),
),
);

export default defineConfig({
ai: { provider: process.env.TYPESAFE_API_KEY ? "typesafe" : offline },
});

A mock can't tell whether the page is right. This one says yes to every claim and never sees a loading page, so positive checks pass, .not checks fail, and ai.run reports its goal as reached before doing anything. Tailor the handler to the questions your tests ask, or, often simpler, tag the tests that use Jev, such as @ai, and leave them out with --tag "not @ai" where there's no key.