confidence-scorer.tsβ’6.85 kB
/**
* Confidence Scorer for node-specific validations
*
* Provides confidence scores for node-specific recommendations,
* allowing users to understand the reliability of suggestions.
*/
export interface ConfidenceScore {
value: number; // 0.0 to 1.0
reason: string;
factors: ConfidenceFactor[];
}
export interface ConfidenceFactor {
name: string;
weight: number;
matched: boolean;
description: string;
}
export class ConfidenceScorer {
/**
* Calculate confidence score for resource locator recommendation
*/
static scoreResourceLocatorRecommendation(
fieldName: string,
nodeType: string,
value: string
): ConfidenceScore {
const factors: ConfidenceFactor[] = [];
let totalWeight = 0;
let matchedWeight = 0;
// Factor 1: Exact field name match (highest confidence)
const exactFieldMatch = this.checkExactFieldMatch(fieldName, nodeType);
factors.push({
name: 'exact-field-match',
weight: 0.5,
matched: exactFieldMatch,
description: `Field name '${fieldName}' is known to use resource locator in ${nodeType}`
});
// Factor 2: Field name pattern (medium confidence)
const patternMatch = this.checkFieldPattern(fieldName);
factors.push({
name: 'field-pattern',
weight: 0.3,
matched: patternMatch,
description: `Field name '${fieldName}' matches common resource locator patterns`
});
// Factor 3: Value pattern (low confidence)
const valuePattern = this.checkValuePattern(value);
factors.push({
name: 'value-pattern',
weight: 0.1,
matched: valuePattern,
description: 'Value contains patterns typical of resource identifiers'
});
// Factor 4: Node type category (medium confidence)
const nodeCategory = this.checkNodeCategory(nodeType);
factors.push({
name: 'node-category',
weight: 0.1,
matched: nodeCategory,
description: `Node type '${nodeType}' typically uses resource locators`
});
// Calculate final score
for (const factor of factors) {
totalWeight += factor.weight;
if (factor.matched) {
matchedWeight += factor.weight;
}
}
const score = totalWeight > 0 ? matchedWeight / totalWeight : 0;
// Determine reason based on score
let reason: string;
if (score >= 0.8) {
reason = 'High confidence: Multiple strong indicators suggest resource locator format';
} else if (score >= 0.5) {
reason = 'Medium confidence: Some indicators suggest resource locator format';
} else if (score >= 0.3) {
reason = 'Low confidence: Weak indicators for resource locator format';
} else {
reason = 'Very low confidence: Minimal evidence for resource locator format';
}
return {
value: score,
reason,
factors
};
}
/**
* Known field mappings with exact matches
*/
private static readonly EXACT_FIELD_MAPPINGS: Record<string, string[]> = {
'github': ['owner', 'repository', 'user', 'organization'],
'googlesheets': ['sheetId', 'documentId', 'spreadsheetId'],
'googledrive': ['fileId', 'folderId', 'driveId'],
'slack': ['channel', 'user', 'channelId', 'userId'],
'notion': ['databaseId', 'pageId', 'blockId'],
'airtable': ['baseId', 'tableId', 'viewId']
};
private static checkExactFieldMatch(fieldName: string, nodeType: string): boolean {
const nodeBase = nodeType.split('.').pop()?.toLowerCase() || '';
for (const [pattern, fields] of Object.entries(this.EXACT_FIELD_MAPPINGS)) {
if (nodeBase === pattern || nodeBase.startsWith(`${pattern}-`)) {
return fields.includes(fieldName);
}
}
return false;
}
/**
* Common patterns in field names that suggest resource locators
*/
private static readonly FIELD_PATTERNS = [
/^.*Id$/i, // ends with Id
/^.*Ids$/i, // ends with Ids
/^.*Key$/i, // ends with Key
/^.*Name$/i, // ends with Name
/^.*Path$/i, // ends with Path
/^.*Url$/i, // ends with Url
/^.*Uri$/i, // ends with Uri
/^(table|database|collection|bucket|folder|file|document|sheet|board|project|issue|user|channel|team|organization|repository|owner)$/i
];
private static checkFieldPattern(fieldName: string): boolean {
return this.FIELD_PATTERNS.some(pattern => pattern.test(fieldName));
}
/**
* Check if the value looks like it contains identifiers
*/
private static checkValuePattern(value: string): boolean {
// Remove = prefix if present for analysis
const content = value.startsWith('=') ? value.substring(1) : value;
// Skip if not an expression
if (!content.includes('{{') || !content.includes('}}')) {
return false;
}
// Check for patterns that suggest IDs or resource references
const patterns = [
/\{\{.*\.(id|Id|ID|key|Key|name|Name|path|Path|url|Url|uri|Uri).*\}\}/i,
/\{\{.*_(id|Id|ID|key|Key|name|Name|path|Path|url|Url|uri|Uri).*\}\}/i,
/\{\{.*(id|Id|ID|key|Key|name|Name|path|Path|url|Url|uri|Uri).*\}\}/i
];
return patterns.some(pattern => pattern.test(content));
}
/**
* Node categories that commonly use resource locators
*/
private static readonly RESOURCE_HEAVY_NODES = [
'github', 'gitlab', 'bitbucket', // Version control
'googlesheets', 'googledrive', 'dropbox', // Cloud storage
'slack', 'discord', 'telegram', // Communication
'notion', 'airtable', 'baserow', // Databases
'jira', 'asana', 'trello', 'monday', // Project management
'salesforce', 'hubspot', 'pipedrive', // CRM
'stripe', 'paypal', 'square', // Payment
'aws', 'gcp', 'azure', // Cloud providers
'mysql', 'postgres', 'mongodb', 'redis' // Databases
];
private static checkNodeCategory(nodeType: string): boolean {
const nodeBase = nodeType.split('.').pop()?.toLowerCase() || '';
return this.RESOURCE_HEAVY_NODES.some(category =>
nodeBase.includes(category)
);
}
/**
* Get confidence level as a string
*/
static getConfidenceLevel(score: number): 'high' | 'medium' | 'low' | 'very-low' {
if (score >= 0.8) return 'high';
if (score >= 0.5) return 'medium';
if (score >= 0.3) return 'low';
return 'very-low';
}
/**
* Should apply recommendation based on confidence and threshold
*/
static shouldApplyRecommendation(
score: number,
threshold: 'strict' | 'normal' | 'relaxed' = 'normal'
): boolean {
const thresholds = {
strict: 0.8, // Only apply high confidence recommendations
normal: 0.5, // Apply medium and high confidence
relaxed: 0.3 // Apply low, medium, and high confidence
};
return score >= thresholds[threshold];
}
}