npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

promptguard-sdk

v1.10.0

Published

Drop-in security for AI applications - AI Firewall SDK with auto-instrumentation

Readme

npm version CI License TypeScript

PromptGuard Node.js SDK

Drop-in security for AI applications. Secure any GenAI app - regardless of framework or LLM provider.

Installation

npm install promptguard-sdk

The npm package and the import specifier are both promptguard-sdk — no surprises:

import { init, PromptGuard } from 'promptguard-sdk';

Get a free API key at app.promptguard.co.

The SDK reads PROMPTGUARD_API_KEY from the environment; it does not auto-load .env. Use dotenv (call import 'dotenv/config' first) if you keep secrets in a .env file.

PromptGuard fails open by default — if the Guard API is unavailable, calls proceed unscanned so your app stays up. Set failOpen: false to block (fail closed) on a Guard outage instead.

Module format: the package currently ships CommonJS (require) builds. It works in ESM projects via Node's CJS interop (import { init } from 'promptguard-sdk' transpiles to a require), and in plain CommonJS via const { init } = require('promptguard-sdk').

Running a native-ESM app? Auto-instrumentation (init()) may not cover your LLM calls — see Limitations: ESM apps before relying on enforce mode.

Option 1: Auto-Instrumentation (Recommended)

One line secures every LLM call in your application - no matter which framework you use.

// All imports first — ES module imports are hoisted and always run before
// any other statement, regardless of their position in the file.
import { init } from 'promptguard-sdk';
import OpenAI from 'openai';

// init() runs as the first executed statement and patches the SDK prototypes.
// Patching works regardless of import order, so you don't need to worry about
// importing the LLM SDK "after" calling init().
init({ apiKey: 'pg_live_xxx' });

const client = new OpenAI();

// This call is automatically scanned by PromptGuard.
const response = await client.chat.completions.create({
  model: 'gpt-5-nano',
  messages: [{ role: 'user', content: 'Hello!' }],
});

init() is intentionally quiet on success (SDK logging defaults to warn). To confirm which provider SDKs are actually being protected, read getAppliedPatches() — the source of truth for what got patched:

import { init, getAppliedPatches } from 'promptguard-sdk';
init({ apiKey: 'pg_live_xxx' });
console.log('PromptGuard protecting:', getAppliedPatches()); // e.g. ['openai']

If this list is empty, nothing is being scanned (see Limitations: ESM apps). Set logLevel: 'info' on init() to also emit a one-line confirmation banner.

Supported SDKs

Auto-instrumentation patches the create / generateContent / chat / send methods on:

| SDK | npm Package | What Gets Patched | |-----|------------|-------------------| | OpenAI | openai | chat.completions.create, responses.create (string and message-item input forms) | | Anthropic | @anthropic-ai/sdk | messages.create | | Google Generative AI | @google/generative-ai | generateContent | | Cohere | cohere-ai | Client.chat / ClientV2.chat | | AWS Bedrock | @aws-sdk/client-bedrock-runtime | BedrockRuntimeClient.send (InvokeModel, InvokeModelWithResponseStream, Converse, ConverseStream) |

Any framework built on these SDKs is automatically covered: LangChain.js, Vercel AI SDK, AutoGen, Semantic Kernel, and more.

Patches attach to the modules resolved via CommonJS require(). If init() finds no patchable SDK it logs a warning (nothing would be scanned). You can also verify at runtime with getAppliedPatches():

import { init, getAppliedPatches } from 'promptguard-sdk';
init({ apiKey: 'pg_live_xxx' });
console.log(getAppliedPatches()); // e.g. ['openai', 'anthropic']

Native-ESM apps: see Limitations: ESM apps.

Modes

// Enforce mode (default) - blocks policy violations.
init({ apiKey: 'pg_live_xxx', mode: 'enforce' });

// Monitor mode - logs threats but never blocks. Good for shadow deployment.
init({ apiKey: 'pg_live_xxx', mode: 'monitor' });

