ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
296 lines • 12.2 kB
JavaScript
export class AIExtractionUtils {
static extractLLMCalls(requests) {
const calls = [];
for (const request of requests) {
let provider = null;
let model = null;
let tokens = null;
let cost = 0;
// OpenAI
if (request.url.includes('api.openai.com')) {
provider = 'OpenAI';
try {
const reqBody = JSON.parse(request.requestBody || '{}');
model = reqBody.model;
if (request.responseBody) {
const resBody = JSON.parse(request.responseBody);
if (resBody.usage) {
tokens = {
prompt: resBody.usage.prompt_tokens,
completion: resBody.usage.completion_tokens,
total: resBody.usage.total_tokens
};
// Calculate cost (simplified pricing)
cost = this.calculateOpenAICost(model || '', tokens);
}
}
}
catch (e) { }
}
// Anthropic
else if (request.url.includes('api.anthropic.com')) {
provider = 'Anthropic';
try {
const reqBody = JSON.parse(request.requestBody || '{}');
model = reqBody.model;
if (request.responseBody) {
const resBody = JSON.parse(request.responseBody);
if (resBody.usage) {
tokens = {
prompt: resBody.usage.input_tokens,
completion: resBody.usage.output_tokens,
total: (resBody.usage.input_tokens || 0) + (resBody.usage.output_tokens || 0)
};
cost = this.calculateAnthropicCost(model || '', tokens);
}
}
}
catch (e) { }
}
if (provider) {
calls.push({
provider,
model: model || 'unknown',
timestamp: request.timestamp,
duration: request.duration,
tokens,
cost,
request: JSON.parse(request.requestBody || '{}'),
response: request.responseBody ? JSON.parse(request.responseBody) : null,
error: request.error
});
}
}
return calls;
}
static extractVectorSearches(requests) {
const searches = [];
for (const request of requests) {
let database = null;
let operation = 'unknown';
let metadata = {};
// Pinecone
if (request.url.includes('.pinecone.io')) {
database = 'Pinecone';
const urlParts = request.url.split('/');
operation = urlParts[urlParts.length - 1];
// Extract index name
const match = request.url.match(/https?:\/\/([^.]+)\.svc/);
if (match) {
metadata.index = match[1];
}
try {
const reqBody = JSON.parse(request.requestBody || '{}');
metadata.topK = reqBody.topK;
metadata.namespace = reqBody.namespace;
if (request.responseBody) {
const resBody = JSON.parse(request.responseBody);
if (resBody.matches) {
metadata.resultCount = resBody.matches.length;
metadata.bestScore = resBody.matches[0]?.score;
}
}
}
catch (e) { }
}
// pgvector
else if (request.requestBody && request.requestBody.includes('<->')) {
database = 'pgvector';
operation = 'similarity_search';
}
if (database) {
searches.push({
database,
operation,
timestamp: request.timestamp,
duration: request.duration,
query: request.requestBody,
results: request.responseBody,
metadata
});
}
}
return searches;
}
static extractRetrievalResults(requests) {
const results = [];
for (const request of requests) {
let database = null;
let queryResults = [];
let metadata = {};
// Pinecone
if (request.url.includes('.pinecone.io') && request.url.includes('query')) {
database = 'Pinecone';
try {
const reqBody = JSON.parse(request.requestBody || '{}');
metadata.topK = reqBody.topK;
if (request.responseBody) {
const resBody = JSON.parse(request.responseBody);
if (resBody.matches) {
queryResults = resBody.matches.map((m) => ({
id: m.id,
score: m.score,
metadata: m.metadata
}));
}
}
}
catch (e) { }
}
// Generic vector search (pgvector, custom endpoints)
else if (request.method === 'POST' && request.responseBody) {
try {
const resBody = JSON.parse(request.responseBody);
// Check for common result patterns
if (resBody.results && Array.isArray(resBody.results)) {
database = 'Vector DB';
queryResults = resBody.results.map((r) => ({
id: r.id || r._id,
score: r.similarity || r.score || r.distance || 0,
metadata: r.metadata || {}
}));
}
else if (resBody.matches && Array.isArray(resBody.matches)) {
database = 'Vector DB';
queryResults = resBody.matches.map((m) => ({
id: m.id,
score: m.score || 0,
metadata: m.metadata || {}
}));
}
}
catch (e) { }
}
if (database && queryResults.length > 0) {
results.push({
database,
results: queryResults,
metadata,
timestamp: request.timestamp
});
}
}
return results;
}
static extractDocumentProcessing(requests) {
const processing = [];
for (const request of requests) {
const contentType = request.headers?.['Content-Type'] || request.headers?.['content-type'] || '';
let documentType = null;
if (contentType.includes('pdf') || request.url.includes('pdf')) {
documentType = 'PDF';
}
else if (contentType.includes('wordprocessingml') || request.url.includes('docx')) {
documentType = 'DOCX';
}
if (documentType) {
let response = null;
try {
if (request.responseBody) {
response = JSON.parse(request.responseBody);
}
}
catch (e) { }
processing.push({
type: documentType,
endpoint: new URL(request.url).pathname,
response,
duration: request.duration
});
}
}
return processing;
}
static analyzePipelineFlow(requests) {
const steps = [];
// Sort by timestamp
const sortedRequests = [...requests].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
let order = 1;
// Document processing
const docProcessing = sortedRequests.find(r => r.url.includes('process') && (r.url.includes('document') || r.url.includes('pdf')));
if (docProcessing) {
steps.push({
order: order++,
name: 'Document Processing',
timestamp: docProcessing.timestamp,
duration: docProcessing.duration,
details: 'Document ingestion and chunking'
});
}
// Embedding generation
const embedding = sortedRequests.find(r => r.url.includes('embeddings'));
if (embedding) {
steps.push({
order: order++,
name: 'Embedding Generation',
timestamp: embedding.timestamp,
duration: embedding.duration,
details: 'Converting text to vector embeddings'
});
}
// Vector storage
const vectorStore = sortedRequests.find(r => r.url.includes('upsert') || (r.method === 'POST' && r.url.includes('pinecone')));
if (vectorStore) {
steps.push({
order: order++,
name: 'Vector Storage',
timestamp: vectorStore.timestamp,
duration: vectorStore.duration,
details: 'Storing embeddings in vector database'
});
}
// Retrieval
const retrieval = sortedRequests.find(r => r.url.includes('query') || r.url.includes('search'));
if (retrieval) {
steps.push({
order: order++,
name: 'Retrieval',
timestamp: retrieval.timestamp,
duration: retrieval.duration,
details: 'Searching for relevant documents'
});
}
// Generation
const generation = sortedRequests.find(r => r.url.includes('completions') || r.url.includes('messages'));
if (generation) {
steps.push({
order: order++,
name: 'Generation',
timestamp: generation.timestamp,
duration: generation.duration,
details: 'Generating response with LLM'
});
}
return steps;
}
static calculateOpenAICost(model, tokens) {
if (!tokens)
return 0;
// Simplified pricing (actual prices may vary)
const pricing = {
'gpt-4': { input: 0.03, output: 0.06 },
'gpt-3.5-turbo': { input: 0.0005, output: 0.0015 },
'text-embedding-ada-002': { input: 0.0001, output: 0 }
};
const modelPricing = pricing[model] || { input: 0, output: 0 };
const inputCost = (tokens.prompt || tokens.total || 0) * modelPricing.input / 1000;
const outputCost = (tokens.completion || 0) * modelPricing.output / 1000;
return inputCost + outputCost;
}
static calculateAnthropicCost(model, tokens) {
if (!tokens)
return 0;
// Simplified pricing
const pricing = {
'claude-3-opus': { input: 0.015, output: 0.075 },
'claude-3-sonnet': { input: 0.003, output: 0.015 },
'claude-3-haiku': { input: 0.00025, output: 0.00125 }
};
const modelKey = Object.keys(pricing).find(k => model.includes(k));
const modelPricing = modelKey ? pricing[modelKey] : { input: 0, output: 0 };
const inputCost = (tokens.prompt || 0) * modelPricing.input / 1000;
const outputCost = (tokens.completion || 0) * modelPricing.output / 1000;
return inputCost + outputCost;
}
}
//# sourceMappingURL=ai-utils.js.map