Guardrails & approval
Built-in guardrails cover the common cases — PII, prompt injection, budget caps, schema validation — and a custom one is just a function you write.
Built-in guardrails
guardrails.ts
import {
createClient,
openai,
createPiiInputGuardrail,
createPromptInjectionGuardrail,
createBudgetGuardrail,
} from "samai-sdk";
const budget = createBudgetGuardrail({ maxCostUsd: 5.0 });
const client = createClient({
provider: openai({ apiKey: "..." }),
inputGuardrails: [
createPiiInputGuardrail({ mode: "redact" }), // scrub PII before it's sent
createPromptInjectionGuardrail({ mode: "block" }), // reject jailbreak attempts
budget.inputGuardrail, // reject once budget is spent
],
outputGuardrails: [
budget.outputGuardrail, // records cost after every call
],
});
console.log(budget.getStats()); // { totalTokens, totalCostUsd }Guardrails run on every call made through the client — nothing extra to remember to add per request. Input guardrails can reject a call before it reaches the model; output guardrails run after, and can also just observe (like budget.outputGuardrail recording cost).
Writing a custom guardrail
An InputGuardrail or OutputGuardrail is just an async function matching a small signature — no base class, no registration step:
custom-guardrail.ts
const client = createClient({
provider: openai({ apiKey: "..." }),
inputGuardrails: [
async ({ messages }) => {
const last = messages.at(-1);
const text = typeof last?.content === "string" ? last.content : "";
if (text.includes("secret-password")) {
return { allowed: false, reason: "contains sensitive term" };
}
return { allowed: true };
},
],
});Validating structured output
createSchemaGuardrail() checks the model's output against a schema — zod, or any Standard Schema V1 validator — and attaches the parsed, typed result to result.object:
schema-guardrail.ts
import { createSchemaGuardrail } from "samai-sdk";
import { z } from "zod";
const client = createClient({
provider: anthropic({ apiKey: "..." }),
outputGuardrails: [
createSchemaGuardrail(z.object({ summary: z.string(), score: z.number() })),
],
});
const result = await client.generate({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Return JSON: {summary, score} for this review: ..." }],
});
console.log(result.object); // typed, validated objectFail closed by default
A tool marked
requiresApproval is rejected if no onApprovalRequest handler is configured — see Tools & schemas for the approval workflow itself.