@guardaian/sdk
Version:
Zero-friction AI governance and monitoring SDK for Node.js applications
512 lines (439 loc) • 17 kB
JavaScript
/**
* Hugging Face Inference API Interceptor - Bulletproof Production Version
*
* CRITICAL GUARANTEES:
* 1. Customer's Hugging Face calls ALWAYS work, even if GuardAIan is down
* 2. Zero performance impact on customer's AI operations
* 3. All tracking errors are isolated and non-blocking
* 4. Supports all HF endpoints (textGeneration, chatCompletion, featureExtraction, textToImage)
*/
function patchHuggingFace(HfInference, guardaianClient) {
// Early validation with error protection
if (!HfInference || typeof HfInference !== 'function') {
safeLog(guardaianClient, '⚠️ Invalid Hugging Face module - skipping patch (non-blocking)');
return;
}
// Prevent double-patching
if (HfInference.__guardaianPatched) {
safeLog(guardaianClient, '⚠️ Hugging Face already patched - skipping');
return;
}
try {
safeLog(guardaianClient, '🔧 Patching Hugging Face Inference SDK with bulletproof protection...');
// Patch various HF endpoints
patchTextGeneration(HfInference, guardaianClient);
patchChatCompletion(HfInference, guardaianClient);
patchFeatureExtraction(HfInference, guardaianClient);
patchTextToImage(HfInference, guardaianClient);
// Mark as patched
HfInference.__guardaianPatched = true;
safeLog(guardaianClient, '✅ Hugging Face Inference SDK patched successfully with failsafe protection');
} catch (patchError) {
// Even patching errors should not break customer code
safeLog(guardaianClient, `⚠️ Hugging Face patching failed (non-blocking): ${patchError.message}`);
}
}
/**
* Patch HF Text Generation endpoint with bulletproof error handling
*/
function patchTextGeneration(HfInference, guardaianClient) {
try {
const originalTextGeneration = HfInference.prototype.textGeneration;
if (!originalTextGeneration) {
safeLog(guardaianClient, '⚠️ HF textGeneration method not found - skipping patch');
return;
}
HfInference.prototype.textGeneration = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Always call original HF API first
let response, originalError;
try {
response = await originalTextGeneration.call(this, params, options);
} catch (error) {
originalError = error;
}
// CRITICAL: Track usage in completely isolated context
setImmediate(() => {
try {
trackHuggingFaceUsage(params, response, originalError, startTime, guardaianClient, 'textGeneration', options);
} catch (trackingError) {
safeLog(guardaianClient, `❌ HF textGeneration tracking error (isolated): ${trackingError.message}`);
}
});
// CRITICAL: Always return original result or throw original error
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch HF textGeneration: ${patchError.message}`);
}
}
/**
* Patch HF Chat Completion endpoint with bulletproof error handling
*/
function patchChatCompletion(HfInference, guardaianClient) {
try {
const originalChatCompletion = HfInference.prototype.chatCompletion;
if (!originalChatCompletion) {
safeLog(guardaianClient, '⚠️ HF chatCompletion method not found - skipping patch');
return;
}
HfInference.prototype.chatCompletion = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Always call original HF API first
let response, originalError;
try {
response = await originalChatCompletion.call(this, params, options);
} catch (error) {
originalError = error;
}
// CRITICAL: Track usage in completely isolated context
setImmediate(() => {
try {
trackHuggingFaceUsage(params, response, originalError, startTime, guardaianClient, 'chatCompletion', options);
} catch (trackingError) {
safeLog(guardaianClient, `❌ HF chatCompletion tracking error (isolated): ${trackingError.message}`);
}
});
// CRITICAL: Always return original result or throw original error
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch HF chatCompletion: ${patchError.message}`);
}
}
/**
* Patch HF Feature Extraction endpoint with bulletproof error handling
*/
function patchFeatureExtraction(HfInference, guardaianClient) {
try {
const originalFeatureExtraction = HfInference.prototype.featureExtraction;
if (!originalFeatureExtraction) {
safeLog(guardaianClient, '⚠️ HF featureExtraction method not found - skipping patch');
return;
}
HfInference.prototype.featureExtraction = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Always call original HF API first
let response, originalError;
try {
response = await originalFeatureExtraction.call(this, params, options);
} catch (error) {
originalError = error;
}
// CRITICAL: Track usage in completely isolated context
setImmediate(() => {
try {
trackHuggingFaceUsage(params, response, originalError, startTime, guardaianClient, 'featureExtraction', options);
} catch (trackingError) {
safeLog(guardaianClient, `❌ HF featureExtraction tracking error (isolated): ${trackingError.message}`);
}
});
// CRITICAL: Always return original result or throw original error
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch HF featureExtraction: ${patchError.message}`);
}
}
/**
* Patch HF Text to Image endpoint with bulletproof error handling
*/
function patchTextToImage(HfInference, guardaianClient) {
try {
const originalTextToImage = HfInference.prototype.textToImage;
if (!originalTextToImage) {
safeLog(guardaianClient, '⚠️ HF textToImage method not found - skipping patch');
return;
}
HfInference.prototype.textToImage = async function(params, options) {
const startTime = Date.now();
// CRITICAL: Always call original HF API first
let response, originalError;
try {
response = await originalTextToImage.call(this, params, options);
} catch (error) {
originalError = error;
}
// CRITICAL: Track usage in completely isolated context
setImmediate(() => {
try {
trackHuggingFaceUsage(params, response, originalError, startTime, guardaianClient, 'textToImage', options);
} catch (trackingError) {
safeLog(guardaianClient, `❌ HF textToImage tracking error (isolated): ${trackingError.message}`);
}
});
// CRITICAL: Always return original result or throw original error
if (originalError) throw originalError;
return response;
};
} catch (patchError) {
safeLog(guardaianClient, `❌ Failed to patch HF textToImage: ${patchError.message}`);
}
}
/**
* Track Hugging Face usage with complete error isolation
*/
async function trackHuggingFaceUsage(params, response, error, startTime, guardaianClient, operation, options) {
try {
const endTime = Date.now();
const duration = endTime - startTime;
const model = params.model || options?.model || getDefaultModel(operation);
if (response) {
// Successful request tracking
const usage = extractHuggingFaceUsage(params, response, operation);
const cost = calculateHuggingFaceCost(model, usage, operation);
// Fire-and-forget tracking call
guardaianClient.track({
service: 'huggingface',
model: model,
operation: operation,
inputTokens: usage.inputTokens || 0,
outputTokens: usage.outputTokens || 0,
totalTokens: (usage.inputTokens || 0) + (usage.outputTokens || 0),
cost: cost,
duration: duration,
requestData: sanitizeHuggingFaceRequest(params, operation, options),
responseData: sanitizeHuggingFaceResponse(response, operation),
metadata: {
success: true,
operation: operation,
responseType: Array.isArray(response) ? 'array' : typeof response
}
}).catch(trackingError => {
safeLog(guardaianClient, `❌ HF track call failed (isolated): ${trackingError.message}`);
});
safeLog(guardaianClient, `✅ Tracked HF ${operation}: ${model} (${(usage.inputTokens || 0) + (usage.outputTokens || 0)} tokens, $${cost.toFixed(6)})`);
} else if (error) {
// Failed request tracking
guardaianClient.track({
service: 'huggingface',
model: model,
operation: operation,
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
cost: 0,
duration: duration,
requestData: sanitizeHuggingFaceRequest(params, operation, options),
responseData: {
error: true,
status: error.status || 'unknown',
message: error.message?.substring(0, 200) || 'Unknown error'
},
metadata: {
success: false,
errorType: error.constructor.name,
operation: operation
}
}).catch(trackingError => {
safeLog(guardaianClient, `❌ HF error tracking failed (isolated): ${trackingError.message}`);
});
safeLog(guardaianClient, `📊 Tracked HF ${operation} error: ${model} - ${error.message}`);
}
} catch (trackingError) {
// Ultimate safety net
safeLog(guardaianClient, `❌ HF usage tracking completely failed (isolated): ${trackingError.message}`);
}
}
/**
* Get default model for operation with error protection
*/
function getDefaultModel(operation) {
try {
const defaults = {
'textGeneration': 'gpt2',
'chatCompletion': 'microsoft/DialoGPT-medium',
'featureExtraction': 'sentence-transformers/all-MiniLM-L6-v2',
'textToImage': 'runwayml/stable-diffusion-v1-5'
};
return defaults[operation] || 'unknown-model';
} catch (defaultError) {
return 'unknown-model';
}
}
/**
* Extract usage data from Hugging Face response with error protection
*/
function extractHuggingFaceUsage(params, response, operation) {
try {
switch (operation) {
case 'textGeneration':
const inputText = params.inputs || '';
const outputText = response.generated_text || '';
const cleanOutput = outputText.replace(inputText, ''); // Remove input from output
return {
inputTokens: estimateTokens(inputText),
outputTokens: estimateTokens(cleanOutput)
};
case 'chatCompletion':
const usage = response.usage || {};
return {
inputTokens: usage.prompt_tokens || estimateTokensFromMessages(params.messages || []),
outputTokens: usage.completion_tokens || estimateTokens(response.choices?.[0]?.message?.content || '')
};
case 'featureExtraction':
// Embeddings only have input tokens
const inputTextEmbed = params.inputs || '';
return {
inputTokens: estimateTokens(inputTextEmbed),
outputTokens: 0
};
case 'textToImage':
// Image generation based on prompt
const inputTextImage = params.inputs || '';
return {
inputTokens: estimateTokens(inputTextImage),
outputTokens: 0
};
default:
return { inputTokens: 0, outputTokens: 0 };
}
} catch (extractError) {
return { inputTokens: 0, outputTokens: 0 };
}
}
/**
* Estimate tokens from text with error protection
*/
function estimateTokens(text) {
try {
if (!text || typeof text !== 'string') return 0;
return Math.ceil(text.length / 4); // Rough estimate: 1 token ≈ 4 characters
} catch (estimateError) {
return 0;
}
}
/**
* Estimate tokens from messages array with error protection
*/
function estimateTokensFromMessages(messages) {
try {
return messages.reduce((total, message) => {
const content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
return total + estimateTokens(content) + 4; // +4 for role and formatting tokens
}, 0);
} catch (estimateError) {
return 0;
}
}
/**
* Calculate cost for Hugging Face API calls with error protection
*/
function calculateHuggingFaceCost(model, usage, operation) {
try {
// Hugging Face Inference API pricing varies by model and compute time
// Many models are free but rate-limited, enterprise pricing varies
const baseRates = {
'textGeneration': { input: 0.0001, output: 0.0002 },
'chatCompletion': { input: 0.0001, output: 0.0002 },
'featureExtraction': { input: 0.0001, output: 0 },
'textToImage': { input: 0.001, output: 0 } // Higher cost for image generation
};
// Model-specific pricing adjustments
const modelMultipliers = {
'gpt2': 0.5,
'microsoft/DialoGPT-medium': 0.8,
'sentence-transformers/all-MiniLM-L6-v2': 0.3,
'runwayml/stable-diffusion-v1-5': 2.0,
'meta-llama/Llama-2-7b-chat-hf': 1.2,
'mistralai/Mistral-7B-Instruct-v0.1': 1.0,
'HuggingFaceH4/zephyr-7b-beta': 1.0
};
const rates = baseRates[operation] || baseRates['textGeneration'];
const multiplier = modelMultipliers[model] || 1.0;
const inputCost = (usage.inputTokens || 0) * rates.input * multiplier / 1000;
const outputCost = (usage.outputTokens || 0) * rates.output * multiplier / 1000;
return Math.round((inputCost + outputCost) * 10000) / 10000;
} catch (costError) {
safeLog(null, `❌ HF cost calculation error: ${costError.message}`);
return 0;
}
}
/**
* Sanitize Hugging Face request data with privacy protection
*/
function sanitizeHuggingFaceRequest(params, operation, options) {
try {
const sanitized = {
model: params.model || options?.model || getDefaultModel(operation),
operation: operation
};
switch (operation) {
case 'textGeneration':
sanitized.inputs = params.inputs ? params.inputs.substring(0, 200) + (params.inputs.length > 200 ? '...' : '') : undefined;
sanitized.parameters = params.parameters;
break;
case 'chatCompletion':
sanitized.messages = params.messages ? params.messages.map(msg => ({
role: msg.role,
content: typeof msg.content === 'string'
? msg.content.substring(0, 100) + (msg.content.length > 100 ? '...' : '')
: '[complex_content]'
})) : [];
sanitized.temperature = params.temperature;
sanitized.max_tokens = params.max_tokens;
sanitized.top_p = params.top_p;
break;
case 'featureExtraction':
sanitized.inputs = params.inputs ? params.inputs.substring(0, 200) + (params.inputs.length > 200 ? '...' : '') : undefined;
break;
case 'textToImage':
sanitized.inputs = params.inputs ? params.inputs.substring(0, 200) + (params.inputs.length > 200 ? '...' : '') : undefined;
sanitized.parameters = params.parameters;
break;
}
sanitized.options = options;
return sanitized;
} catch (sanitizeError) {
return { error: 'sanitization_failed' };
}
}
/**
* Sanitize Hugging Face response data with privacy protection
*/
function sanitizeHuggingFaceResponse(response, operation) {
try {
const sanitized = {
operation: operation,
response_type: Array.isArray(response) ? 'array' : typeof response
};
switch (operation) {
case 'textGeneration':
sanitized.generated_text_length = response.generated_text?.length || 0;
break;
case 'chatCompletion':
sanitized.finish_reason = response.choices?.[0]?.finish_reason;
sanitized.choices_count = response.choices?.length || 0;
sanitized.response_id = response.id;
break;
case 'featureExtraction':
sanitized.embeddings_count = Array.isArray(response) ? response.length : 1;
sanitized.dimensions = Array.isArray(response) && response[0] ? response[0].length : 0;
break;
case 'textToImage':
sanitized.image_generated = !!response;
break;
}
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
}
}
module.exports = {
patchHuggingFace
};