Options

init({
  apiKey: 'pg_live_xxx',           // or set PROMPTGUARD_API_KEY env var
  baseUrl: 'https://...',     // or set PROMPTGUARD_BASE_URL env var
  mode: 'enforce',            // 'enforce' | 'monitor'
  failOpen: true,             // allow calls when Guard API is unreachable
  scanResponses: false,       // also scan LLM responses
  timeout: 10_000,            // Guard API timeout in ms
});

Shutdown

import { shutdown } from 'promptguard-sdk';

// Removes all patches and cleans up.
shutdown();

Option 2: Proxy Mode

Route LLM traffic through PromptGuard. Just swap your base URL.

import { PromptGuard } from 'promptguard-sdk';

const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });

// Use exactly like the OpenAI client.
const response = await pg.chat.completions.create({
  model: 'gpt-5-nano',
  messages: [{ role: 'user', content: 'Hello!' }],
});

Security Scanning

const result = await pg.security.scan('Ignore previous instructions...');
if (result.blocked) {
  console.log(`Threat detected: ${result.reason}`);
}

PII Redaction

const result = await pg.security.redact(
  'My email is [email protected] and SSN is 123-45-6789'
);
console.log(result.redacted);

Framework Integrations

LangChain.js

import { PromptGuardCallbackHandler } from 'promptguard-sdk/integrations/langchain';
import { ChatOpenAI } from '@langchain/openai';

const handler = new PromptGuardCallbackHandler({
  apiKey: 'pg_live_xxx',
  mode: 'enforce',
  scanResponses: true,
});

// Attach to a single model
const llm = new ChatOpenAI({
  model: 'gpt-5-nano',
  callbacks: [handler],
});

// Or use with any chain / agent
const result = await chain.invoke(
  { input: 'Hello' },
  { callbacks: [handler] },
);

The callback handler provides rich context to PromptGuard - chain names, tool calls, agent steps - for more precise threat detection.

Redact decisions block in enforce mode: LangChain callbacks observe calls but cannot rewrite the inputs of the in-flight LLM call, so a redact decision cannot be honored — in enforce mode it is escalated to a block (PromptGuardBlockedError) rather than silently sending the content the Guard API asked to redact. Use auto-instrumentation or explicit GuardClient.scan() calls if you need actual redaction.

scanResponses defaults to false (consistent with init() and the Vercel AI middleware) — pass scanResponses: true to opt in to output scanning.

Vercel AI SDK

import { openai } from '@ai-sdk/openai';
import { wrapLanguageModel, generateText } from 'ai';
import { promptGuardMiddleware } from 'promptguard-sdk/integrations/vercel-ai';

const model = wrapLanguageModel({
  model: openai('gpt-5-nano'),
  middleware: promptGuardMiddleware({
    apiKey: 'pg_live_xxx',
    mode: 'enforce',
    scanResponses: true,
  }),
});

const { text } = await generateText({
  model,
  prompt: 'Hello!',
});

Standalone Guard API

Use the Guard client directly for maximum control:

import { GuardClient } from 'promptguard-sdk';

const guard = new GuardClient({ apiKey: 'pg_live_xxx' });

// Scan before sending to LLM (options-object form, preferred)
const decision = await guard.scan(
  [{ role: 'user', content: userInput }],
  { direction: 'input', model: 'gpt-5-nano' },
);

if (decision.blocked) {
  console.log(`Blocked: ${decision.threatType}`);
} else if (decision.redacted && decision.redactedMessages) {
  // Use redacted messages instead
  messages = decision.redactedMessages;
}

// Scan LLM response
const outputDecision = await guard.scan(
  [{ role: 'assistant', content: llmOutput }],
  { direction: 'output' },
);

// The positional form still works for back-compat:
// await guard.scan(messages, 'input', 'gpt-5-nano');

Retry Logic

Both the proxy client (PromptGuard) and the Guard client (GuardClient) support configurable retry behavior for transient failures:

