Vercel AI SDK
Add Checkrd policy enforcement to the Vercel AI SDK via LanguageModelV2Middleware.
Vercel AI SDK
Checkrd ships a LanguageModelV2Middleware that hooks into every generateText, streamText, generateObject, and streamObject call. The middleware policy-evaluates the call before the model runs and emits structured telemetry on completion (including streaming token counts).
Install
npm install checkrd ai @ai-sdk/openaiQuickstart
import { initAsync, wrapAsync } from "checkrd";
import { checkrdMiddleware } from "checkrd/integrations/ai-sdk";
import { generateText, wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai";
const checkrd = await initAsync({
policy: "policy.yaml",
agentId: "research-agent",
});
const model = wrapLanguageModel({
model: openai("gpt-4o"),
middleware: checkrdMiddleware({
engine: checkrd.engine,
enforce: true,
agentId: "research-agent",
sink: checkrd.sink,
}),
});
const result = await generateText({
model,
prompt: "Tell me a joke",
});Streaming
The middleware wraps the model's stream with a TransformStream that accumulates token usage as chunks flow through. The completion telemetry event fires when the stream's flush runs:
const { textStream } = await streamText({
model,
prompt: "...",
});
for await (const chunk of textStream) {
process.stdout.write(chunk);
}
// Telemetry event with input_tokens / output_tokens emits here.What gets enforced
| AI SDK call | Synthetic URL | Operation |
|---|---|---|
generateText | ai-sdk://{provider}/{modelId} | generate |
streamText | ai-sdk://{provider}/{modelId} | stream |
generateObject | ai-sdk://{provider}/{modelId} | generate |
streamObject | ai-sdk://{provider}/{modelId} | stream |
Body fields exposed for matchers: messages, prompt, mode, temperature, topP, maxTokens.
agent: research-agent
default: allow
rules:
- name: deny-gpt4-for-cheap-tier
deny:
url: "ai-sdk://openai/gpt-4*"
headers:
x-tenant-tier: "free"
- name: limit-temperature
deny:
url: "ai-sdk://*/*"
body:
temperature: ">1.5"Edge runtimes
The Vercel AI SDK runs in Edge Runtime, Cloudflare Workers, and Deno. Use initAsync (not init) so the WASM core loads via fetch + WebAssembly.compile instead of node:fs.readFileSync. The middleware itself is runtime-agnostic.
// Cloudflare Workers entry
import { initAsync } from "checkrd";
import { checkrdMiddleware } from "checkrd/integrations/ai-sdk";
export default {
async fetch(req, env) {
const checkrd = await initAsync({
apiKey: env.CHECKRD_API_KEY,
agentId: "edge-agent",
});
// ... use middleware ...
},
};Observation mode
Set enforce: false to log denies without aborting:
checkrdMiddleware({
engine: checkrd.engine,
enforce: false, // observation mode
agentId: "research-agent",
sink: checkrd.sink,
});Caveats
- The AI SDK passes streaming chunks through
flush. Token counts are only available at end-of-stream — they're not in the gate event, only the completion event. - Only the v2 middleware shape is supported. AI SDK 4.x uses
LanguageModelV2Middleware. The legacy v1 middleware shape is not supported. - Provider name detection. The middleware reads
providerfrom the model spec. Custom providers (createOpenAICompatible) report their configured name.