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

sensoit

v1.2.0

Published

Official Sensoit SDK for Node.js - AI guardrails and prompt management

Readme

Sensoit Node.js SDK

Official Node.js SDK for Sensoit - AI Prompt Security & Management Platform.

Sensoit provides enterprise-grade guardrails for AI applications, including PII detection, toxicity filtering, keyword blocking, and custom policy enforcement.

Installation

npm install sensoit-sdk
# or
yarn add sensoit-sdk
# or
pnpm add sensoit-sdk

Quick Start

import { Sensoit } from 'sensoit-sdk';

const gf = new Sensoit({
  apiKey: 'gf_live_xxx',
});

const result = await gf.run('customer-support', {
  input: 'I need help with my order #12345',
  variables: { customer_name: 'John' },
});

if (result.blocked) {
  console.log('Blocked:', result.violations);
} else {
  console.log('Response:', result.output);
}

Configuration

const gf = new Sensoit({
  // Required: Your Sensoit API key
  apiKey: 'gf_live_xxx',

  // Optional: API base URL (default: https://api.sensoit.io)
  baseUrl: 'https://api.sensoit.io',

  // Optional: Request timeout in ms (default: 30000)
  timeout: 30000,

  // Optional: Max retry attempts (default: 3)
  maxRetries: 3,

  // Optional: Enable debug logging (default: false)
  debug: true,

  // Optional: Cache configuration
  cache: {
    enabled: true,       // default: true
    ttlSeconds: 60,      // default: 60
  },
});

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | required | Your Sensoit API key | | baseUrl | string | https://api.sensoit.io | API base URL | | timeout | number | 30000 | Request timeout in ms | | maxRetries | number | 3 | Max retry attempts | | debug | boolean | false | Enable debug logging | | cache.enabled | boolean | true | Enable prompt caching | | cache.ttlSeconds | number | 60 | Cache TTL in seconds |

Core Methods

run(promptName, options)

Run a prompt through Sensoit with guardrail protection.

const result = await gf.run('support-assistant', {
  // Required: User input
  input: 'I need help with my order',

  // Optional: Template variables
  variables: {
    customer_name: 'John',
    order_id: '12345',
  },

  // Optional: Session ID for conversation tracking
  sessionId: 'session_abc123',

  // Optional: Bypass cache
  forceRefresh: false,

  // Optional: Validate only (no LLM call)
  dryRun: false,

  // Optional: Override timeout for this request
  timeout: 60000,

  // Optional: Additional metadata
  metadata: {
    source: 'web-chat',
  },
});

Return Type: RunResult

interface RunResult {
  output: string;           // Generated response
  allowed: boolean;         // Request passed guardrails
  blocked: boolean;         // Request was blocked
  escalated: boolean;       // Request needs human review
  violations: GuardrailViolation[];  // Policy violations
  tokensUsed: number;       // Total tokens (in + out)
  tokensIn: number;         // Input tokens
  tokensOut: number;        // Output tokens
  latencyMs: number;        // Total latency
  costUsd: number;          // Estimated cost
  promptVersion: number;    // Prompt version used
  model: string;            // LLM model used
  provider: string;         // LLM provider
  cached: boolean;          // Served from cache
  requestId: string;        // Unique request ID
}

runBatch(inputs)

Run multiple prompts in parallel.

const batchResult = await gf.runBatch([
  {
    prompt: 'translator',
    options: { input: 'Hello', variables: { lang: 'es' } },
  },
  {
    prompt: 'translator',
    options: { input: 'Goodbye', variables: { lang: 'fr' } },
  },
]);

console.log('Summary:', batchResult.summary);
// { total: 2, allowed: 2, blocked: 0, escalated: 0, failed: 0, ... }

for (const result of batchResult.results) {
  console.log(`${result.prompt}: ${result.output}`);
}

Return Type: BatchRunResult

interface BatchRunResult {
  results: BatchRunItemResult[];
  summary: {
    total: number;
    allowed: number;
    blocked: number;
    escalated: number;
    failed: number;
    totalCostUsd: number;
    avgLatencyMs: number;
    totalTokensUsed: number;
  };
}

invalidateCache(promptName?)

Clear cached prompt data.

// Clear cache for specific prompt
gf.invalidateCache('support-assistant');