// Proxy client
const pg = new PromptGuard({
  apiKey: 'pg_live_xxx',
  maxRetries: 3,      // Number of retry attempts (default: 3)
  retryDelay: 500,     // Base delay in ms between retries (default: 1000)
});

// Guard client (standalone scanning)
const guard = new GuardClient({
  apiKey: 'pg_live_xxx',
  maxRetries: 3,      // default: 3
  retryDelay: 500,     // default: 1000
});

Because auto-instrumentation (init()) and the framework integrations all scan through GuardClient, they retry transient Guard API failures too — pass maxRetries / retryDelay to init(), PromptGuardCallbackHandler, or promptGuardMiddleware:

init({ apiKey: 'pg_live_xxx', maxRetries: 3, retryDelay: 500 });

Retries use exponential backoff starting from retryDelay, with jitter so concurrent clients don't retry in lockstep. A server-provided Retry-After header is honored but clamped to 60 seconds. Only transient errors (network timeouts, 429/5xx responses) are retried; client errors (4xx) fail immediately.

Enforcement is unchanged by retries. Retrying only affects transient Guard failures (network errors, 429/5xx). A real block / redact decision is terminal and is never retried, and once retries are exhausted a GuardApiError is raised so your existing failOpen policy governs — exactly as it would with maxRetries: 0. Retries never turn a would-be error into an allow.

Idempotency caveat: all requests — including POSTs — are retried on transient failure. If a request reached the server but the response was lost, the retry re-submits it. Chat/completion/scan calls are safe to re-submit, but each attempt may bill separately; set maxRetries: 0 if you need strict at-most-once semantics.

Embeddings

Scan and secure embedding requests through the proxy:

const response = await pg.embeddings.create({
  model: 'text-embedding-3-small',
  input: 'The quick brown fox jumps over the lazy dog',
});
console.log(response.data[0].embedding.slice(0, 5));

Batch embedding requests are also supported:

const response = await pg.embeddings.create({
  model: 'text-embedding-3-small',
  input: ['First document', 'Second document', 'Third document'],
});
for (const item of response.data) {
  console.log(`Index ${item.index}: ${item.embedding.length} dimensions`);
}

AI Agent Security

const validation = await pg.agent.validateTool(
  'agent-123',
  'execute_shell',
  { command: 'ls -la' },
);

if (!validation.allowed) {
  console.log(`Blocked: ${validation.reason}`);
}

Red Team Testing

const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });

// Run the autonomous red team agent (LLM-powered mutation)
const report = await pg.redteam.runAutonomous({
  budget: 200,
  targetPreset: 'support_bot:strict', // snake_case `target_preset` also accepted
});
console.log(`Grade: ${report.grade}, Bypass rate: ${(report.bypassRate * 100).toFixed(0)}%`);

// Get Attack Intelligence stats
const stats = await pg.redteam.intelligenceStats();
console.log(`Total patterns: ${stats.totalPatterns}`);

Configuration

| Option | Environment Variable | Default | Description | |--------|---------------------|---------|-------------| | apiKey | PROMPTGUARD_API_KEY | - | PromptGuard API key (required) | | baseUrl | PROMPTGUARD_BASE_URL | https://api.promptguard.co/api/v1 | API base URL | | mode | - | "enforce" | "enforce" or "monitor" | | failOpen | - | true | Allow calls when Guard API is unreachable | | scanResponses | - | false | Also scan LLM responses | | timeout | - | 10000 | HTTP timeout in milliseconds | | logLevel | - | "warn" | SDK log verbosity: "debug", "info", "warn", "error", "silent" | | silent | - | false | Shorthand for logLevel: "silent" |

The proxy client (PromptGuard) talks to the /api/v1/proxy endpoints. If you set baseUrl / PROMPTGUARD_BASE_URL to .../api/v1 (without /proxy), the SDK appends the /proxy suffix for you, so requests still land on the proxy.

