sensoit
v1.2.0
Published
Official Sensoit SDK for Node.js - AI guardrails and prompt management
Maintainers
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-sdkQuick 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.ioconst 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
- Documentation: https://docs.sensoit.io
- Issues: https://github.com/sensoit/sensoit-sdk/issues
- Email: [email protected]
