Vercel AI SDK
Protect Vercel AI SDK generations with OGuardAI so PII is tokenized before the model and restored in the output
The OGuardAI TypeScript SDK ships an optional Vercel AI SDK integration that wraps a single text
generation so PII in the prompt is tokenized before the model runs and restored in the model output
afterward. The generate call and the model only ever see the tokenized safe_text; the sealed
session never enters the AI SDK. The integration is a thin seam over the SDK guardedCall primitive.
Install
The integration has no dependency on the ai package: you pass your own generate function, so the
adapter works against any ai version and any provider. Install the SDK and the AI SDK, plus the
provider package that supplies your model (for example @ai-sdk/openai).
npm install @oguardai/sdk aiOne boundary-safe entry point
guardedGenerateText is the single entry point, and it is boundary-safe by design. The transform and the
rehydrate run outside the AI SDK: your generate function is the only code that touches the model, and
it only ever receives the tokenized prompt. No experimental_telemetry span inside the generate call,
and no provider request, observes the raw prompt or the restored output.
Unlike the LangChain integration, there is no composable in-framework variant here, so there is no tracer-exposed path to caveat. The single seam keeps the raw input and the restored output on the host side of the boundary in every case.
Signature
function guardedGenerateText(
generate: (safePrompt: string) => Promise<GenerateTextLike | string> | GenerateTextLike | string,
client: OGuardAIClient,
prompt: string,
options?: GuardedGenerateOptions,
): Promise<string>;
type GenerateTextLike = { text: string };
interface GuardedGenerateOptions {
policy?: string;
language?: string;
outputChannel?: OutputChannel;
restoreMode?: RestoreMode;
}generate receives the tokenized prompt and returns either the AI SDK result (any object with a
string text, which is what generateText returns) or a plain string. guardedGenerateText returns
the restored text as a plain string, not a result object.
| Option | Default | Description |
|---|---|---|
policy | server default | OGuardAI policy name applied to the transform. |
language | server default | Language hint for detection (for example de). |
outputChannel | user_output | Output channel whose restore rules apply on the way back. |
restoreMode | full | Restore mode for the rehydrate step (full, partial, masked, formatted, abstract, none). |
Usage
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { OGuardAIClient } from "@oguardai/sdk";
import { guardedGenerateText } from "@oguardai/sdk/integrations/vercel";
const client = new OGuardAIClient({ baseUrl: "http://localhost:3000" });
const model = openai("gpt-4o-mini");
const reply = await guardedGenerateText(
(safePrompt) => generateText({ model, prompt: safePrompt }),
client,
"Antworte Herrn Julian Schneider unter julian.schneider@firma.example",
{ policy: "german-support", language: "de", outputChannel: "customer_email" },
);The generate closure runs on Antworte Herrn {{person:p_001:...}} unter {{email:e_001:...}} only.
The returned reply has the real values restored per the customer_email channel.
What the model and tracer see
The prompt reaches the model as tokenized safe_text, never a raw value:
Antworte Herrn {{person:p_001:...}} unter {{email:e_001:...}}Because the transform and rehydrate happen outside the AI SDK, an experimental_telemetry span
opened inside your generateText call records only the tokenized prompt and the tokenized model
output. The restoration happens after guardedGenerateText receives the model result, on the host
side of the boundary, so no tracer, log, or provider request ever observes a raw or a restored name,
email, or IBAN.
Scope
The adapter protects one prompt string. For a multi-message chat, use guardedChat and
rehydrateResponse on the client directly, which share one sealed session across the conversation.
Tokens embedded in tool-call arguments or intermediate agent steps are out of scope for this adapter
and are not restored.
Fail-closed behavior
- If the transform returns no sealed session,
guardedGenerateTextthrows before the model is called, so a prompt that cannot later be restored or proven protected never reaches the provider. - If
generateresolves to neither a string nor an object with a stringtext, the adapter throws aTypeErrorrather than returning an unverified value.
How it works
- The integration calls
/v1/transformon the prompt, replacing PII with semantic tokens. - Your
generatefunction runs on the tokenizedsafe_textand returns its output. - The integration calls
/v1/rehydrateon that output using the sealed session, restoring the real values per the output channel.
If the transform fails or yields no session, the generate function is never invoked (fail closed).