@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
485 lines • 19.2 kB
JavaScript
/**
* InsightsEngine - Analyzes query results to detect patterns and provide recommendations
*/
import { INSIGHT_PATTERNS } from './constants.js';
export class InsightsEngine {
customPatterns = new Map();
/**
* Analyze results and generate insights
*/
analyzeResults(results, context) {
const insights = [];
// Add performance insights
insights.push(...this.analyzePerformance(context));
// Add data pattern insights
insights.push(...this.analyzeDataPatterns(results, context));
// Add entity-specific insights
insights.push(...this.analyzeEntitySpecificPatterns(results, context));
// Add recommendation insights
insights.push(...this.generateRecommendations(results, context));
// Sort by severity and dedup
return this.deduplicateAndSort(insights);
}
/**
* Register a custom insight pattern
*/
registerPattern(name, pattern) {
this.customPatterns.set(name, pattern);
}
analyzePerformance(context) {
const insights = [];
// Slow query warning
if (context.executionTime > 5000) {
insights.push({
type: 'anomaly',
title: 'Slow query detected',
description: `Query took ${(context.executionTime / 1000).toFixed(2)} seconds to execute`,
severity: 'warning',
data: {
executionTime: context.executionTime,
suggestion: 'Consider adding more specific filters or optimizing the query'
}
});
}
// Large result set
if (context.resultCount > 1000) {
insights.push({
type: 'pattern',
title: 'Large result set',
description: `Query returned ${context.resultCount} results`,
severity: 'info',
data: {
resultCount: context.resultCount,
hasMore: context.hasMoreResults
}
});
}
// No results
if (context.resultCount === 0) {
insights.push({
type: 'pattern',
title: 'No results found',
description: 'The query returned no matching results',
severity: 'info',
data: {
suggestions: this.getNoResultsSuggestions(context.queryIntent)
}
});
}
return insights;
}
analyzeDataPatterns(results, context) {
const insights = [];
if (results.length === 0)
return insights;
// Analyze based on entity type
switch (context.queryIntent.primaryEntity) {
case 'flags':
insights.push(...this.analyzeFlagPatterns(results));
break;
case 'experiments':
insights.push(...this.analyzeExperimentPatterns(results));
break;
case 'audiences':
insights.push(...this.analyzeAudiencePatterns(results));
break;
case 'variations':
insights.push(...this.analyzeVariationPatterns(results));
break;
}
// Analyze common patterns
insights.push(...this.analyzeCommonPatterns(results));
return insights;
}
analyzeFlagPatterns(results) {
const insights = [];
// High variation count
const highVariationFlags = results.filter(r => (r.variation_count || r.variations?.length || 0) > 5);
if (highVariationFlags.length > 0) {
insights.push({
type: 'pattern',
title: 'Flags with high variation count',
description: `Found ${highVariationFlags.length} flags with more than 5 variations`,
severity: 'info',
data: {
flags: highVariationFlags.map(f => ({
key: f.key,
name: f.name,
variationCount: f.variation_count || f.variations?.length
})),
recommendation: 'Consider simplifying flags with many variations'
}
});
}
// Unused flags (no rules or all disabled)
const unusedFlags = results.filter(r => !r.rules || r.rules.length === 0 ||
(r.rules_count === 0 && r.enabled === false));
if (unusedFlags.length > 0) {
insights.push({
type: 'pattern',
title: 'Potentially unused flags',
description: `Found ${unusedFlags.length} flags with no active rules`,
severity: 'warning',
data: {
flags: unusedFlags.map(f => ({ key: f.key, name: f.name })),
recommendation: 'Review these flags for potential cleanup'
}
});
}
// Complex variable definitions
const complexVariableFlags = results.filter(r => {
const varCount = r.variable_count ||
(r.variable_definitions && Object.keys(r.variable_definitions).length) || 0;
return varCount > 10;
});
if (complexVariableFlags.length > 0) {
insights.push({
type: 'pattern',
title: 'Flags with complex variable structures',
description: `Found ${complexVariableFlags.length} flags with more than 10 variables`,
severity: 'info',
data: {
flags: complexVariableFlags.map(f => ({
key: f.key,
name: f.name,
variableCount: f.variable_count
}))
}
});
}
return insights;
}
analyzeExperimentPatterns(results) {
const insights = [];
// Status distribution
const statusCounts = this.countByField(results, 'status');
if (Object.keys(statusCounts).length > 0) {
insights.push({
type: 'pattern',
title: 'Experiment status distribution',
description: 'Overview of experiment statuses',
severity: 'info',
data: {
distribution: statusCounts,
total: results.length
}
});
}
// Experiments with many audiences
const complexTargeting = results.filter(r => (r.audience_count || r.audience_ids?.length || 0) > 3);
if (complexTargeting.length > 0) {
insights.push({
type: 'pattern',
title: 'Experiments with complex targeting',
description: `Found ${complexTargeting.length} experiments targeting more than 3 audiences`,
severity: 'info',
data: {
experiments: complexTargeting.map(e => ({
name: e.name,
audienceCount: e.audience_count || e.audience_ids?.length
})),
recommendation: 'Complex targeting may reduce reach'
}
});
}
// Long-running experiments
const longRunning = results.filter(r => {
if (!r.created_time || r.status !== 'running')
return false;
const daysRunning = this.daysBetween(new Date(r.created_time), new Date());
return daysRunning > 90;
});
if (longRunning.length > 0) {
insights.push({
type: 'anomaly',
title: 'Long-running experiments',
description: `Found ${longRunning.length} experiments running for more than 90 days`,
severity: 'warning',
data: {
experiments: longRunning.map(e => ({
name: e.name,
daysRunning: this.daysBetween(new Date(e.created_time), new Date())
})),
recommendation: 'Consider reviewing long-running experiments for conclusive results'
}
});
}
return insights;
}
analyzeAudiencePatterns(results) {
const insights = [];
// Unused audiences
const unusedAudiences = results.filter(r => r.experiment_count === 0 || (!r.experiments || r.experiments.length === 0));
if (unusedAudiences.length > 0) {
insights.push({
type: 'pattern',
title: 'Unused audiences',
description: `Found ${unusedAudiences.length} audiences not used in any experiments`,
severity: 'warning',
data: {
audiences: unusedAudiences.map(a => ({ id: a.id, name: a.name })),
recommendation: 'Consider archiving unused audiences'
}
});
}
// Complex condition audiences
const complexAudiences = results.filter(r => {
const conditionCount = r.condition_count ||
(r.conditions && this.countConditions(r.conditions)) || 0;
return conditionCount > 5;
});
if (complexAudiences.length > 0) {
insights.push({
type: 'pattern',
title: 'Audiences with complex conditions',
description: `Found ${complexAudiences.length} audiences with more than 5 conditions`,
severity: 'info',
data: {
audiences: complexAudiences.map(a => ({
name: a.name,
conditionCount: a.condition_count
})),
recommendation: 'Complex conditions may impact performance'
}
});
}
return insights;
}
analyzeVariationPatterns(results) {
const insights = [];
// Disabled variations
const disabledVariations = results.filter(r => r.enabled === false);
if (disabledVariations.length > 0) {
insights.push({
type: 'pattern',
title: 'Disabled variations',
description: `Found ${disabledVariations.length} disabled variations`,
severity: 'info',
data: {
variations: disabledVariations.map(v => ({
key: v.key,
flagKey: v.flag_key
}))
}
});
}
// Variations with custom code
const customCodeVariations = results.filter(r => r.has_custom_code ||
(r.actions && r.actions.some((a) => a.type === 'custom_code')));
if (customCodeVariations.length > 0) {
insights.push({
type: 'pattern',
title: 'Variations with custom code',
description: `Found ${customCodeVariations.length} variations using custom code`,
severity: 'info',
data: {
count: customCodeVariations.length,
recommendation: 'Ensure custom code is tested and maintained'
}
});
}
return insights;
}
analyzeCommonPatterns(results) {
const insights = [];
// Archived items
const archivedItems = results.filter(r => r.archived === true);
if (archivedItems.length > 0) {
const percentArchived = (archivedItems.length / results.length) * 100;
if (percentArchived > 30) {
insights.push({
type: 'pattern',
title: 'High percentage of archived items',
description: `${percentArchived.toFixed(1)}% of results are archived`,
severity: 'info',
data: {
archivedCount: archivedItems.length,
totalCount: results.length,
recommendation: 'Consider filtering out archived items or cleaning up old data'
}
});
}
}
// Recently modified items
const recentlyModified = results.filter(r => {
const updateField = r.updated_time || r.last_modified || r.updated_at;
if (!updateField)
return false;
const daysSince = this.daysBetween(new Date(updateField), new Date());
return daysSince <= 7;
});
if (recentlyModified.length > 0) {
insights.push({
type: 'pattern',
title: 'Recent activity',
description: `${recentlyModified.length} items modified in the last 7 days`,
severity: 'info',
data: {
recentCount: recentlyModified.length,
percentage: ((recentlyModified.length / results.length) * 100).toFixed(1)
}
});
}
return insights;
}
analyzeEntitySpecificPatterns(results, context) {
const insights = [];
// Check standard patterns
for (const [patternName, pattern] of Object.entries(INSIGHT_PATTERNS)) {
const matching = results.filter(r => pattern.condition(r));
if (matching.length > 0) {
const description = pattern.template
.replace('{count}', matching.length.toString())
.replace('{allocation}', matching[0]?.traffic_allocation || '');
insights.push({
type: pattern.type,
title: pattern.title,
description,
severity: pattern.type === 'anomaly' ? 'warning' : 'info',
data: {
matchingItems: matching.length,
examples: matching.slice(0, 3).map(item => ({
id: item.id,
name: item.name || item.key
}))
}
});
}
}
// Check custom patterns
for (const [name, pattern] of this.customPatterns) {
const matching = results.filter(r => pattern.condition(r));
if (matching.length > 0) {
insights.push(pattern.generateInsight(matching, context));
}
}
return insights;
}
generateRecommendations(results, context) {
const recommendations = [];
// Optimization recommendations based on query
if (context.queryIntent.groupBy && context.queryIntent.groupBy.length > 2) {
recommendations.push({
type: 'recommendation',
title: 'Query optimization suggestion',
description: 'Consider reducing the number of grouping fields for better performance',
severity: 'info',
data: {
currentGrouping: context.queryIntent.groupBy,
suggestion: 'Use fewer grouping fields or create a summary view'
}
});
}
// Data quality recommendations
const nullCounts = this.countNullFields(results);
const highNullFields = Object.entries(nullCounts)
.filter(([field, count]) => count / results.length > 0.5)
.map(([field]) => field);
if (highNullFields.length > 0) {
recommendations.push({
type: 'recommendation',
title: 'Data quality improvement',
description: 'Several fields have high null rates',
severity: 'info',
data: {
fields: highNullFields,
recommendation: 'Consider making these fields required or providing defaults'
}
});
}
return recommendations;
}
getNoResultsSuggestions(intent) {
const suggestions = [];
if (intent.filters && intent.filters.length > 2) {
suggestions.push('Try removing some filters to broaden the search');
}
if (intent.timeRange) {
suggestions.push('Try expanding the time range');
}
if (intent.filters?.some(f => f.operator === 'eq')) {
suggestions.push('Try using "contains" instead of exact matches');
}
suggestions.push('Check if the data has been synced recently');
return suggestions;
}
countByField(results, field) {
const counts = {};
for (const result of results) {
const value = result[field];
if (value !== undefined && value !== null) {
const key = String(value);
counts[key] = (counts[key] || 0) + 1;
}
}
return counts;
}
countConditions(conditions) {
if (!conditions)
return 0;
if (typeof conditions === 'string') {
try {
conditions = JSON.parse(conditions);
}
catch {
return 1;
}
}
if (Array.isArray(conditions)) {
return conditions.length;
}
if (conditions.and) {
return conditions.and.length;
}
if (conditions.or) {
return conditions.or.length;
}
return 1;
}
countNullFields(results) {
const nullCounts = {};
if (results.length === 0)
return nullCounts;
// Get all fields from first result
const fields = Object.keys(results[0]);
for (const field of fields) {
nullCounts[field] = results.filter(r => r[field] === null || r[field] === undefined).length;
}
return nullCounts;
}
daysBetween(date1, date2) {
const diffTime = Math.abs(date2.getTime() - date1.getTime());
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
deduplicateAndSort(insights) {
// Remove duplicates based on title and type
const seen = new Set();
const unique = insights.filter(insight => {
const key = `${insight.type}:${insight.title}`;
if (seen.has(key))
return false;
seen.add(key);
return true;
});
// Sort by severity then type
const severityOrder = {
critical: 0,
warning: 1,
info: 2
};
const typeOrder = {
anomaly: 0,
recommendation: 1,
pattern: 2,
trend: 3
};
return unique.sort((a, b) => {
const severityDiff = (severityOrder[a.severity || 'info'] || 2) -
(severityOrder[b.severity || 'info'] || 2);
if (severityDiff !== 0)
return severityDiff;
return (typeOrder[a.type] || 3) - (typeOrder[b.type] || 3);
});
}
}
//# sourceMappingURL=InsightsEngine.js.map