// Clear all cache
gf.invalidateCache();

Express Middleware

import express from 'express';
import { Sensoit } from 'sensoit-sdk';

const app = express();
const gf = new Sensoit({ apiKey: 'gf_live_xxx' });

app.post('/chat', gf.middleware({
  prompt: 'support-assistant',

  // Extract user input from request
  extractInput: (req) => req.body.message,

  // Extract template variables
  extractVariables: (req) => ({
    user_name: req.user?.name || 'Guest',
    user_id: req.user?.id,
  }),

  // Extract session ID
  extractSessionId: (req) => req.cookies?.sessionId,

  // Handle blocked requests
  onBlock: (req, res, violations) => {
    res.json({
      error: 'I cannot help with that request.',
      violations: violations.map(v => v.policyName),
    });
  },

  // Handle escalated requests
  onEscalate: (req, res) => {
    res.status(202).json({
      message: 'Your request has been forwarded to a human agent.',
    });
  },

  // Handle errors
  onError: (req, res, error) => {
    console.error('Sensoit error:', error);
    res.status(500).json({ error: 'Service unavailable' });
  },

  // Pass through mode (don't block, just attach result)
  passThrough: false,
}), (req, res) => {
  // Access Sensoit result
  const result = req.sensoit!;
  res.json({ reply: result.output });
});

Next.js App Router

// app/api/chat/route.ts
import { Sensoit } from 'sensoit-sdk';

const gf = new Sensoit({ apiKey: process.env.SENSOIT_API_KEY! });

export const POST = gf.nextHandler({
  prompt: 'support-assistant',

  extractInput: async (req) => {
    const body = await req.json();
    return body.message;
  },

  extractVariables: async (req) => {
    const body = await req.json();
    return { user_id: body.userId };
  },

  extractSessionId: (req) => {
    return req.headers.get('x-session-id') || undefined;
  },

  onBlock: (violations) => {
    return Response.json(
      { error: 'Request blocked', violations },
      { status: 200 }
    );
  },

  onEscalate: () => {
    return Response.json(
      { message: 'Escalated for review' },
      { status: 202 }
    );
  },

  streaming: false,
});

Fastify Plugin

import Fastify from 'fastify';
import { Sensoit } from 'sensoit-sdk';

const fastify = Fastify();
const gf = new Sensoit({ apiKey: 'gf_live_xxx' });

await fastify.register(gf.fastifyPlugin({
  prompt: 'support-assistant',

  extractInput: (request) => (request.body as any).message,

  extractVariables: (request) => ({
    user_id: (request.user as any)?.id,
  }),

  routes: ['/api/chat', '/api/support/*'],

  onBlock: (request, reply, violations) => {
    reply.code(200).send({ error: 'Blocked' });
  },
}));

fastify.post('/api/chat', async (request, reply) => {
  const result = request.sensoit!;
  return { reply: result.output };
});

Error Handling

import {
  Sensoit,
  BlockedError,
  EscalatedError,
  RateLimitError,
  AuthError,
  TimeoutError,
  ValidationError,
  isBlockedError,
  isRetryableError,
} from 'sensoit-sdk';

const gf = new Sensoit({ apiKey: 'gf_live_xxx' });

try {
  const result = await gf.run('my-prompt', { input: 'Hello' });
  console.log(result.output);
} catch (error) {
  if (error instanceof BlockedError) {
    console.log('Blocked by:', error.violations);
    console.log('Has critical:', error.hasCriticalViolation());
  } else if (error instanceof EscalatedError) {
    console.log('Escalated:', error.reason);
  } else if (error instanceof RateLimitError) {
    console.log('Rate limited, retry after:', error.retryAfterSeconds);
  } else if (error instanceof AuthError) {
    console.log('Auth error:', error.message);
  } else if (error instanceof TimeoutError) {
    console.log('Timed out after:', error.timeoutMs, 'ms');
  } else if (error instanceof ValidationError) {
    console.log('Validation errors:', error.fieldErrors);
  }
}

// Using type guards
try {
  await gf.run('my-prompt', { input: 'Hello' });
} catch (error) {
  if (isBlockedError(error)) {
    // TypeScript knows error is BlockedError
    console.log(error.violations);
  }

  if (isRetryableError(error)) {
    // Implement custom retry logic
  }
}

