@guardaian/sdk
Version:
Zero-friction AI governance and monitoring SDK for Node.js applications
407 lines (346 loc) • 13.1 kB
JavaScript
/**
* OpenAI API Interceptor - Bulletproof Production Version
*
* CRITICAL GUARANTEES:
* 1. Customer's OpenAI calls ALWAYS work, even if GuardAIan is completely down
* 2. Zero performance impact on customer's AI operations
* 3. All tracking errors are isolated and logged separately
* 4. Transparent operation - customer never knows we're there
*/
function patchOpenAI(OpenAI, guardaianClient) {
// Early validation with error protection
if (!OpenAI || typeof OpenAI !== 'function') {
safeLog(guardaianClient, '⚠️ Invalid OpenAI module - skipping patch (non-blocking)');
return;
}
// Prevent double-patching
if (OpenAI.__guardaianPatched) {
safeLog(guardaianClient, '⚠️ OpenAI already patched - skipping');
return;
}
try {
safeLog(guardaianClient, '🔧 Patching OpenAI SDK with bulletproof protection...');
// Patch chat completions with complete error isolation
patchChatCompletions(OpenAI, guardaianClient);
// Patch embeddings
patchEmbeddings(OpenAI, guardaianClient);
// Patch images (DALL-E)
patchImages(OpenAI, guardaianClient);
// Mark as patched
OpenAI.__guardaianPatched = true;
safeLog(guardaianClient, '✅ OpenAI SDK patched successfully with failsafe protection');
} catch (patchError) {
// Even patching errors should not break customer code
safeLog(guardaianClient, `⚠️ OpenAI patching failed (non-blocking): ${patchError.message}`);
}
}
/**
* Patch OpenAI Chat Completions with bulletproof error handling
*/
function patchChatCompletions(OpenAI, guardaianClient) {
try {
const originalCreate = OpenAI.prototype.chat?.completions?.create;
if (!originalCreate) {
safeLog(guardaianClient, '⚠️ OpenAI chat.completions.create not found - skipping patch');
return;
}
OpenAI.prototype.chat.completions.create = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Always call original OpenAI API first
// Customer's AI call must NEVER depend on GuardAIan
let response, originalError;
try {
// Execute customer's actual OpenAI API call
response = await originalCreate.call(this, params, options);
} catch (error) {
// Store the original error to re-throw later
originalError = error;
}
// CRITICAL: Track usage in completely isolated context
// This happens asynchronously and never affects the customer's response
setImmediate(() => {
try {
trackOpenAIUsage(params, response, originalError, startTime, guardaianClient, 'chat.completions.create');
} catch (trackingError) {
// Tracking errors are completely isolated from customer code
safeLog(guardaianClient, `❌ Chat completion tracking error (isolated): ${trackingError.message}`);
}
});
// CRITICAL: Always return original result or throw original error
// Customer gets exactly what they would get without GuardAIan
if (originalError) {
throw originalError;
}
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch chat completions: ${patchError.message}`);
}
}
/**
* Patch OpenAI Embeddings
*/
function patchEmbeddings(OpenAI, guardaianClient) {
try {
const originalCreate = OpenAI.prototype.embeddings?.create;
if (!originalCreate) {
safeLog(guardaianClient, '⚠️ OpenAI embeddings.create not found - skipping patch');
return;
}
OpenAI.prototype.embeddings.create = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Customer's API call always happens first
let response, originalError;
try {
response = await originalCreate.call(this, params, options);
} catch (error) {
originalError = error;
}
// Isolated tracking
setImmediate(() => {
try {
trackOpenAIUsage(params, response, originalError, startTime, guardaianClient, 'embeddings.create');
} catch (trackingError) {
safeLog(guardaianClient, `❌ Embeddings tracking error (isolated): ${trackingError.message}`);
}
});
// Return original result
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch embeddings: ${patchError.message}`);
}
}
/**
* Patch OpenAI Images (DALL-E)
*/
function patchImages(OpenAI, guardaianClient) {
try {
const originalGenerate = OpenAI.prototype.images?.generate;
if (!originalGenerate) {
safeLog(guardaianClient, '⚠️ OpenAI images.generate not found - skipping patch');
return;
}
OpenAI.prototype.images.generate = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Customer's API call always happens first
let response, originalError;
try {
response = await originalGenerate.call(this, params, options);
} catch (error) {
originalError = error;
}
// Isolated tracking
setImmediate(() => {
try {
trackOpenAIUsage(params, response, originalError, startTime, guardaianClient, 'images.generate');
} catch (trackingError) {
safeLog(guardaianClient, `❌ Images tracking error (isolated): ${trackingError.message}`);
}
});
// Return original result
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch images: ${patchError.message}`);
}
}
/**
* Track OpenAI usage with complete error isolation
*/
async function trackOpenAIUsage(params, response, error, startTime, guardaianClient, operation) {
try {
const endTime = Date.now();
const duration = endTime - startTime;
if (response) {
// Successful request tracking
const usage = response.usage || {};
const cost = calculateOpenAICost(params.model, usage, operation);
// Fire-and-forget tracking call
guardaianClient.track({
service: 'openai',
model: params.model || 'unknown',
operation: operation,
inputTokens: usage.prompt_tokens || 0,
outputTokens: usage.completion_tokens || 0,
totalTokens: usage.total_tokens || 0,
cost: cost,
duration: duration,
requestData: sanitizeRequestData(params),
responseData: sanitizeResponseData(response),
metadata: {
success: true,
hasUsage: !!response.usage
}
}).catch(trackingError => {
// Even the track() call errors are isolated
safeLog(guardaianClient, `❌ Track call failed (isolated): ${trackingError.message}`);
});
safeLog(guardaianClient, `✅ Tracked OpenAI ${operation}: ${params.model} (${usage.total_tokens || 0} tokens, $${cost.toFixed(6)})`);
} else if (error) {
// Failed request tracking (for monitoring)
guardaianClient.track({
service: 'openai',
model: params.model || 'unknown',
operation: operation,
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
cost: 0,
duration: duration,
requestData: sanitizeRequestData(params),
responseData: {
error: true,
status: error.status || 'unknown',
type: error.type || 'api_error',
message: error.message?.substring(0, 200) || 'Unknown error'
},
metadata: {
success: false,
errorType: error.constructor.name
}
}).catch(trackingError => {
// Tracking errors for failed requests are also isolated
safeLog(guardaianClient, `❌ Error tracking failed (isolated): ${trackingError.message}`);
});
safeLog(guardaianClient, `📊 Tracked OpenAI error: ${params.model} - ${error.message}`);
}
} catch (trackingError) {
// Ultimate safety net - even this function can fail safely
safeLog(guardaianClient, `❌ Usage tracking completely failed (isolated): ${trackingError.message}`);
}
}
/**
* Calculate OpenAI costs with error protection
*/
function calculateOpenAICost(model, usage, operation) {
try {
// Latest OpenAI pricing (as of 2024)
const pricing = {
'gpt-4': { input: 0.03 / 1000, output: 0.06 / 1000 },
'gpt-4-turbo': { input: 0.01 / 1000, output: 0.03 / 1000 },
'gpt-4-turbo-preview': { input: 0.01 / 1000, output: 0.03 / 1000 },
'gpt-4o': { input: 0.005 / 1000, output: 0.015 / 1000 },
'gpt-4o-mini': { input: 0.00015 / 1000, output: 0.0006 / 1000 },
'gpt-3.5-turbo': { input: 0.0015 / 1000, output: 0.002 / 1000 },
'gpt-3.5-turbo-instruct': { input: 0.0015 / 1000, output: 0.002 / 1000 },
// Embeddings (input only)
'text-embedding-ada-002': { input: 0.0001 / 1000, output: 0 },
'text-embedding-3-small': { input: 0.00002 / 1000, output: 0 },
'text-embedding-3-large': { input: 0.00013 / 1000, output: 0 },
// Images (per image)
'dall-e-3': { input: 0.04, output: 0 }, // $0.04 per image (1024x1024)
'dall-e-2': { input: 0.02, output: 0 } // $0.02 per image (1024x1024)
};
// Handle image operations
if (operation === 'images.generate') {
const basePrice = pricing[model]?.input || pricing['dall-e-2'].input;
const imageCount = usage.n || 1;
return basePrice * imageCount;
}
// Handle text operations
const modelPricing = pricing[model] || pricing['gpt-3.5-turbo']; // Default fallback
const inputCost = (usage.prompt_tokens || 0) * modelPricing.input;
const outputCost = (usage.completion_tokens || 0) * modelPricing.output;
return Math.round((inputCost + outputCost) * 10000) / 10000; // Round to 4 decimal places
} catch (costError) {
safeLog(null, `❌ Cost calculation error: ${costError.message}`);
return 0;
}
}
/**
* Sanitize request data with privacy protection
*/
function sanitizeRequestData(params) {
try {
if (!params) return {};
const sanitized = {
model: params.model,
temperature: params.temperature,
max_tokens: params.max_tokens,
top_p: params.top_p,
frequency_penalty: params.frequency_penalty,
presence_penalty: params.presence_penalty,
n: params.n,
stream: params.stream,
stop: Array.isArray(params.stop) ? params.stop.length : params.stop ? 1 : 0
};
// Handle messages (chat completions)
if (params.messages && Array.isArray(params.messages)) {
sanitized.messages = params.messages.slice(0, 10).map(msg => ({
role: msg.role,
content: typeof msg.content === 'string'
? msg.content.substring(0, 200) + (msg.content.length > 200 ? '...' : '')
: '[complex_content]'
}));
}
// Handle prompt (legacy completions)
if (params.prompt) {
sanitized.prompt = typeof params.prompt === 'string'
? params.prompt.substring(0, 200) + (params.prompt.length > 200 ? '...' : '')
: '[complex_prompt]';
}
// Handle input (embeddings)
if (params.input) {
if (typeof params.input === 'string') {
sanitized.input = params.input.substring(0, 200) + (params.input.length > 200 ? '...' : '');
} else if (Array.isArray(params.input)) {
sanitized.input = `[${params.input.length} items]`;
}
}
return sanitized;
} catch (sanitizeError) {
return { error: 'sanitization_failed' };
}
}
/**
* Sanitize response data with privacy protection
*/
function sanitizeResponseData(response) {
try {
if (!response) return {};
const sanitized = {
id: response.id,
object: response.object,
created: response.created,
model: response.model,
usage: response.usage
};
// Handle choices (chat/completions)
if (response.choices) {
sanitized.choices = response.choices.length;
if (response.choices[0]) {
sanitized.finish_reason = response.choices[0].finish_reason;
// Don't include actual content for privacy
sanitized.has_content = !!(response.choices[0].message?.content || response.choices[0].text);
}
}
// Handle embeddings
if (response.data && Array.isArray(response.data)) {
sanitized.embeddings_count = response.data.length;
if (response.data[0]?.embedding) {
sanitized.embedding_dimensions = response.data[0].embedding.length;
}
}
return sanitized;
} catch (sanitizeError) {
return { error: 'sanitization_failed' };
}
}
/**
* Safe logging that never crashes
*/
function safeLog(guardaianClient, message) {
try {
if (guardaianClient?.options?.debug) {
console.log(`🛡️ GuardAIan: ${message}`);
}
} catch (logError) {
// Even logging can fail - do nothing to prevent infinite loops
}
}
module.exports = {
patchOpenAI
};