Vercel AI SDK
Wrap any AI SDK language model so prompts are anonymized and responses — including tool calls and streams — are rehydrated.
local-pii/ai-sdk is a Vercel AI SDK middleware. It
anonymizes every prompt (text, history, tool-call arguments, tool results) on
the way to the provider and rehydrates everything on the way back — generated
text, tool-call arguments (so the agent loop runs your tools with real
values), and streamed deltas across chunk boundaries.
bun add ai local-piiUsage
import { openai } from "@ai-sdk/openai"
import { streamText, generateText } from "ai"
import { withPii } from "local-pii/ai-sdk"
const model = withPii(openai("gpt-5.2"))
const { text } = await generateText({
model,
prompt: "Draft a reply to ana@acme.com",
})
// the provider saw [EMAIL_1]; `text` has the address backwithPii(model, opts?) is sugar over
wrapLanguageModel({ model, middleware: piiMiddleware(opts) }).
Tools
No tool wrapper is needed — the middleware rehydrates tool-call arguments before the SDK executes your tool, and re-anonymizes the tool result on the next step.
import { streamText, tool, stepCountIs } from "ai"
import { z } from "zod"
import { withPii } from "local-pii/ai-sdk"
const result = streamText({
model: withPii(openai("gpt-5.2")),
stopWhen: stepCountIs(5),
tools: {
lookup: tool({
description: "Look up a contact",
inputSchema: z.object({ email: z.string() }),
execute: async ({ email }) => findContact(email), // real email here
}),
},
prompt: "look up ana@acme.com and summarize",
})Sessions
Create one session per conversation (per request on a server):
const session = anonymizer.createSession()
const model = withPii(baseModel, { session })Use the opaque token() strategy when tools are enabled.
See Tool Calls for the full round-trip and the third-party
caveat.