Error Classes

| Error | Code | Status | Description | |-------|------|--------|-------------| | BlockedError | BLOCKED | 200 | Response blocked by guardrails | | EscalatedError | ESCALATED | 200 | Response needs human review | | RateLimitError | RATE_LIMIT | 429 | Rate limit exceeded | | AuthError | AUTH_ERROR | 401 | Invalid API key | | TimeoutError | TIMEOUT | - | Request timed out | | ValidationError | VALIDATION_ERROR | 400 | Invalid input | | PromptNotFoundError | PROMPT_NOT_FOUND | 404 | Prompt doesn't exist | | NetworkError | NETWORK_ERROR | - | Network failure | | SensoitAPIError | API_ERROR | varies | General API error |

Events

const gf = new Sensoit({ apiKey: 'gf_live_xxx' });

// Fired when a request is blocked
gf.on('blocked', ({ promptName, violations, requestId }) => {
  console.log('Blocked:', promptName, violations);
});

// Fired when a request is escalated
gf.on('escalated', ({ promptName, requestId, reason }) => {
  console.log('Escalated:', promptName, reason);
});

// Fired on errors
gf.on('error', ({ promptName, error, requestId }) => {
  console.error('Error:', promptName, error.message);
});

// Fired on successful completion
gf.on('complete', ({ promptName, result }) => {
  console.log('Complete:', promptName, result.output);
});

// Fired on retry attempts
gf.on('retry', ({ promptName, attempt, maxAttempts, delayMs }) => {
  console.log(`Retry ${attempt}/${maxAttempts} after ${delayMs}ms`);
});

TypeScript Types

All types are fully exported for TypeScript usage:

import type {
  // Configuration
  SensoitConfig,
  CacheConfig,

  // Run operations
  RunOptions,
  RunResult,

  // Batch operations
  BatchRunInput,
  BatchRunResult,
  BatchRunItemResult,
  BatchRunSummary,

  // Guardrails
  GuardrailViolation,
  GuardrailViolationType,
  ViolationSeverity,
  ViolationAction,

  // Events
  SensoitEvent,
  BlockedEventPayload,
  EscalatedEventPayload,
  ErrorEventPayload,
  CompleteEventPayload,
  RetryEventPayload,

  // Middleware
  ExpressMiddlewareConfig,
  NextHandlerConfig,
  FastifyPluginConfig,
} from 'sensoit-sdk';

Caching

The SDK includes built-in caching for prompt responses:

const gf = new Sensoit({
  apiKey: 'gf_live_xxx',
  cache: {
    enabled: true,
    ttlSeconds: 60,
  },
});

// First call - makes API request
const result1 = await gf.run('prompt', { input: 'Hello' });
console.log(result1.cached); // false

// Second call - served from cache
const result2 = await gf.run('prompt', { input: 'Hello' });
console.log(result2.cached); // true

// Force fresh request
const result3 = await gf.run('prompt', { input: 'Hello', forceRefresh: true });
console.log(result3.cached); // false

// Invalidate cache
gf.invalidateCache('prompt');

Retry Logic

The SDK automatically retries failed requests with exponential backoff:

  • Retries on: 429, 500, 502, 503, 504, network errors
  • Does NOT retry on: 400, 401, 403, 404
  • Default: 3 attempts with 1s, 2s, 4s delays
const gf = new Sensoit({
  apiKey: 'gf_live_xxx',
  maxRetries: 5, // Custom retry count
});

// Listen for retry events
gf.on('retry', ({ attempt, maxAttempts, delayMs, error }) => {
  console.log(`Retrying (${attempt}/${maxAttempts}) after ${delayMs}ms: ${error.message}`);
});

Health Check

const health = await gf.health();
console.log(health);
// { status: 'ok', service: 'sensoit', timestamp: '2024-...' }

Environment Variables

SENSOIT_API_KEY=gf_live_xxx
SENSOIT_BASE_URL=https://api.sensoit.io
const gf = new Sensoit({
  apiKey: process.env.SENSOIT_API_KEY!,
  baseUrl: process.env.SENSOIT_BASE_URL,
});

Requirements

  • Node.js 18.0.0 or higher
  • TypeScript 5.0+ (optional, for TypeScript projects)

License

MIT

Support