Vercel AI SDK
The Vercel AI SDK builds a provider object and hands models from it to
generateText, streamText, and the rest. You create that provider with
createOpenAI instead of importing the default one, giving it the gateway’s
baseURL and your Vulnetix key. Nothing downstream changes.
OPENAI_BASE_URL. The environment variables
written by vulnetix ai-firewall install shell have no effect here. Without
createOpenAI({ baseURL }) the SDK calls the provider directly and the firewall
never sees the request — no error, no warning, just unfirewalled traffic.Install
npm install ai @ai-sdk/openai
Configure
Have the CLI print the snippet with your organisation UUID already in it:
vulnetix ai-firewall snippet --lang ts --sdk vercel-ai --provider openai
Or by hand — note the custom provider, createOpenAI, rather than the default
openai export:
import { createOpenAI } from '@ai-sdk/openai'
import { generateText } from 'ai'
const org = process.env.VULNETIX_ORG_UUID
const vulnetix = createOpenAI({
baseURL: `https://guardrails.vulnetix.com/openai/${org}/v1`,
apiKey: process.env.VULNETIX_API_KEY!, // Vulnetix key, not an OpenAI key
})
const { text } = await generateText({
model: vulnetix('gpt-4o-mini'),
prompt: 'Hello',
})
console.log(text)
Define vulnetix once and export it; every generateText, streamText,
generateObject, and streamObject call in the app takes its model from that
provider and is firewalled by construction. A stray import { openai } from '@ai-sdk/openai' anywhere else in the codebase is a request that bypasses the
gateway — worth a lint rule.
For a non-OpenAI provider, keep createOpenAI (the wire format is
OpenAI-compatible), change the slug in the path, and pass that provider’s own model
string: vulnetix('llama-3.3-70b-versatile') against /groq/{orgUuid}/v1.
Streaming
Unchanged. streamText works exactly as before, including in a Next.js route
handler returning result.toDataStreamResponse().
const result = streamText({
model: vulnetix('gpt-4o-mini'),
prompt: 'Hello',
})
for await (const delta of result.textStream) {
process.stdout.write(delta)
}
Handling a guardrail block
A request your policy refuses never reaches the provider. It arrives as an HTTP
403, which the AI SDK surfaces as an APICallError carrying the status code and
the raw response body — the body is where code and blocked_by live.
import { APICallError } from 'ai'
type FirewallError = { code?: string; blocked_by?: string; message?: string }
try {
const { text } = await generateText({ model: vulnetix('gpt-4o-mini'), prompt })
} catch (err) {
if (APICallError.isInstance(err) && err.statusCode === 403) {
const body = JSON.parse(err.responseBody ?? '{}') as { error?: FirewallError }
const code = body.error?.code // request_blocked, model_denied, …
const rule = body.error?.blocked_by // the rule that stopped it
if (code === 'request_blocked') {
throw new Error(`AI firewall blocked this request: rule "${rule}"`)
}
}
throw err
}
code | Meaning |
|---|---|
request_blocked | A content guardrail matched. blocked_by names the rule; violations lists every rule that matched. |
provider_denied | Policy does not allow this provider. |
model_denied / model_not_allowed | Policy does not allow this model. |
provider_key_missing | No provider key in the vault for this provider. |
In a chat route handler, decide deliberately what the user sees. A request_blocked
is not a server fault and should not surface as a 500 — return the rule name, or a
sanitised “your organisation’s policy does not allow this”, rather than a generic
failure the user will simply retry.
pii_redact rule rewrites the
matched spans in your prompt to the literal [REDACTED] and forwards the request.
Your stream starts normally and the model answers about redacted text.Tool / function calling
Tools defined with tool() and multi-step maxSteps loops pass through untouched.
Guardrails inspect each request the SDK sends, and in a multi-step loop every
step is a fresh request that includes the previous tool results — so a tool that
returns a secret is caught on the next step, before the provider sees it.
Gotchas
- This is the SDK that most often ends up unfirewalled. It ignores
OPENAI_BASE_URLentirely, so the only thing routing traffic through the gateway iscreateOpenAI({ baseURL })in your code. Grep for@ai-sdk/openaiimports before you assume you are covered. apiKeyis your Vulnetix key, not an OpenAI key.- Using
@ai-sdk/anthropicinstead? Its provider already appends/v1internally, and the gateway’s Anthropic route ishttps://guardrails.vulnetix.com/anthropic/{orgUuid}. Whatever you configure, the request must land on…/{orgUuid}/v1/messages— check the URL before you trust it. - Edge runtimes are fine: the gateway is plain HTTPS, and streams relay as SSE.