Guías de integración
Patrón general: check input → llama LLM → validate output → responde.
OpenAI
import OpenAI from 'openai';
import { PromptShield } from '@aisociety/promptshield';
const ps = new PromptShield({ apiKey: process.env.PROMPTSHIELD_API_KEY! });
const openai = new OpenAI();
export async function chat(userMessage: string, sessionId: string) {
const check = await ps.check({ message: userMessage, sessionId });
if (!check.safe) return { blocked: true, reason: check.reason };
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: userMessage }],
});
const reply = completion.choices[0]?.message?.content ?? '';
const out = await ps.validateOutput({ response: reply, sessionId });
if (!out.safe) return { blocked: true, reason: out.reason };
return { reply };
}Anthropic
import Anthropic from '@anthropic-ai/sdk';
import { PromptShield } from '@aisociety/promptshield';
const ps = new PromptShield({ apiKey: process.env.PROMPTSHIELD_API_KEY! });
const anthropic = new Anthropic();
export async function ask(userMessage: string) {
const check = await ps.check({ message: userMessage });
if (!check.safe) throw new Error(`blocked: ${check.reason}`);
const msg = await anthropic.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: userMessage }],
});
const reply = msg.content
.filter((b) => b.type === 'text')
.map((b) => (b as { type: 'text'; text: string }).text)
.join('');
const out = await ps.validateOutput({ response: reply });
if (!out.safe) throw new Error(`output blocked: ${out.reason}`);
return reply;
}Vercel AI SDK
Inserta el check antes de streamText y valida la respuesta al final del stream.
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { PromptShield } from '@aisociety/promptshield';
const ps = new PromptShield({ apiKey: process.env.PROMPTSHIELD_API_KEY! });
export async function POST(req: Request) {
const { messages } = await req.json();
const last = messages[messages.length - 1];
const userMessage = typeof last.content === 'string' ? last.content : '';
const check = await ps.check({ message: userMessage });
if (!check.safe) {
return new Response(JSON.stringify({ error: 'blocked', reason: check.reason }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
const result = streamText({
model: openai('gpt-4o-mini'),
messages,
onFinish: async ({ text }) => {
const out = await ps.validateOutput({ response: text });
if (!out.safe) {
// log + alert your team — the response already streamed.
console.warn('output flagged', out.reason);
}
},
});
return result.toDataStreamResponse();
}Subida de documentos
import { PromptShield } from '@aisociety/promptshield';
import { readFileSync } from 'node:fs';
const ps = new PromptShield({ apiKey: process.env.PROMPTSHIELD_API_KEY! });
const scan = await ps.scanDocument({
file: readFileSync('./contrato.pdf'),
filename: 'contrato.pdf',
mimetype: 'application/pdf',
});
if (scan.status === 'completed') {
if (!scan.safe) throw new Error(`unsafe: ${scan.reason}`);
} else {
// Async (>2 MB) — poll
let s = await ps.getScanStatus(scan.jobId);
while (s.status === 'queued' || s.status === 'processing') {
await new Promise((r) => setTimeout(r, 1000));
s = await ps.getScanStatus(scan.jobId);
}
if (s.status !== 'completed' || !s.result?.safe) {
throw new Error('unsafe document');
}
}