UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

346 lines 13.3 kB
/** * ContextInferrer - Infers context and disambiguates queries * * IMPLEMENTATION STATUS: * COMPLETE: Platform detection, field disambiguation, relationship inference * * Last Updated: July 3, 2025 */ import { ENTITY_PATTERNS } from '../patterns/EntityPatterns.js'; export class ContextInferrer { /** * Infer complete context from query components */ inferContext(query, primaryEntity, relatedEntities, filters, fields) { const context = { platform: this.inferPlatform(primaryEntity, relatedEntities, query), primaryIntent: this.inferPrimaryIntent(query, primaryEntity), impliedEntities: this.inferImpliedEntities(query, primaryEntity, filters), impliedFilters: this.inferImpliedFilters(query, primaryEntity), impliedJoins: this.inferImpliedJoins(primaryEntity, relatedEntities, filters, fields), disambiguations: this.disambiguateTerms(query, primaryEntity) }; return context; } /** * Infer platform from entities and query terms */ inferPlatform(primaryEntity, relatedEntities, query) { const allEntities = [primaryEntity, ...relatedEntities]; // Check entity platforms const webEntities = allEntities.filter(e => ENTITY_PATTERNS[e]?.platform === 'web'); const featureEntities = allEntities.filter(e => ENTITY_PATTERNS[e]?.platform === 'feature'); // Check query terms const hasWebTerms = /experiment|variation|campaign|page|personalization/i.test(query); const hasFeatureTerms = /flag|feature|toggle|rollout|rule/i.test(query); // Platform-specific terms take precedence if (hasWebTerms && !hasFeatureTerms) return 'web'; if (hasFeatureTerms && !hasWebTerms) return 'feature'; // Entity platforms if (webEntities.length > 0 && featureEntities.length === 0) return 'web'; if (featureEntities.length > 0 && webEntities.length === 0) return 'feature'; return 'both'; } /** * Infer the primary intent of the query */ inferPrimaryIntent(query, primaryEntity) { const normalizedQuery = query.toLowerCase(); // Analysis intents if (/analyz|insight|report|metric|performance/i.test(normalizedQuery)) { return 'analysis'; } // Comparison intents if (/compar|versus|vs|difference|between/i.test(normalizedQuery)) { return 'comparison'; } // Troubleshooting intents if (/why|issue|problem|not working|broken|fail/i.test(normalizedQuery)) { return 'troubleshooting'; } // Discovery intents if (/which|what|where|find|search/i.test(normalizedQuery)) { return 'discovery'; } // Monitoring intents if (/status|state|current|active|enabled/i.test(normalizedQuery)) { return 'monitoring'; } return 'general'; } /** * Infer entities that should be included based on context */ inferImpliedEntities(query, primaryEntity, filters) { const implied = []; // Flag queries with environment filters imply flag_environments if (primaryEntity === 'flags' && (filters.some(f => f.field === 'enabled') || /environment/i.test(query))) { implied.push('flag_environments'); } // Experiment queries with metrics imply results if (primaryEntity === 'experiments' && /metric|result|performance|conversion/i.test(query)) { implied.push('results'); } // Audience targeting only implies audiences if we're actually querying audience data // NOT when we're just filtering on the JSON field audience_conditions if (/audience\s+(name|id|description)|all\s+audiences|list\s+audiences/i.test(query)) { implied.push('audiences'); } // Note: audience_conditions is a JSON field in rules/experiments, not a reason to JOIN audiences table // Rule-related queries if (/rule|targeting|rollout/i.test(query) && primaryEntity === 'flags') { implied.push('rules'); if (/ruleset/i.test(query)) { implied.push('rulesets'); } } // Project context if (/project|workspace/i.test(query) && !implied.includes('projects')) { implied.push('projects'); } return [...new Set(implied)]; // Remove duplicates } /** * Infer filters based on common patterns */ inferImpliedFilters(query, primaryEntity) { const filters = []; // Active/enabled status if (/\bactive\b/i.test(query) && !/(inactive|not active)/i.test(query)) { if (primaryEntity === 'flags') { filters.push({ field: 'enabled', operator: '=', value: true, confidence: 0.8 }); } else { filters.push({ field: 'status', operator: '=', value: 'active', confidence: 0.8 }); } } // Archived status if (/\barchived\b/i.test(query) && !/(not archived|unarchived)/i.test(query)) { filters.push({ field: 'archived', operator: '=', value: true, confidence: 0.9 }); } // Production environment if (/\bproduction\b|\bprod\b/i.test(query)) { filters.push({ field: 'environment_key', operator: '=', value: 'production', confidence: 0.9 }); } // Recent items if (/\brecent\b|\bnew\b|\blatest\b/i.test(query)) { filters.push({ field: 'created_time', operator: '>', value: 'NOW() - INTERVAL 7 DAY', confidence: 0.7 }); } // High traffic/percentage if (/\bhigh traffic\b|\bhigh percentage\b/i.test(query)) { filters.push({ field: 'percentage_included', operator: '>', value: 7500, // 75% in basis points confidence: 0.7 }); } return filters; } /** * Infer joins based on entity relationships and filters */ inferImpliedJoins(primaryEntity, relatedEntities, filters, fields) { const joins = []; // Flag environment joins const needsFlagEnvironmentJoin = primaryEntity === 'flags' && (filters.some(f => ['enabled', 'environment_key', 'rollout_percentage'].includes(f.field) || f.field.includes('flag_environments.enabled') || f.field.includes('flag_environments.environment_key') || f.field.includes('flag_environments.percentage_included')) || (fields && fields.some(field => field.includes('flag_environments.') || ['enabled', 'environment_key'].includes(field.split('.').pop() || '')))); if (needsFlagEnvironmentJoin) { joins.push({ type: 'LEFT', entity: 'flag_environments', on: { leftField: 'flags.key', rightField: 'flag_environments.flag_key' } }); } // Experiment variation joins // L2-2 FIX: Don't add variations JOIN for flags - variations are JSON in flags table if (primaryEntity === 'experiments' && (relatedEntities.includes('variations') || filters.some(f => f.field.includes('variation')))) { joins.push({ type: 'LEFT', entity: 'variations', on: { leftField: 'experiments.id', rightField: 'variations.experiment_id' } }); } // L2-2 FIX: Don't add variations JOIN for flags either if (primaryEntity === 'flags' && (relatedEntities.includes('variations') || filters.some(f => f.field.includes('variation')))) { // Variations are stored as JSON in the flags table, not a separate entity // L2-2 FIX: Skipping variations JOIN for flags - variations are JSON column } // Rule audience joins if ((primaryEntity === 'rules' || relatedEntities.includes('rules')) && filters.some(f => f.field === 'audience_conditions')) { joins.push({ type: 'LEFT', entity: 'audiences', on: { leftField: 'rules.audience_conditions', rightField: 'audiences.id' } }); } return joins; } /** * Disambiguate ambiguous terms */ disambiguateTerms(query, primaryEntity) { const disambiguations = []; // "status" can mean different things if (/\bstatus\b/i.test(query)) { if (primaryEntity === 'flags') { disambiguations.push({ term: 'status', interpretation: 'enabled/disabled state in environments', confidence: 0.9 }); } else if (primaryEntity === 'experiments') { disambiguations.push({ term: 'status', interpretation: 'experiment state (running/paused/archived)', confidence: 0.9 }); } } // "active" interpretation if (/\bactive\b/i.test(query)) { if (primaryEntity === 'flags') { disambiguations.push({ term: 'active', interpretation: 'enabled in at least one environment', confidence: 0.85 }); } else { disambiguations.push({ term: 'active', interpretation: 'not archived and currently running', confidence: 0.85 }); } } // "traffic" interpretation if (/\btraffic\b/i.test(query)) { if (primaryEntity === 'flags') { disambiguations.push({ term: 'traffic', interpretation: 'rollout percentage in rules', confidence: 0.9 }); } else if (primaryEntity === 'experiments') { disambiguations.push({ term: 'traffic', interpretation: 'traffic allocation to variations', confidence: 0.9 }); } } // "results" interpretation if (/\bresults?\b/i.test(query)) { if (primaryEntity === 'experiments') { disambiguations.push({ term: 'results', interpretation: 'experiment metrics and statistical results', confidence: 0.95 }); } else { disambiguations.push({ term: 'results', interpretation: 'query output/data', confidence: 0.7 }); } } return disambiguations; } /** * Resolve field ambiguity based on context */ resolveFieldAmbiguity(field, primaryEntity, relatedEntities) { // Common ambiguous fields - using partial mapping const ambiguousFields = { 'name': { 'flags': 'flags.name', 'experiments': 'experiments.name', 'audiences': 'audiences.name', 'variations': 'variations.name' }, 'status': { 'flags': 'flag_environments.enabled', 'experiments': 'experiments.status', 'campaigns': 'campaigns.status' }, 'created': { 'flags': 'flags.created_time', 'experiments': 'experiments.created', 'events': 'events.created' } }; // Check if field needs disambiguation const normalizedField = field.toLowerCase(); if (ambiguousFields[normalizedField]) { const entityMap = ambiguousFields[normalizedField]; // Use primary entity first if (entityMap[primaryEntity]) { return entityMap[primaryEntity]; } // Check related entities for (const entity of relatedEntities) { if (entityMap[entity]) { return entityMap[entity]; } } } // Check if field already has table prefix if (field.includes('.')) { return field; } // Add primary entity prefix for safety return `${primaryEntity}.${field}`; } } //# sourceMappingURL=ContextInferrer.js.map