Security: the SDK sends your API key (and, in proxy mode, your prompt content) to whatever PROMPTGUARD_BASE_URL points at. Self-hosting is supported, so only point it at a host you trust.

Logging is process-global: logLevel / silent set a single shared log level for the whole SDK. If several integrations or init() calls pass different values, the most recently constructed one wins. Use setLogLevel() directly for fine-grained control.

Limitations

ESM apps (auto-instrumentation)

Auto-instrumentation (init()) patches the provider modules that Node resolves via CommonJS require(). If your application runs as native ESM ("type": "module" in package.json, or .mjs files) and a provider ships separate ESM builds, the module instances your code imports can be different objects from the ones the SDK patched — the dual-package hazard. In that case your LLM calls bypass the patches entirely and enforce mode silently protects nothing.

What to do:

  • Verify at runtime with getAppliedPatches() after init() — and note that a patch being listed proves the CJS build was patched, not that your ESM imports go through it. init() also logs a warning when it applies zero patches.
  • Prefer the ESM-safe APIs, which don't rely on module patching:
    • LangChain: PromptGuardCallbackHandler (promptguard-sdk/integrations/langchain)
    • Vercel AI SDK: promptGuardMiddleware (promptguard-sdk/integrations/vercel-ai)
    • Any framework: explicit GuardClient.scan() calls around your LLM invocations
  • Transpiled-to-CJS TypeScript apps (the common tsc/ts-node default) are not affected — their imports compile to require() and hit the patched modules.

Other limitations

  • Streaming responses are not output-scanned. With auto-instrumentation and scanResponses: true, streaming calls (stream: true, Bedrock ConverseStreamCommand, etc.) skip the output scan — the stream is consumed incrementally by your code and cannot be buffered without breaking stream semantics. Input scanning still applies. A debug-level log is emitted when the output scan is skipped. The same applies to the Vercel AI SDK middleware: streamText() outputs are not scanned (wrapStream logs the skip); generateText() outputs are.
  • OpenAI APIPromise helpers are not preserved by auto-instrumentation. Patched methods return a plain Promise, so .withResponse() / .asResponse() on client.chat.completions.create(...) are unavailable while init() is active. await the call and use the plain result instead.
  • Proxy client streaming: pg.chat.completions.create({ stream: true }) is rejected with a clear error — streaming is not yet supported by the proxy client.

Error Handling

import { PromptGuardBlockedError, GuardApiError } from 'promptguard-sdk';

try {
  await client.chat.completions.create({ ... });
} catch (error) {
  if (error instanceof PromptGuardBlockedError) {
    // Request was blocked by policy
    console.log(error.decision.threatType);
    console.log(error.decision.confidence);
    console.log(error.decision.eventId);
  } else if (error instanceof GuardApiError) {
    // Guard API is unreachable (only when failOpen=false)
    console.log(error.statusCode);
  }
}

TypeScript Support

Full TypeScript support with type definitions for all exports:

import type {
  GuardDecision,
  GuardMessage,
  GuardContext,
  InitOptions,
  ChatCompletionRequest,
  ChatCompletionResponse,
  SecurityScanResult,
  AutonomousRedTeamRequest,
  AutonomousRedTeamReport,
  IntelligenceStats,
} from 'promptguard-sdk';

Response fields are camelCase — with one deliberate exception. The SDK's normalized response objects (the security, redteam, agent, scrape, and guard namespaces) use camelCase field names (e.g. report.bypassRate, stats.totalPatterns, validation.riskScore, result.threatsDetected) regardless of the snake_case wire format — consistent with GuardDecision and SecurityScanResult. The OpenAI-compatible responses (chat.completions, completions, embeddings, i.e. ChatCompletionResponse) are the exception: they intentionally preserve OpenAI's snake_case shape (choices[].finish_reason, usage.prompt_tokens, …) so they stay drop-in compatible with the openai client. Request options are camelCase throughout (maxTokens, targetPreset), including GuardContext (chainName, agentId, sessionId, toolCalls).

Links

License

MIT