- Change category from 'premium' to 'core' - this is essential relationship quality improvement - Enable by default (enabled: true) instead of disabled by default - Fix contradictory documentation that claimed "enabled by default" but implemented "disabled by default" - Update reference condition to handle new default behavior properly - Update comment from "Enhancement features" to "Core relationship quality features" This aligns the implementation with the documented intent and provides better relationship quality out of the box without requiring explicit configuration.
753 lines
No EOL
25 KiB
TypeScript
753 lines
No EOL
25 KiB
TypeScript
/**
|
|
* 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
|
|
readonly metadata = {
|
|
reads: ['type', 'verb', 'source', 'target'] as string[],
|
|
writes: ['weight', 'confidence', 'intelligentScoring'] as string[]
|
|
} // Adds scoring metadata to verbs
|
|
operations = ['addVerb', 'relate'] as ('addVerb' | 'relate')[]
|
|
priority = 10 // Enhancement feature - runs after core operations
|
|
|
|
// Augmentation metadata
|
|
readonly category = 'core' as const
|
|
readonly description = 'AI-powered intelligent scoring for relationship strength analysis'
|
|
|
|
private config: Required<VerbScoringConfig>
|
|
private relationshipStats: Map<string, RelationshipMetrics> = 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
|
|
}
|
|
|
|
// Set enabled property based on config
|
|
this.enabled = this.config.enabled
|
|
}
|
|
|
|
protected async onInitialize(): Promise<void> {
|
|
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<T = any>(
|
|
operation: string,
|
|
params: any,
|
|
next: () => Promise<T>
|
|
): Promise<T> {
|
|
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<number> {
|
|
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<number> {
|
|
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<string> {
|
|
// 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<number> {
|
|
// Define valid relationship patterns in taxonomy
|
|
const validPatterns: Record<string, Record<string, number>> = {
|
|
'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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
const stats = this.getStats()
|
|
this.log(`Intelligent verb scoring shutdown: ${stats.relationshipsScored} relationships scored, ${Math.round(stats.averageConfidence * 100)}% avg confidence`)
|
|
}
|
|
} |