@ethicalzen/sdk
Version:
Official EthicalZen SDK for Node.js - AI safety guardrails made simple
220 lines (219 loc) • 7.84 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EthicalZenProxyClient = void 0;
const axios_1 = __importDefault(require("axios"));
/**
* EthicalZen Proxy Client
*
* Initialize once with gateway URL and certificate, then make normal REST API calls.
* The SDK automatically adds certificate headers, and the gateway intercepts, validates,
* and forwards requests to their original destination.
*
* **How it works:**
* 1. Client calls normal URL: `https://api.openai.com/v1/chat/completions`
* 2. SDK adds `X-Certificate-ID` header automatically
* 3. Gateway intercepts request, validates input with guardrails
* 4. Gateway forwards to original URL if validation passes
* 5. Gateway intercepts response, validates output with guardrails
* 6. Gateway returns response to client if validation passes
*
* @example
* ```typescript
* // Initialize once with certificate
* const client = new EthicalZenProxyClient({
* gatewayURL: 'http://gateway:8080',
* certificateId: 'hipaa-compliant-llm-v1',
* apiKey: 'sk-openai-key'
* });
*
* // Make normal API calls - uses REAL URL, certificate added automatically
* const response = await client.post('https://api.openai.com/v1/chat/completions', {
* model: "gpt-4",
* messages: [{ role: "user", content: "Hello" }]
* });
*
* // Response is validated by gateway before reaching client
* console.log(response.data);
* ```
*/
class EthicalZenProxyClient {
constructor(config) {
if (!config.gatewayURL) {
throw new Error('Gateway URL is required');
}
if (!config.certificateId) {
throw new Error('Certificate ID is required');
}
this.gatewayURL = config.gatewayURL.replace(/\/$/, ''); // Remove trailing slash
this.certificateId = config.certificateId;
this.tenantId = config.tenantId || 'demo'; // Default to 'demo' tenant
// Create axios client that intercepts all requests
this.client = axios_1.default.create({
timeout: config.timeout || 30000,
headers: {
'User-Agent': '@ethicalzen/sdk/0.1.0',
...(config.apiKey && { 'X-API-Key': config.apiKey })
}
});
// Store API key for use in interceptor
const apiKey = config.apiKey;
// Add request interceptor to package certificate as header and route through gateway
this.client.interceptors.request.use((requestConfig) => {
if (!requestConfig.url)
return requestConfig;
const originalURL = requestConfig.url;
// Route through gateway's /api/proxy endpoint
requestConfig.url = `${this.gatewayURL}/api/proxy`;
// Add our headers while PRESERVING existing headers (like Authorization)
// Order matters: existing headers first, then our overrides
const newHeaders = {
// Default Content-Type (can be overridden by existing)
'Content-Type': 'application/json',
// Gateway authentication
...(apiKey && { 'X-API-Key': apiKey }),
// DC (Deterministic Contract) headers
'X-DC-Id': this.certificateId,
'X-DC-Digest': 'sha256-sdk-auto',
'X-DC-Suite': 'S1',
'X-Tenant-ID': this.tenantId,
'X-Target-Endpoint': originalURL,
};
// Merge: keep existing headers (like Authorization), add our headers
if (requestConfig.headers && typeof requestConfig.headers === 'object') {
Object.assign(requestConfig.headers, newHeaders);
}
else {
requestConfig.headers = newHeaders;
}
return requestConfig;
});
}
/**
* Make a POST request through the gateway
*
* @param url - Normal service URL (e.g., https://api.openai.com/v1/chat/completions)
* @param data - Request body
* @param config - Optional axios config
* @returns Response from target service (after gateway validation)
*
* **Note:** SDK automatically packages certificate and routes through gateway.
* You use the REAL service URL, not a proxy URL.
*
* @example
* ```typescript
* // Use REAL OpenAI URL - SDK handles gateway routing automatically
* const response = await client.post(
* 'https://api.openai.com/v1/chat/completions',
* {
* model: "gpt-4",
* messages: [{ role: "user", content: "Hello" }]
* }
* );
* ```
*/
async post(url, data, config) {
// Interceptor automatically adds certificate and routes through gateway
return this.client.post(url, data, config);
}
/**
* Make a GET request through the gateway
*
* @param url - Normal service URL
* @param config - Optional axios config
* @returns Response from target service (after gateway validation)
*
* @example
* ```typescript
* const response = await client.get('https://api.example.com/data');
* ```
*/
async get(url, config) {
return this.client.get(url, config);
}
/**
* Make a PUT request through the gateway
*
* @param url - Normal service URL
* @param data - Request body
* @param config - Optional axios config
* @returns Response from target service (after gateway validation)
*/
async put(url, data, config) {
return this.client.put(url, data, config);
}
/**
* Make a PATCH request through the gateway
*
* @param url - Normal service URL
* @param data - Request body
* @param config - Optional axios config
* @returns Response from target service (after gateway validation)
*/
async patch(url, data, config) {
return this.client.patch(url, data, config);
}
/**
* Make a DELETE request through the gateway
*
* @param url - Normal service URL
* @param config - Optional axios config
* @returns Response from target service (after gateway validation)
*/
async delete(url, config) {
return this.client.delete(url, config);
}
/**
* Make a request with custom method through the gateway
*
* @param config - Axios request config with method and url
* @returns Response from target service (after gateway validation)
*/
async request(config) {
if (!config.url) {
throw new Error('URL is required in request config');
}
return this.client.request(config);
}
/**
* Get the current gateway URL
* @returns Gateway base URL
*/
getGatewayURL() {
return this.gatewayURL;
}
/**
* Get the current certificate ID
* @returns Certificate ID
*/
getCertificateId() {
return this.certificateId;
}
/**
* Get the current tenant ID
* @returns Tenant ID
*/
getTenantId() {
return this.tenantId;
}
/**
* Get proxy URL for a specific target (for reference/debugging)
* Note: The actual routing uses /api/proxy with headers, not this URL format
*
* @param targetURL - Target service URL
* @returns Formatted proxy URL (for reference only)
*/
getProxyURL(targetURL) {
return `${this.gatewayURL}/api/proxy`;
}
/**
* Get the underlying axios instance for advanced usage
* @returns Axios instance with interceptors configured
*/
getAxiosInstance() {
return this.client;
}
}
exports.EthicalZenProxyClient = EthicalZenProxyClient;