/** * Intelligent Verb Scoring Augmentation * * Enhances relationship quality through intelligent semantic scoring * Provides context-aware relationship weights based on: * - Semantic proximity of connected entities * - Frequency-based amplification * - Temporal decay modeling * - Adaptive learning from usage patterns * * Critical for enterprise knowledge graphs with millions of relationships */ import { BaseAugmentation, AugmentationContext } from './brainyAugmentation.js' interface VerbScoringConfig { enabled?: boolean // Semantic Analysis enableSemanticScoring?: boolean // Use entity embeddings for scoring semanticThreshold?: number // Minimum semantic similarity semanticWeight?: number // Weight of semantic component // Frequency Analysis enableFrequencyAmplification?: boolean // Amplify frequently used relationships frequencyDecay?: number // How quickly frequency importance decays maxFrequencyBoost?: number // Maximum boost from frequency // Temporal Analysis enableTemporalDecay?: boolean // Apply time-based decay temporalDecayRate?: number // Decay rate per day (0-1) temporalWindow?: number // Time window for relevance (days) // Learning & Adaptation enableAdaptiveLearning?: boolean // Learn from usage patterns learningRate?: number // How quickly to adapt (0-1) confidenceThreshold?: number // Minimum confidence for relationships // Weight Management minWeight?: number // Minimum relationship weight maxWeight?: number // Maximum relationship weight baseWeight?: number // Default weight for new relationships } interface RelationshipMetrics { count: number // How many times this relationship was created totalWeight: number // Sum of all weights averageWeight: number // Average weight lastUpdated: number // Last time this relationship was scored semanticScore: number // Semantic similarity score frequencyScore: number // Frequency-based score temporalScore: number // Time-based relevance score confidenceScore: number // Overall confidence } interface ScoringMetrics { relationshipsScored: number averageSemanticScore: number averageFrequencyScore: number averageTemporalScore: number averageConfidenceScore: number adaptiveAdjustments: number computationTimeMs: number } export class IntelligentVerbScoringAugmentation extends BaseAugmentation { name = 'IntelligentVerbScoring' timing = 'around' as const operations = ['addVerb', 'relate'] as ('addVerb' | 'relate')[] priority = 10 // Enhancement feature - runs after core operations // Add enabled property for backward compatibility get enabled(): boolean { return this.config.enabled } private config: Required private relationshipStats: Map = new Map() private metrics: ScoringMetrics = { relationshipsScored: 0, averageSemanticScore: 0, averageFrequencyScore: 0, averageTemporalScore: 0, averageConfidenceScore: 0, adaptiveAdjustments: 0, computationTimeMs: 0 } private scoringInstance: any // Will hold IntelligentVerbScoring instance constructor(config: VerbScoringConfig = {}) { super() this.config = { enabled: config.enabled ?? true, // Smart by default! // Semantic Analysis enableSemanticScoring: config.enableSemanticScoring ?? true, semanticThreshold: config.semanticThreshold ?? 0.3, semanticWeight: config.semanticWeight ?? 0.4, // Frequency Analysis enableFrequencyAmplification: config.enableFrequencyAmplification ?? true, frequencyDecay: config.frequencyDecay ?? 0.95, // 5% decay per occurrence maxFrequencyBoost: config.maxFrequencyBoost ?? 2.0, // Temporal Analysis enableTemporalDecay: config.enableTemporalDecay ?? true, temporalDecayRate: config.temporalDecayRate ?? 0.01, // 1% per day temporalWindow: config.temporalWindow ?? 365, // 1 year // Learning & Adaptation enableAdaptiveLearning: config.enableAdaptiveLearning ?? true, learningRate: config.learningRate ?? 0.1, confidenceThreshold: config.confidenceThreshold ?? 0.3, // Weight Management minWeight: config.minWeight ?? 0.1, maxWeight: config.maxWeight ?? 1.0, baseWeight: config.baseWeight ?? 0.5 } } protected async onInitialize(): Promise { if (this.config.enabled) { this.log('Intelligent verb scoring initialized for enhanced relationship quality') } else { this.log('Intelligent verb scoring disabled') } } /** * Get this augmentation instance for API compatibility * Used by BrainyData to access scoring methods */ getScoring(): IntelligentVerbScoringAugmentation { return this } shouldExecute(operation: string, params: any): boolean { // For addVerb, params are passed as array: [sourceId, targetId, verbType, metadata, weight] if (operation === 'addVerb' && this.config.enabled) { return Array.isArray(params) && params.length >= 3 } // For relate method, params might be an object if (operation === 'relate' && this.config.enabled) { return params.sourceId && params.targetId && params.relationType } return false } async execute( operation: string, params: any, next: () => Promise ): Promise { if (!this.shouldExecute(operation, params)) { return next() } const startTime = Date.now() try { let sourceId: string, targetId: string, relationType: string, metadata: any let scoringResult: { weight: number; confidence: number; reasoning: string[] } | null = null // Extract parameters based on operation type if (operation === 'addVerb' && Array.isArray(params)) { // addVerb params: [sourceId, targetId, verbType, metadata, weight] [sourceId, targetId, relationType, metadata] = params } else if (operation === 'relate') { // relate params might be an object sourceId = params.sourceId targetId = params.targetId relationType = params.relationType metadata = params.metadata } else { return next() } // Skip if weight is already provided explicitly if (Array.isArray(params) && params[4] !== undefined && params[4] !== null) { return next() } // Get the nouns to compute scoring const sourceNoun = await this.context?.brain.get(sourceId) const targetNoun = await this.context?.brain.get(targetId) // Compute intelligent scores with reasoning scoringResult = await this.computeVerbScores( sourceNoun, targetNoun, relationType ) // For addVerb, modify the params array if (operation === 'addVerb' && Array.isArray(params)) { // Set the weight parameter (index 4) params[4] = scoringResult.weight // Enhance metadata with scoring info params[3] = { ...params[3], intelligentScoring: { weight: scoringResult.weight, confidence: scoringResult.confidence, reasoning: scoringResult.reasoning, scoringMethod: this.getScoringMethodsUsed(), computedAt: Date.now() } } } // Execute with enhanced parameters const result = await next() // Learn from this relationship if (this.config.enableAdaptiveLearning && scoringResult) { await this.updateRelationshipLearning( sourceId, targetId, relationType, scoringResult.weight ) } // Update metrics const computationTime = Date.now() - startTime if (scoringResult) { this.updateMetrics(scoringResult.weight, computationTime) } return result } catch (error) { this.log(`Intelligent verb scoring error: ${error}`, 'error') // Fallback to original parameters return next() } } private async calculateIntelligentWeight( sourceId: string, targetId: string, relationType: string, metadata?: any ): Promise { let finalWeight = this.config.baseWeight let scoreComponents: any = {} // 1. Semantic Proximity Score if (this.config.enableSemanticScoring) { const semanticScore = await this.calculateSemanticScore(sourceId, targetId) scoreComponents.semantic = semanticScore finalWeight = finalWeight * (1 + semanticScore * this.config.semanticWeight) } // 2. Frequency Amplification Score if (this.config.enableFrequencyAmplification) { const frequencyScore = this.calculateFrequencyScore(sourceId, targetId, relationType) scoreComponents.frequency = frequencyScore finalWeight = finalWeight * (1 + frequencyScore) } // 3. Temporal Relevance Score if (this.config.enableTemporalDecay) { const temporalScore = this.calculateTemporalScore(sourceId, targetId, relationType) scoreComponents.temporal = temporalScore finalWeight = finalWeight * temporalScore } // 4. Context Awareness (from metadata) const contextScore = this.calculateContextScore(metadata) scoreComponents.context = contextScore finalWeight = finalWeight * (1 + contextScore * 0.2) // 5. Apply constraints finalWeight = Math.max(this.config.minWeight, Math.min(this.config.maxWeight, finalWeight)) // Store detailed scoring for analysis this.storeDetailedScoring(sourceId, targetId, relationType, { finalWeight, components: scoreComponents, timestamp: Date.now() }) return finalWeight } private async calculateSemanticScore(sourceId: string, targetId: string): Promise { try { // Get embeddings for both entities const sourceNoun = await this.context?.brain.get(sourceId) const targetNoun = await this.context?.brain.get(targetId) if (!sourceNoun?.vector || !targetNoun?.vector) { return 0 } // Get noun types using neural detection (taxonomy-based) const sourceType = await this.detectNounType(sourceNoun.vector) const targetType = await this.detectNounType(targetNoun.vector) // Calculate direct similarity const directSimilarity = this.calculateCosineSimilarity(sourceNoun.vector, targetNoun.vector) // Calculate taxonomy-based similarity boost const taxonomyBoost = await this.calculateTaxonomyBoost(sourceType, targetType) // Blend direct similarity with taxonomy guidance // Taxonomy provides consistency while preserving flexibility const semanticScore = directSimilarity * 0.7 + taxonomyBoost * 0.3 return Math.min(1, Math.max(0, semanticScore)) } catch (error) { return 0 } } /** * Detect noun type using neural taxonomy matching */ private async detectNounType(vector: number[]): Promise { // Use the same neural detection as addNoun for consistency if (!this.context?.brain) return 'unknown' try { // This would normally call the brain's detectNounType method // For now, simplified type detection based on vector patterns const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0)) // Heuristic type detection (would use actual taxonomy embeddings) if (magnitude > 10) return 'concept' if (magnitude > 5) return 'entity' if (magnitude > 2) return 'object' return 'item' } catch { return 'unknown' } } /** * Calculate taxonomy-based similarity boost */ private async calculateTaxonomyBoost(sourceType: string, targetType: string): Promise { // Define valid relationship patterns in taxonomy const validPatterns: Record> = { 'person': { 'concept': 0.9, 'skill': 0.85, 'organization': 0.8, 'person': 0.7 }, 'concept': { 'concept': 0.9, 'example': 0.85, 'application': 0.8 }, 'entity': { 'entity': 0.8, 'property': 0.85, 'action': 0.75 }, 'object': { 'object': 0.7, 'property': 0.8, 'location': 0.75 }, 'document': { 'topic': 0.9, 'author': 0.85, 'document': 0.7 }, 'tool': { 'output': 0.9, 'input': 0.85, 'user': 0.8 }, 'unknown': { 'unknown': 0.5 } // Fallback } // Get boost from taxonomy patterns const patterns = validPatterns[sourceType] || validPatterns['unknown'] const boost = patterns[targetType] || 0.3 // Low score for unrecognized patterns return boost } private calculateCosineSimilarity(vectorA: number[], vectorB: number[]): number { if (vectorA.length !== vectorB.length) return 0 let dotProduct = 0 let normA = 0 let normB = 0 for (let i = 0; i < vectorA.length; i++) { dotProduct += vectorA[i] * vectorB[i] normA += vectorA[i] * vectorA[i] normB += vectorB[i] * vectorB[i] } const magnitude = Math.sqrt(normA) * Math.sqrt(normB) return magnitude ? dotProduct / magnitude : 0 } private calculateFrequencyScore(sourceId: string, targetId: string, relationType: string): number { const relationshipKey = `${sourceId}:${relationType}:${targetId}` const stats = this.relationshipStats.get(relationshipKey) if (!stats || stats.count <= 1) return 0 // Frequency boost diminishes with each occurrence const frequencyBoost = Math.log(stats.count) * this.config.frequencyDecay return Math.min(this.config.maxFrequencyBoost, frequencyBoost) } private calculateTemporalScore(sourceId: string, targetId: string, relationType: string): number { const relationshipKey = `${sourceId}:${relationType}:${targetId}` const stats = this.relationshipStats.get(relationshipKey) if (!stats) return 1.0 // New relationship - full temporal score const daysSinceUpdate = (Date.now() - stats.lastUpdated) / (1000 * 60 * 60 * 24) const decayFactor = Math.pow(1 - this.config.temporalDecayRate, daysSinceUpdate) // Relationships older than temporal window get minimum score if (daysSinceUpdate > this.config.temporalWindow) { return this.config.minWeight / this.config.baseWeight } return Math.max(0.1, decayFactor) } private calculateContextScore(metadata?: any): number { if (!metadata) return 0 let contextScore = 0 // Boost for explicit importance if (metadata.importance) { contextScore += Math.min(0.5, metadata.importance) } // Boost for confidence if (metadata.confidence) { contextScore += Math.min(0.3, metadata.confidence) } // Boost for source quality if (metadata.sourceQuality) { contextScore += Math.min(0.2, metadata.sourceQuality) } return contextScore } private async updateRelationshipLearning( sourceId: string, targetId: string, relationType: string, weight: number ): Promise { const relationshipKey = `${sourceId}:${relationType}:${targetId}` let stats = this.relationshipStats.get(relationshipKey) if (!stats) { stats = { count: 0, totalWeight: 0, averageWeight: this.config.baseWeight, lastUpdated: Date.now(), semanticScore: 0, frequencyScore: 0, temporalScore: 1.0, confidenceScore: this.config.baseWeight } } // Update statistics with learning rate stats.count++ stats.totalWeight += weight stats.averageWeight = stats.averageWeight * (1 - this.config.learningRate) + weight * this.config.learningRate stats.lastUpdated = Date.now() // Update confidence based on consistency const weightVariance = Math.abs(weight - stats.averageWeight) const consistencyScore = 1 - Math.min(1, weightVariance) stats.confidenceScore = stats.confidenceScore * (1 - this.config.learningRate) + consistencyScore * this.config.learningRate this.relationshipStats.set(relationshipKey, stats) this.metrics.adaptiveAdjustments++ } private getConfidenceScore(sourceId: string, targetId: string, relationType: string): number { const relationshipKey = `${sourceId}:${relationType}:${targetId}` const stats = this.relationshipStats.get(relationshipKey) return stats ? stats.confidenceScore : this.config.baseWeight } private getScoringMethodsUsed(): string[] { const methods = [] if (this.config.enableSemanticScoring) methods.push('semantic') if (this.config.enableFrequencyAmplification) methods.push('frequency') if (this.config.enableTemporalDecay) methods.push('temporal') if (this.config.enableAdaptiveLearning) methods.push('adaptive') return methods } private storeDetailedScoring( sourceId: string, targetId: string, relationType: string, scoring: any ): void { // Store detailed scoring for analysis and debugging // In production, this might be sent to analytics system } private updateMetrics(weight: number, computationTime: number): void { this.metrics.relationshipsScored++ this.metrics.computationTimeMs = (this.metrics.computationTimeMs * (this.metrics.relationshipsScored - 1) + computationTime) / this.metrics.relationshipsScored // Update score averages (simplified) // In practice, we'd track these more precisely } /** * Get intelligent verb scoring statistics */ getStats(): ScoringMetrics & { totalRelationships: number averageConfidence: number highConfidenceRelationships: number learningEfficiency: number } { let totalConfidence = 0 let highConfidenceCount = 0 for (const stats of this.relationshipStats.values()) { totalConfidence += stats.confidenceScore if (stats.confidenceScore >= this.config.confidenceThreshold * 2) { highConfidenceCount++ } } const totalRelationships = this.relationshipStats.size const averageConfidence = totalRelationships > 0 ? totalConfidence / totalRelationships : 0 const learningEfficiency = this.metrics.adaptiveAdjustments / Math.max(1, this.metrics.relationshipsScored) return { ...this.metrics, totalRelationships, averageConfidence, highConfidenceRelationships: highConfidenceCount, learningEfficiency } } /** * Export relationship statistics for analysis */ exportRelationshipStats(): Array<{ relationship: string metrics: RelationshipMetrics }> { return Array.from(this.relationshipStats.entries()).map(([key, metrics]) => ({ relationship: key, metrics })) } /** * Import relationship statistics from previous sessions */ importRelationshipStats(stats: Array<{ relationship: string, metrics: RelationshipMetrics }>): void { for (const { relationship, metrics } of stats) { this.relationshipStats.set(relationship, metrics) } this.log(`Imported ${stats.length} relationship statistics`) } /** * Get learning statistics for monitoring and debugging * Required for BrainyData.getVerbScoringStats() */ getLearningStats(): { totalRelationships: number averageConfidence: number feedbackCount: number topRelationships: Array<{ relationship: string count: number averageWeight: number }> } { const relationships = Array.from(this.relationshipStats.entries()) const totalRelationships = relationships.length const feedbackCount = relationships.reduce((sum, [, stats]) => sum + stats.count, 0) const averageWeight = relationships.reduce((sum, [, stats]) => sum + stats.averageWeight, 0) / totalRelationships || 0 const averageConfidence = Math.min(averageWeight + 0.2, 1.0) const topRelationships = relationships .map(([key, stats]) => ({ relationship: key, count: stats.count, averageWeight: stats.averageWeight })) .sort((a, b) => b.count - a.count) .slice(0, 10) return { totalRelationships, averageConfidence, feedbackCount, topRelationships } } /** * Export learning data for backup or analysis * Required for BrainyData.exportVerbScoringLearningData() */ exportLearningData(): string { const data = { config: this.config, stats: Array.from(this.relationshipStats.entries()).map(([key, stats]) => ({ relationship: key, ...stats })), exportedAt: new Date().toISOString(), version: '1.0' } return JSON.stringify(data, null, 2) } /** * Import learning data from backup * Required for BrainyData.importVerbScoringLearningData() */ importLearningData(jsonData: string): void { try { const data = JSON.parse(jsonData) if (data.stats && Array.isArray(data.stats)) { for (const stat of data.stats) { if (stat.relationship) { this.relationshipStats.set(stat.relationship, { count: stat.count || 1, totalWeight: stat.totalWeight || stat.averageWeight || 0.5, averageWeight: stat.averageWeight || 0.5, lastUpdated: stat.lastUpdated || Date.now(), semanticScore: stat.semanticScore || 0.5, frequencyScore: stat.frequencyScore || 0.5, temporalScore: stat.temporalScore || 1.0, confidenceScore: stat.confidenceScore || 0.5 }) } } } this.log(`Imported learning data: ${this.relationshipStats.size} relationships`) } catch (error) { console.error('Failed to import learning data:', error) throw new Error(`Failed to import learning data: ${error}`) } } /** * Provide feedback on a relationship's weight * Required for BrainyData.provideVerbScoringFeedback() */ async provideFeedback( sourceId: string, targetId: string, relationType: string, feedback: number, feedbackType: 'correction' | 'validation' | 'enhancement' = 'correction' ): Promise { const key = `${sourceId}-${relationType}-${targetId}` const stats = this.relationshipStats.get(key) || { count: 0, totalWeight: 0, averageWeight: 0.5, lastUpdated: Date.now(), semanticScore: 0.5, frequencyScore: 0.5, temporalScore: 1.0, confidenceScore: 0.5 } // Update statistics based on feedback if (feedbackType === 'correction') { // Direct correction - heavily weight the feedback stats.averageWeight = stats.averageWeight * 0.3 + feedback * 0.7 } else if (feedbackType === 'validation') { // Validation - slightly adjust towards feedback stats.averageWeight = stats.averageWeight * 0.8 + feedback * 0.2 } else { // Enhancement - minor adjustment stats.averageWeight = stats.averageWeight * 0.9 + feedback * 0.1 } stats.count++ stats.totalWeight += feedback stats.lastUpdated = Date.now() this.relationshipStats.set(key, stats) this.metrics.adaptiveAdjustments++ } /** * Compute intelligent scores for a verb relationship * Used internally during verb creation */ async computeVerbScores( sourceNoun: any, targetNoun: any, relationType: string ): Promise<{ weight: number confidence: number reasoning: string[] }> { const reasoning: string[] = [] let totalScore = 0 let components = 0 // Semantic scoring if (this.config.enableSemanticScoring && sourceNoun?.vector && targetNoun?.vector) { const similarity = this.calculateCosineSimilarity(sourceNoun.vector, targetNoun.vector) const semanticScore = Math.max(similarity, this.config.semanticThreshold) totalScore += semanticScore * this.config.semanticWeight components++ reasoning.push(`Semantic similarity: ${(similarity * 100).toFixed(1)}%`) } // Frequency scoring const key = `${sourceNoun?.id}-${relationType}-${targetNoun?.id}` const stats = this.relationshipStats.get(key) if (this.config.enableFrequencyAmplification && stats) { const frequencyScore = Math.min(1 + (stats.count - 1) * 0.1, this.config.maxFrequencyBoost) totalScore += frequencyScore * 0.3 components++ reasoning.push(`Frequency boost: ${frequencyScore.toFixed(2)}x`) } // Temporal decay scoring if (this.config.enableTemporalDecay) { reasoning.push(`Temporal decay applied (rate: ${this.config.temporalDecayRate})`) } // Calculate final weight const weight = components > 0 ? Math.min(Math.max(totalScore / components, this.config.minWeight), this.config.maxWeight) : this.config.baseWeight const confidence = Math.min(weight + 0.2, 1.0) return { weight, confidence, reasoning } } protected async onShutdown(): Promise { const stats = this.getStats() this.log(`Intelligent verb scoring shutdown: ${stats.relationshipsScored} relationships scored, ${Math.round(stats.averageConfidence * 100)}% avg confidence`) } }