@ethicalzen/sdk
Version:
Official EthicalZen SDK for Node.js - AI safety guardrails made simple
296 lines (295 loc) • 10.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EthicalZenClient = void 0;
const axios_1 = __importDefault(require("axios"));
/**
* EthicalZen Client
*
* Core client for interacting with the EthicalZen API.
* Supports two modes:
* - **Explicit** (default): Uses custom headers (X-Certificate-ID, X-Tenant-ID, X-Target-Endpoint)
* - **Transparent**: Acts as transparent proxy (certificate in path, no custom headers)
*
* @example Explicit Mode (Default)
* ```typescript
* const client = new EthicalZenClient({
* apiKey: process.env.ETHICALZEN_API_KEY,
* tenantId: 'demo',
* mode: 'explicit'
* });
*
* const result = await client.enforce({
* contractId: 'my-service/general/us/v1.0',
* payload: { output: 'AI response here' }
* });
* ```
*
* @example Transparent Mode (Zero Custom Code)
* ```typescript
* const client = new EthicalZenClient({
* apiKey: process.env.ETHICALZEN_API_KEY,
* certificateId: 'hipaa-compliant-llm-v1',
* baseURL: 'http://gateway:8080',
* mode: 'transparent'
* });
*
* // Get transparent proxy URL for OpenAI
* const proxyURL = client.getTransparentProxyURL('https://api.openai.com/v1');
*
* // Use with standard OpenAI SDK
* const openai = new OpenAI({
* apiKey: process.env.OPENAI_API_KEY,
* baseURL: proxyURL
* });
* ```
*/
class EthicalZenClient {
constructor(config) {
if (!config.apiKey) {
throw new Error('EthicalZen API key is required');
}
// Validate mode-specific requirements
this.mode = config.mode || 'explicit';
if (this.mode === 'explicit') {
if (!config.tenantId) {
throw new Error('Tenant ID is required for explicit mode');
}
this.tenantId = config.tenantId;
}
else if (this.mode === 'transparent') {
if (!config.certificateId) {
throw new Error('Certificate ID is required for transparent mode');
}
this.certificateId = config.certificateId;
}
this.apiKey = config.apiKey;
this.gatewayURL = config.baseURL || 'http://localhost:8080';
// Setup axios client based on mode
if (this.mode === 'explicit') {
this.client = axios_1.default.create({
baseURL: this.gatewayURL,
timeout: config.timeout || 30000,
headers: {
'X-API-Key': config.apiKey,
'X-Tenant-ID': config.tenantId,
'Content-Type': 'application/json',
'User-Agent': '@ethicalzen/sdk/0.1.0'
}
});
}
else {
// Transparent mode - minimal headers
this.client = axios_1.default.create({
baseURL: this.gatewayURL,
timeout: config.timeout || 30000,
headers: {
'Content-Type': 'application/json',
'User-Agent': '@ethicalzen/sdk/0.1.0'
}
});
}
}
/**
* Get transparent proxy URL for a target service
* Only works in transparent mode
*
* @param targetURL - Target service URL (e.g., https://api.openai.com/v1)
* @returns Transparent proxy URL in format: {gateway}/proxy/{cert}/{target}
*
* @example
* ```typescript
* const client = new EthicalZenClient({
* apiKey: 'sk-...',
* certificateId: 'hipaa-compliant-llm-v1',
* baseURL: 'http://gateway:8080',
* mode: 'transparent'
* });
*
* // Get proxy URL
* const proxyURL = client.getTransparentProxyURL('https://api.openai.com/v1');
* // Returns: http://gateway:8080/proxy/hipaa-compliant-llm-v1/https://api.openai.com/v1
*
* // Use with OpenAI SDK
* const openai = new OpenAI({ apiKey: '...', baseURL: proxyURL });
* ```
*/
getTransparentProxyURL(targetURL) {
if (this.mode !== 'transparent') {
throw new Error('getTransparentProxyURL() only works in transparent mode');
}
if (!this.certificateId) {
throw new Error('Certificate ID is required for transparent proxy');
}
// Build transparent proxy URL: {gateway}/proxy/{cert}/{target}
return `${this.gatewayURL}/proxy/${this.certificateId}/${targetURL}`;
}
/**
* Get current mode
* @returns Current SDK mode ('explicit' or 'transparent')
*/
getMode() {
return this.mode;
}
/**
* Get certificate ID (transparent mode only)
* @returns Certificate ID or undefined
*/
getCertificateId() {
return this.certificateId;
}
/**
* Get tenant ID (explicit mode only)
* @returns Tenant ID or undefined
*/
getTenantId() {
return this.tenantId;
}
/**
* Enforce contract guardrails on AI input/output
* Works in both explicit and transparent modes
*
* @param request - Enforcement request with contractId and payload
* @returns Enforcement result with violations (if any)
*
* @example Explicit Mode
* ```typescript
* const result = await client.enforce({
* contractId: 'chatbot/general/us/v1.0',
* payload: {
* output: 'Your AI response here'
* }
* });
* ```
*
* @example Transparent Mode
* ```typescript
* // Transparent mode typically doesn't use enforce() directly
* // Instead, use getTransparentProxyURL() with standard SDKs
* ```
*/
async enforce(request) {
try {
// Go ACVPS Gateway API: POST /api/validate
const response = await this.client.post('/api/validate', {
contract_id: request.contractId,
payload: request.payload
});
// Map Go gateway response to SDK format
const violations = (response.data.violations || []).map((v) => ({
policyId: v.feature || 'unknown',
guardrail: v.feature || 'unknown',
message: `Envelope violation: ${v.feature} = ${v.value} (allowed: ${v.min}-${v.max})`,
severity: 'high',
details: {
feature: v.feature,
value: v.value,
min: v.min,
max: v.max
}
}));
return {
success: true,
passed: response.data.valid === true,
contractId: request.contractId,
tenantId: this.tenantId || 'transparent',
violations: violations,
features: response.data.features,
extractionTimeMs: response.data.extraction_time_ms,
validationTimeMs: response.data.validation_time_ms,
detectionMethod: response.data.detection_method,
timestamp: new Date().toISOString(),
latencyMs: (response.data.extraction_time_ms || 0) + (response.data.validation_time_ms || 0)
};
}
catch (error) {
// If enforcement fails, return safe default (fail-open vs fail-closed configurable)
return {
success: false,
passed: false,
contractId: request.contractId,
tenantId: this.tenantId || 'transparent',
violations: [{
policyId: 'enforcement_error',
guardrail: 'system',
message: error.response?.data?.error || error.message || 'Enforcement failed',
severity: 'critical',
details: {
error: error.toString(),
status: error.response?.status,
data: error.response?.data
}
}],
timestamp: new Date().toISOString()
};
}
}
/**
* Register a new AI service for safety certification
*
* @param service - Service details
* @returns Registration response with evaluation ID
*
* @example
* ```typescript
* const result = await client.registerService({
* name: 'customer-support-bot',
* description: 'AI chatbot for customer support',
* domain: 'general',
* region: 'us'
* });
*
* console.log('Evaluation ID:', result.evaluationId);
* ```
*/
async registerService(service) {
try {
const response = await this.client.post('/services/register', service);
return {
success: true,
evaluationId: response.data.evaluation_id || response.data.evaluationId,
message: response.data.message || 'Service registered successfully'
};
}
catch (error) {
throw new Error(`Failed to register service: ${error.message}`);
}
}
/**
* Get evaluation results for a registered service
*
* @param evaluationId - Evaluation ID from registration
* @returns Evaluation results
*/
async getEvaluationResults(evaluationId) {
try {
const response = await this.client.get(`/api/simulation-results/${evaluationId}`);
return response.data;
}
catch (error) {
throw new Error(`Failed to get evaluation results: ${error.message}`);
}
}
/**
* Approve a certificate after reviewing evaluation results
*
* @param evaluationId - Evaluation ID to approve
* @param notes - Optional approval notes
* @returns Approval response with contract ID
*/
async approveCertificate(evaluationId, notes) {
try {
const response = await this.client.post('/api/evaluation/approve', {
evaluationId,
notes
});
return response.data;
}
catch (error) {
throw new Error(`Failed to approve certificate: ${error.message}`);
}
}
}
exports.EthicalZenClient = EthicalZenClient;