Initial commit: Brainy - Multi-Dimensional AI Database
Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
This commit is contained in:
commit
f8c45f2d8d
448 changed files with 103294 additions and 0 deletions
172
dist/augmentations/conduitAugmentations.d.ts
vendored
Normal file
172
dist/augmentations/conduitAugmentations.d.ts
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
import { AugmentationType, IConduitAugmentation, IWebSocketSupport, AugmentationResponse, WebSocketConnection } from '../types/augmentations.js';
|
||||
/**
|
||||
* Base class for conduit augmentations that provide data synchronization between Brainy instances
|
||||
*/
|
||||
declare abstract class BaseConduitAugmentation implements IConduitAugmentation {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
enabled: boolean;
|
||||
protected isInitialized: boolean;
|
||||
protected connections: Map<string, any>;
|
||||
constructor(name: string);
|
||||
initialize(): Promise<void>;
|
||||
shutDown(): Promise<void>;
|
||||
getStatus(): Promise<'active' | 'inactive' | 'error'>;
|
||||
abstract establishConnection(targetSystemId: string, config: Record<string, unknown>): Promise<AugmentationResponse<WebSocketConnection>>;
|
||||
abstract readData(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
abstract writeData(data: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
abstract monitorStream(streamId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
protected ensureInitialized(): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* WebSocket conduit augmentation for syncing Brainy instances using WebSockets
|
||||
*
|
||||
* This conduit is for syncing between browsers and servers, or between servers.
|
||||
* WebSockets cannot be used for direct browser-to-browser communication without a server in the middle.
|
||||
*/
|
||||
export declare class WebSocketConduitAugmentation extends BaseConduitAugmentation implements IWebSocketSupport {
|
||||
readonly description = "Conduit augmentation that syncs Brainy instances using WebSockets";
|
||||
private webSocketConnections;
|
||||
private messageCallbacks;
|
||||
constructor(name?: string);
|
||||
getType(): AugmentationType;
|
||||
/**
|
||||
* Establishes a connection to another Brainy instance
|
||||
* @param targetSystemId The URL or identifier of the target system
|
||||
* @param config Configuration options for the connection
|
||||
*/
|
||||
establishConnection(targetSystemId: string, config: Record<string, unknown>): Promise<AugmentationResponse<WebSocketConnection>>;
|
||||
/**
|
||||
* Reads data from a connected Brainy instance
|
||||
* @param query Query parameters for reading data
|
||||
* @param options Additional options
|
||||
*/
|
||||
readData(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Writes data to a connected Brainy instance
|
||||
* @param data The data to write
|
||||
* @param options Additional options
|
||||
*/
|
||||
writeData(data: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Monitors a data stream from a connected Brainy instance
|
||||
* @param streamId The ID of the stream to monitor (usually a connection ID)
|
||||
* @param callback Function to call when new data is received
|
||||
*/
|
||||
monitorStream(streamId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Establishes a WebSocket connection
|
||||
* @param url The WebSocket server URL to connect to
|
||||
* @param protocols Optional subprotocols
|
||||
*/
|
||||
connectWebSocket(url: string, protocols?: string | string[]): Promise<WebSocketConnection>;
|
||||
/**
|
||||
* Sends data through an established WebSocket connection
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param data The data to send (will be serialized if not a string)
|
||||
*/
|
||||
sendWebSocketMessage(connectionId: string, data: unknown): Promise<void>;
|
||||
/**
|
||||
* Registers a callback for incoming WebSocket messages
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param callback The function to call when a message is received
|
||||
*/
|
||||
onWebSocketMessage(connectionId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Removes a callback for incoming WebSocket messages
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param callback The function to remove from the callbacks
|
||||
*/
|
||||
offWebSocketMessage(connectionId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Closes an established WebSocket connection
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param code Optional close code
|
||||
* @param reason Optional close reason
|
||||
*/
|
||||
closeWebSocket(connectionId: string, code?: number, reason?: string): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* WebRTC conduit augmentation for syncing Brainy instances using WebRTC
|
||||
*
|
||||
* This conduit is for direct peer-to-peer syncing between browsers.
|
||||
* It is the recommended approach for browser-to-browser communication.
|
||||
*/
|
||||
export declare class WebRTCConduitAugmentation extends BaseConduitAugmentation implements IWebSocketSupport {
|
||||
readonly description = "Conduit augmentation that syncs Brainy instances using WebRTC";
|
||||
private peerConnections;
|
||||
private dataChannels;
|
||||
private webSocketConnections;
|
||||
private messageCallbacks;
|
||||
private signalServer;
|
||||
constructor(name?: string);
|
||||
getType(): AugmentationType;
|
||||
initialize(): Promise<void>;
|
||||
/**
|
||||
* Establishes a connection to another Brainy instance using WebRTC
|
||||
* @param targetSystemId The peer ID or signal server URL
|
||||
* @param config Configuration options for the connection
|
||||
*/
|
||||
establishConnection(targetSystemId: string, config: Record<string, unknown>): Promise<AugmentationResponse<WebSocketConnection>>;
|
||||
/**
|
||||
* Handles an incoming WebRTC offer
|
||||
* @param peerId The ID of the peer sending the offer
|
||||
* @param offer The SDP offer
|
||||
* @param config Configuration options
|
||||
*/
|
||||
private handleOffer;
|
||||
/**
|
||||
* Reads data from a connected Brainy instance
|
||||
* @param query Query parameters for reading data
|
||||
* @param options Additional options
|
||||
*/
|
||||
readData(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Writes data to a connected Brainy instance
|
||||
* @param data The data to write
|
||||
* @param options Additional options
|
||||
*/
|
||||
writeData(data: Record<string, unknown>, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Monitors a data stream from a connected Brainy instance
|
||||
* @param streamId The ID of the stream to monitor (usually a connection ID)
|
||||
* @param callback Function to call when new data is received
|
||||
*/
|
||||
monitorStream(streamId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Establishes a WebSocket connection (used for signaling in WebRTC)
|
||||
* @param url The WebSocket server URL to connect to
|
||||
* @param protocols Optional subprotocols
|
||||
*/
|
||||
connectWebSocket(url: string, protocols?: string | string[]): Promise<WebSocketConnection>;
|
||||
/**
|
||||
* Sends data through an established WebSocket or WebRTC connection
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param data The data to send (will be serialized if not a string)
|
||||
*/
|
||||
sendWebSocketMessage(connectionId: string, data: unknown): Promise<void>;
|
||||
/**
|
||||
* Registers a callback for incoming WebSocket or WebRTC messages
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param callback The function to call when a message is received
|
||||
*/
|
||||
onWebSocketMessage(connectionId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Removes a callback for incoming WebSocket or WebRTC messages
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param callback The function to remove from the callbacks
|
||||
*/
|
||||
offWebSocketMessage(connectionId: string, callback: (data: unknown) => void): Promise<void>;
|
||||
/**
|
||||
* Closes an established WebSocket or WebRTC connection
|
||||
* @param connectionId The identifier of the established connection
|
||||
* @param code Optional close code
|
||||
* @param reason Optional close reason
|
||||
*/
|
||||
closeWebSocket(connectionId: string, code?: number, reason?: string): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Factory function to create the appropriate conduit augmentation based on the type
|
||||
*/
|
||||
export declare function createConduitAugmentation(type: 'websocket' | 'webrtc', name?: string, options?: Record<string, unknown>): Promise<IConduitAugmentation & IWebSocketSupport>;
|
||||
export {};
|
||||
1158
dist/augmentations/conduitAugmentations.js
vendored
Normal file
1158
dist/augmentations/conduitAugmentations.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
dist/augmentations/conduitAugmentations.js.map
vendored
Normal file
1
dist/augmentations/conduitAugmentations.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
158
dist/augmentations/intelligentVerbScoring.d.ts
vendored
Normal file
158
dist/augmentations/intelligentVerbScoring.d.ts
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import { ICognitionAugmentation, AugmentationResponse } from '../types/augmentations.js';
|
||||
/**
|
||||
* Configuration options for the Intelligent Verb Scoring augmentation
|
||||
*/
|
||||
export interface IVerbScoringConfig {
|
||||
/** Enable semantic proximity scoring based on entity embeddings */
|
||||
enableSemanticScoring: boolean;
|
||||
/** Enable frequency-based weight amplification */
|
||||
enableFrequencyAmplification: boolean;
|
||||
/** Enable temporal decay for weights */
|
||||
enableTemporalDecay: boolean;
|
||||
/** Decay rate per day for temporal scoring (0-1) */
|
||||
temporalDecayRate: number;
|
||||
/** Minimum weight threshold */
|
||||
minWeight: number;
|
||||
/** Maximum weight threshold */
|
||||
maxWeight: number;
|
||||
/** Base confidence score for new relationships */
|
||||
baseConfidence: number;
|
||||
/** Learning rate for adaptive scoring (0-1) */
|
||||
learningRate: number;
|
||||
}
|
||||
/**
|
||||
* Default configuration for the Intelligent Verb Scoring augmentation
|
||||
*/
|
||||
export declare const DEFAULT_VERB_SCORING_CONFIG: IVerbScoringConfig;
|
||||
/**
|
||||
* Relationship statistics for learning and adaptation
|
||||
*/
|
||||
interface RelationshipStats {
|
||||
count: number;
|
||||
totalWeight: number;
|
||||
averageWeight: number;
|
||||
lastSeen: Date;
|
||||
firstSeen: Date;
|
||||
semanticSimilarity?: number;
|
||||
}
|
||||
/**
|
||||
* Intelligent Verb Scoring Cognition Augmentation
|
||||
*
|
||||
* Automatically generates intelligent weight and confidence scores for verb relationships
|
||||
* using semantic analysis, frequency patterns, and temporal factors.
|
||||
*/
|
||||
export declare class IntelligentVerbScoring implements ICognitionAugmentation {
|
||||
readonly name = "intelligent-verb-scoring";
|
||||
readonly description = "Automatically generates intelligent weight and confidence scores for verb relationships";
|
||||
enabled: boolean;
|
||||
private config;
|
||||
private relationshipStats;
|
||||
private brainyInstance;
|
||||
private isInitialized;
|
||||
constructor(config?: Partial<IVerbScoringConfig>);
|
||||
initialize(): Promise<void>;
|
||||
shutDown(): Promise<void>;
|
||||
getStatus(): Promise<'active' | 'inactive' | 'error'>;
|
||||
/**
|
||||
* Set reference to the BrainyData instance for accessing graph data
|
||||
*/
|
||||
setBrainyInstance(instance: any): void;
|
||||
/**
|
||||
* Main reasoning method for generating intelligent verb scores
|
||||
*/
|
||||
reason(query: string, context?: Record<string, unknown>): AugmentationResponse<{
|
||||
inference: string;
|
||||
confidence: number;
|
||||
}>;
|
||||
infer(dataSubset: Record<string, unknown>): AugmentationResponse<Record<string, unknown>>;
|
||||
executeLogic(ruleId: string, input: Record<string, unknown>): AugmentationResponse<boolean>;
|
||||
/**
|
||||
* Generate intelligent weight and confidence scores for a verb relationship
|
||||
*
|
||||
* @param sourceId - ID of the source entity
|
||||
* @param targetId - ID of the target entity
|
||||
* @param verbType - Type of the relationship
|
||||
* @param existingWeight - Existing weight if any
|
||||
* @param metadata - Additional metadata about the relationship
|
||||
* @returns Computed weight and confidence scores
|
||||
*/
|
||||
computeVerbScores(sourceId: string, targetId: string, verbType: string, existingWeight?: number, metadata?: any): Promise<{
|
||||
weight: number;
|
||||
confidence: number;
|
||||
reasoning: string[];
|
||||
}>;
|
||||
/**
|
||||
* Calculate semantic similarity between two entities using their embeddings
|
||||
*/
|
||||
private calculateSemanticScore;
|
||||
/**
|
||||
* Calculate frequency-based boost for repeated relationships
|
||||
*/
|
||||
private calculateFrequencyBoost;
|
||||
/**
|
||||
* Calculate temporal decay factor based on recency
|
||||
*/
|
||||
private calculateTemporalFactor;
|
||||
/**
|
||||
* Calculate learning-based adjustment using historical patterns
|
||||
*/
|
||||
private calculateLearningAdjustment;
|
||||
/**
|
||||
* Update relationship statistics for learning
|
||||
*/
|
||||
private updateRelationshipStats;
|
||||
/**
|
||||
* Blend two scores using a weighted average
|
||||
*/
|
||||
private blendScores;
|
||||
/**
|
||||
* Get current configuration
|
||||
*/
|
||||
getConfig(): IVerbScoringConfig;
|
||||
/**
|
||||
* Update configuration
|
||||
*/
|
||||
updateConfig(newConfig: Partial<IVerbScoringConfig>): void;
|
||||
/**
|
||||
* Get relationship statistics (for debugging/monitoring)
|
||||
*/
|
||||
getRelationshipStats(): Map<string, RelationshipStats>;
|
||||
/**
|
||||
* Clear relationship statistics
|
||||
*/
|
||||
clearStats(): void;
|
||||
/**
|
||||
* Provide feedback to improve future scoring
|
||||
* This allows the system to learn from user corrections or validation
|
||||
*
|
||||
* @param sourceId - Source entity ID
|
||||
* @param targetId - Target entity ID
|
||||
* @param verbType - Relationship type
|
||||
* @param feedbackWeight - The corrected/validated weight (0-1)
|
||||
* @param feedbackConfidence - The corrected/validated confidence (0-1)
|
||||
* @param feedbackType - Type of feedback ('correction', 'validation', 'enhancement')
|
||||
*/
|
||||
provideFeedback(sourceId: string, targetId: string, verbType: string, feedbackWeight: number, feedbackConfidence?: number, feedbackType?: 'correction' | 'validation' | 'enhancement'): Promise<void>;
|
||||
/**
|
||||
* Get learning statistics for monitoring and debugging
|
||||
*/
|
||||
getLearningStats(): {
|
||||
totalRelationships: number;
|
||||
averageConfidence: number;
|
||||
feedbackCount: number;
|
||||
topRelationships: Array<{
|
||||
relationship: string;
|
||||
count: number;
|
||||
averageWeight: number;
|
||||
}>;
|
||||
};
|
||||
/**
|
||||
* Export learning data for backup or analysis
|
||||
*/
|
||||
exportLearningData(): string;
|
||||
/**
|
||||
* Import learning data from backup
|
||||
*/
|
||||
importLearningData(jsonData: string): void;
|
||||
}
|
||||
export {};
|
||||
377
dist/augmentations/intelligentVerbScoring.js
vendored
Normal file
377
dist/augmentations/intelligentVerbScoring.js
vendored
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
import { cosineDistance } from '../utils/distance.js';
|
||||
/**
|
||||
* Default configuration for the Intelligent Verb Scoring augmentation
|
||||
*/
|
||||
export const DEFAULT_VERB_SCORING_CONFIG = {
|
||||
enableSemanticScoring: true,
|
||||
enableFrequencyAmplification: true,
|
||||
enableTemporalDecay: true,
|
||||
temporalDecayRate: 0.01, // 1% decay per day
|
||||
minWeight: 0.1,
|
||||
maxWeight: 1.0,
|
||||
baseConfidence: 0.5,
|
||||
learningRate: 0.1
|
||||
};
|
||||
/**
|
||||
* Intelligent Verb Scoring Cognition Augmentation
|
||||
*
|
||||
* Automatically generates intelligent weight and confidence scores for verb relationships
|
||||
* using semantic analysis, frequency patterns, and temporal factors.
|
||||
*/
|
||||
export class IntelligentVerbScoring {
|
||||
constructor(config = {}) {
|
||||
this.name = 'intelligent-verb-scoring';
|
||||
this.description = 'Automatically generates intelligent weight and confidence scores for verb relationships';
|
||||
this.enabled = false; // Off by default as requested
|
||||
this.relationshipStats = new Map();
|
||||
this.isInitialized = false;
|
||||
this.config = { ...DEFAULT_VERB_SCORING_CONFIG, ...config };
|
||||
}
|
||||
async initialize() {
|
||||
if (this.isInitialized)
|
||||
return;
|
||||
this.isInitialized = true;
|
||||
}
|
||||
async shutDown() {
|
||||
this.relationshipStats.clear();
|
||||
this.isInitialized = false;
|
||||
}
|
||||
async getStatus() {
|
||||
return this.enabled && this.isInitialized ? 'active' : 'inactive';
|
||||
}
|
||||
/**
|
||||
* Set reference to the BrainyData instance for accessing graph data
|
||||
*/
|
||||
setBrainyInstance(instance) {
|
||||
this.brainyInstance = instance;
|
||||
}
|
||||
/**
|
||||
* Main reasoning method for generating intelligent verb scores
|
||||
*/
|
||||
reason(query, context) {
|
||||
if (!this.enabled) {
|
||||
return {
|
||||
success: false,
|
||||
data: { inference: 'Augmentation is disabled', confidence: 0 },
|
||||
error: 'Intelligent verb scoring is disabled'
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
inference: 'Intelligent verb scoring active',
|
||||
confidence: 1.0
|
||||
}
|
||||
};
|
||||
}
|
||||
infer(dataSubset) {
|
||||
return {
|
||||
success: true,
|
||||
data: dataSubset
|
||||
};
|
||||
}
|
||||
executeLogic(ruleId, input) {
|
||||
return {
|
||||
success: true,
|
||||
data: true
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generate intelligent weight and confidence scores for a verb relationship
|
||||
*
|
||||
* @param sourceId - ID of the source entity
|
||||
* @param targetId - ID of the target entity
|
||||
* @param verbType - Type of the relationship
|
||||
* @param existingWeight - Existing weight if any
|
||||
* @param metadata - Additional metadata about the relationship
|
||||
* @returns Computed weight and confidence scores
|
||||
*/
|
||||
async computeVerbScores(sourceId, targetId, verbType, existingWeight, metadata) {
|
||||
if (!this.enabled || !this.brainyInstance) {
|
||||
return {
|
||||
weight: existingWeight ?? 0.5,
|
||||
confidence: this.config.baseConfidence,
|
||||
reasoning: ['Intelligent scoring disabled']
|
||||
};
|
||||
}
|
||||
const reasoning = [];
|
||||
let weight = existingWeight ?? 0.5;
|
||||
let confidence = this.config.baseConfidence;
|
||||
try {
|
||||
// Get relationship key for statistics
|
||||
const relationKey = `${sourceId}-${verbType}-${targetId}`;
|
||||
// Update relationship statistics
|
||||
this.updateRelationshipStats(relationKey, weight, metadata);
|
||||
// Apply semantic scoring if enabled
|
||||
if (this.config.enableSemanticScoring) {
|
||||
const semanticScore = await this.calculateSemanticScore(sourceId, targetId);
|
||||
if (semanticScore !== null) {
|
||||
weight = this.blendScores(weight, semanticScore, 0.3);
|
||||
confidence = Math.min(confidence + semanticScore * 0.2, 1.0);
|
||||
reasoning.push(`Semantic similarity: ${semanticScore.toFixed(3)}`);
|
||||
}
|
||||
}
|
||||
// Apply frequency amplification if enabled
|
||||
if (this.config.enableFrequencyAmplification) {
|
||||
const frequencyBoost = this.calculateFrequencyBoost(relationKey);
|
||||
weight = this.blendScores(weight, frequencyBoost, 0.2);
|
||||
if (frequencyBoost > 0.5) {
|
||||
confidence = Math.min(confidence + 0.1, 1.0);
|
||||
reasoning.push(`Frequency boost: ${frequencyBoost.toFixed(3)}`);
|
||||
}
|
||||
}
|
||||
// Apply temporal decay if enabled
|
||||
if (this.config.enableTemporalDecay) {
|
||||
const temporalFactor = this.calculateTemporalFactor(relationKey);
|
||||
weight *= temporalFactor;
|
||||
reasoning.push(`Temporal factor: ${temporalFactor.toFixed(3)}`);
|
||||
}
|
||||
// Apply learning adjustments
|
||||
const learningAdjustment = this.calculateLearningAdjustment(relationKey);
|
||||
weight = this.blendScores(weight, learningAdjustment, this.config.learningRate);
|
||||
// Clamp values to configured bounds
|
||||
weight = Math.max(this.config.minWeight, Math.min(this.config.maxWeight, weight));
|
||||
confidence = Math.max(0, Math.min(1, confidence));
|
||||
reasoning.push(`Final weight: ${weight.toFixed(3)}, confidence: ${confidence.toFixed(3)}`);
|
||||
return { weight, confidence, reasoning };
|
||||
}
|
||||
catch (error) {
|
||||
console.warn('Error computing verb scores:', error);
|
||||
return {
|
||||
weight: existingWeight ?? 0.5,
|
||||
confidence: this.config.baseConfidence,
|
||||
reasoning: [`Error in scoring: ${error}`]
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculate semantic similarity between two entities using their embeddings
|
||||
*/
|
||||
async calculateSemanticScore(sourceId, targetId) {
|
||||
try {
|
||||
if (!this.brainyInstance?.storage)
|
||||
return null;
|
||||
// Get noun embeddings from storage
|
||||
const sourceNoun = await this.brainyInstance.storage.getNoun(sourceId);
|
||||
const targetNoun = await this.brainyInstance.storage.getNoun(targetId);
|
||||
if (!sourceNoun?.vector || !targetNoun?.vector)
|
||||
return null;
|
||||
// Calculate cosine similarity (1 - distance)
|
||||
const distance = cosineDistance(sourceNoun.vector, targetNoun.vector);
|
||||
return Math.max(0, 1 - distance);
|
||||
}
|
||||
catch (error) {
|
||||
console.warn('Error calculating semantic score:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculate frequency-based boost for repeated relationships
|
||||
*/
|
||||
calculateFrequencyBoost(relationKey) {
|
||||
const stats = this.relationshipStats.get(relationKey);
|
||||
if (!stats || stats.count <= 1)
|
||||
return 0.5;
|
||||
// Logarithmic scaling: more occurrences = higher weight, but with diminishing returns
|
||||
const boost = Math.log(stats.count + 1) / Math.log(10); // Log base 10
|
||||
return Math.min(boost, 1.0);
|
||||
}
|
||||
/**
|
||||
* Calculate temporal decay factor based on recency
|
||||
*/
|
||||
calculateTemporalFactor(relationKey) {
|
||||
const stats = this.relationshipStats.get(relationKey);
|
||||
if (!stats)
|
||||
return 1.0;
|
||||
const daysSinceLastSeen = (Date.now() - stats.lastSeen.getTime()) / (1000 * 60 * 60 * 24);
|
||||
const decayFactor = Math.exp(-this.config.temporalDecayRate * daysSinceLastSeen);
|
||||
return Math.max(0.1, decayFactor); // Minimum 10% of original weight
|
||||
}
|
||||
/**
|
||||
* Calculate learning-based adjustment using historical patterns
|
||||
*/
|
||||
calculateLearningAdjustment(relationKey) {
|
||||
const stats = this.relationshipStats.get(relationKey);
|
||||
if (!stats || stats.count <= 1)
|
||||
return 0.5;
|
||||
// Use moving average of weights as learned baseline
|
||||
return Math.max(0, Math.min(1, stats.averageWeight));
|
||||
}
|
||||
/**
|
||||
* Update relationship statistics for learning
|
||||
*/
|
||||
updateRelationshipStats(relationKey, weight, metadata) {
|
||||
const now = new Date();
|
||||
const existing = this.relationshipStats.get(relationKey);
|
||||
if (existing) {
|
||||
// Update existing stats
|
||||
existing.count++;
|
||||
existing.totalWeight += weight;
|
||||
existing.averageWeight = existing.totalWeight / existing.count;
|
||||
existing.lastSeen = now;
|
||||
}
|
||||
else {
|
||||
// Create new stats entry
|
||||
this.relationshipStats.set(relationKey, {
|
||||
count: 1,
|
||||
totalWeight: weight,
|
||||
averageWeight: weight,
|
||||
lastSeen: now,
|
||||
firstSeen: now
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Blend two scores using a weighted average
|
||||
*/
|
||||
blendScores(score1, score2, weight2) {
|
||||
const weight1 = 1 - weight2;
|
||||
return score1 * weight1 + score2 * weight2;
|
||||
}
|
||||
/**
|
||||
* Get current configuration
|
||||
*/
|
||||
getConfig() {
|
||||
return { ...this.config };
|
||||
}
|
||||
/**
|
||||
* Update configuration
|
||||
*/
|
||||
updateConfig(newConfig) {
|
||||
this.config = { ...this.config, ...newConfig };
|
||||
}
|
||||
/**
|
||||
* Get relationship statistics (for debugging/monitoring)
|
||||
*/
|
||||
getRelationshipStats() {
|
||||
return new Map(this.relationshipStats);
|
||||
}
|
||||
/**
|
||||
* Clear relationship statistics
|
||||
*/
|
||||
clearStats() {
|
||||
this.relationshipStats.clear();
|
||||
}
|
||||
/**
|
||||
* Provide feedback to improve future scoring
|
||||
* This allows the system to learn from user corrections or validation
|
||||
*
|
||||
* @param sourceId - Source entity ID
|
||||
* @param targetId - Target entity ID
|
||||
* @param verbType - Relationship type
|
||||
* @param feedbackWeight - The corrected/validated weight (0-1)
|
||||
* @param feedbackConfidence - The corrected/validated confidence (0-1)
|
||||
* @param feedbackType - Type of feedback ('correction', 'validation', 'enhancement')
|
||||
*/
|
||||
async provideFeedback(sourceId, targetId, verbType, feedbackWeight, feedbackConfidence, feedbackType = 'correction') {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
const relationKey = `${sourceId}-${verbType}-${targetId}`;
|
||||
const existing = this.relationshipStats.get(relationKey);
|
||||
if (existing) {
|
||||
// Apply feedback with learning rate
|
||||
const newWeight = existing.averageWeight * (1 - this.config.learningRate) +
|
||||
feedbackWeight * this.config.learningRate;
|
||||
// Update the running average with feedback
|
||||
existing.totalWeight = (existing.totalWeight * existing.count + feedbackWeight) / (existing.count + 1);
|
||||
existing.averageWeight = existing.totalWeight / existing.count;
|
||||
existing.count += 1;
|
||||
existing.lastSeen = new Date();
|
||||
if (this.brainyInstance?.loggingConfig?.verbose) {
|
||||
console.log(`Feedback applied for ${relationKey}: ${feedbackType}, ` +
|
||||
`old weight: ${existing.averageWeight.toFixed(3)}, ` +
|
||||
`feedback: ${feedbackWeight.toFixed(3)}, ` +
|
||||
`new weight: ${newWeight.toFixed(3)}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create new entry with feedback as initial data
|
||||
this.relationshipStats.set(relationKey, {
|
||||
count: 1,
|
||||
totalWeight: feedbackWeight,
|
||||
averageWeight: feedbackWeight,
|
||||
lastSeen: new Date(),
|
||||
firstSeen: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get learning statistics for monitoring and debugging
|
||||
*/
|
||||
getLearningStats() {
|
||||
const relationships = Array.from(this.relationshipStats.entries());
|
||||
const totalRelationships = relationships.length;
|
||||
const feedbackCount = relationships.reduce((sum, [, stats]) => sum + stats.count, 0);
|
||||
// Calculate average confidence (approximated from weight patterns)
|
||||
const averageWeight = relationships.reduce((sum, [, stats]) => sum + stats.averageWeight, 0) / totalRelationships || 0;
|
||||
const averageConfidence = Math.min(averageWeight + 0.2, 1.0); // Heuristic: confidence typically higher than weight
|
||||
// Get top relationships by count
|
||||
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
|
||||
*/
|
||||
exportLearningData() {
|
||||
const data = {
|
||||
config: this.config,
|
||||
stats: Array.from(this.relationshipStats.entries()).map(([key, stats]) => ({
|
||||
relationship: key,
|
||||
...stats,
|
||||
firstSeen: stats.firstSeen.toISOString(),
|
||||
lastSeen: stats.lastSeen.toISOString()
|
||||
})),
|
||||
exportedAt: new Date().toISOString(),
|
||||
version: '1.0'
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
/**
|
||||
* Import learning data from backup
|
||||
*/
|
||||
importLearningData(jsonData) {
|
||||
try {
|
||||
const data = JSON.parse(jsonData);
|
||||
if (data.version !== '1.0') {
|
||||
console.warn('Learning data version mismatch, importing anyway');
|
||||
}
|
||||
// Update configuration if provided
|
||||
if (data.config) {
|
||||
this.config = { ...this.config, ...data.config };
|
||||
}
|
||||
// Import relationship statistics
|
||||
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,
|
||||
firstSeen: new Date(stat.firstSeen || Date.now()),
|
||||
lastSeen: new Date(stat.lastSeen || Date.now()),
|
||||
semanticSimilarity: stat.semanticSimilarity
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
console.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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=intelligentVerbScoring.js.map
|
||||
1
dist/augmentations/intelligentVerbScoring.js.map
vendored
Normal file
1
dist/augmentations/intelligentVerbScoring.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
72
dist/augmentations/memoryAugmentations.d.ts
vendored
Normal file
72
dist/augmentations/memoryAugmentations.d.ts
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { AugmentationType, IMemoryAugmentation, AugmentationResponse } from '../types/augmentations.js';
|
||||
import { StorageAdapter } from '../coreTypes.js';
|
||||
/**
|
||||
* Base class for memory augmentations that wrap a StorageAdapter
|
||||
*/
|
||||
declare abstract class BaseMemoryAugmentation implements IMemoryAugmentation {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
enabled: boolean;
|
||||
protected storage: StorageAdapter;
|
||||
protected isInitialized: boolean;
|
||||
constructor(name: string, storage: StorageAdapter);
|
||||
initialize(): Promise<void>;
|
||||
shutDown(): Promise<void>;
|
||||
getStatus(): Promise<'active' | 'inactive' | 'error'>;
|
||||
storeData(key: string, data: unknown, options?: Record<string, unknown>): Promise<AugmentationResponse<boolean>>;
|
||||
retrieveData(key: string, options?: Record<string, unknown>): Promise<AugmentationResponse<unknown>>;
|
||||
updateData(key: string, data: unknown, options?: Record<string, unknown>): Promise<AugmentationResponse<boolean>>;
|
||||
deleteData(key: string, options?: Record<string, unknown>): Promise<AugmentationResponse<boolean>>;
|
||||
listDataKeys(pattern?: string, options?: Record<string, unknown>): Promise<AugmentationResponse<string[]>>;
|
||||
/**
|
||||
* Searches for data in the storage using vector similarity.
|
||||
* Implements the findNearest functionality by calculating distances client-side.
|
||||
* @param query The query vector or data to search for
|
||||
* @param k Number of results to return (default: 10)
|
||||
* @param options Optional search options
|
||||
*/
|
||||
search(query: unknown, k?: number, options?: Record<string, unknown>): Promise<AugmentationResponse<Array<{
|
||||
id: string;
|
||||
score: number;
|
||||
data: unknown;
|
||||
}>>>;
|
||||
protected ensureInitialized(): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses in-memory storage
|
||||
*/
|
||||
export declare class MemoryStorageAugmentation extends BaseMemoryAugmentation {
|
||||
readonly description = "Memory augmentation that stores data in memory";
|
||||
enabled: boolean;
|
||||
constructor(name: string);
|
||||
getType(): AugmentationType;
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses file system storage
|
||||
*/
|
||||
export declare class FileSystemStorageAugmentation extends BaseMemoryAugmentation {
|
||||
readonly description = "Memory augmentation that stores data in the file system";
|
||||
enabled: boolean;
|
||||
private rootDirectory;
|
||||
constructor(name: string, rootDirectory?: string);
|
||||
initialize(): Promise<void>;
|
||||
getType(): AugmentationType;
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses OPFS (Origin Private File System) storage
|
||||
*/
|
||||
export declare class OPFSStorageAugmentation extends BaseMemoryAugmentation {
|
||||
readonly description = "Memory augmentation that stores data in the Origin Private File System";
|
||||
enabled: boolean;
|
||||
constructor(name: string);
|
||||
getType(): AugmentationType;
|
||||
}
|
||||
/**
|
||||
* Factory function to create the appropriate memory augmentation based on the environment
|
||||
*/
|
||||
export declare function createMemoryAugmentation(name: string, options?: {
|
||||
storageType?: 'memory' | 'filesystem' | 'opfs';
|
||||
rootDirectory?: string;
|
||||
requestPersistentStorage?: boolean;
|
||||
}): Promise<IMemoryAugmentation>;
|
||||
export {};
|
||||
280
dist/augmentations/memoryAugmentations.js
vendored
Normal file
280
dist/augmentations/memoryAugmentations.js
vendored
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
import { AugmentationType } from '../types/augmentations.js';
|
||||
import { MemoryStorage, OPFSStorage } from '../storage/storageFactory.js';
|
||||
// FileSystemStorage will be dynamically imported when needed to avoid fs imports in browser
|
||||
import { cosineDistance } from '../utils/distance.js';
|
||||
/**
|
||||
* Base class for memory augmentations that wrap a StorageAdapter
|
||||
*/
|
||||
class BaseMemoryAugmentation {
|
||||
constructor(name, storage) {
|
||||
this.description = 'Base memory augmentation';
|
||||
this.enabled = true;
|
||||
this.isInitialized = false;
|
||||
this.name = name;
|
||||
this.storage = storage;
|
||||
}
|
||||
async initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.storage.init();
|
||||
this.isInitialized = true;
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to initialize ${this.name}:`, error);
|
||||
throw new Error(`Failed to initialize ${this.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
async shutDown() {
|
||||
this.isInitialized = false;
|
||||
}
|
||||
async getStatus() {
|
||||
return this.isInitialized ? 'active' : 'inactive';
|
||||
}
|
||||
async storeData(key, data, options) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
await this.storage.saveMetadata(key, data);
|
||||
return { success: true, data: true };
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to store data for key ${key}:`, error);
|
||||
return {
|
||||
success: false,
|
||||
data: false,
|
||||
error: `Failed to store data: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async retrieveData(key, options) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
const data = await this.storage.getMetadata(key);
|
||||
return {
|
||||
success: true,
|
||||
data
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to retrieve data for key ${key}:`, error);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: `Failed to retrieve data: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async updateData(key, data, options) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
await this.storage.saveMetadata(key, data);
|
||||
return { success: true, data: true };
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to update data for key ${key}:`, error);
|
||||
return {
|
||||
success: false,
|
||||
data: false,
|
||||
error: `Failed to update data: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async deleteData(key, options) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
// There's no direct deleteMetadata method, so we save null
|
||||
await this.storage.saveMetadata(key, null);
|
||||
return { success: true, data: true };
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to delete data for key ${key}:`, error);
|
||||
return {
|
||||
success: false,
|
||||
data: false,
|
||||
error: `Failed to delete data: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async listDataKeys(pattern, options) {
|
||||
// This is a limitation of the current StorageAdapter interface
|
||||
// It doesn't provide a way to list all metadata keys
|
||||
// We could implement this in the future by extending the StorageAdapter interface
|
||||
return {
|
||||
success: false,
|
||||
data: [],
|
||||
error: 'listDataKeys is not supported by this storage adapter'
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Searches for data in the storage using vector similarity.
|
||||
* Implements the findNearest functionality by calculating distances client-side.
|
||||
* @param query The query vector or data to search for
|
||||
* @param k Number of results to return (default: 10)
|
||||
* @param options Optional search options
|
||||
*/
|
||||
async search(query, k = 10, options) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
// Check if query is a vector
|
||||
let queryVector;
|
||||
if (Array.isArray(query) && query.every(item => typeof item === 'number')) {
|
||||
queryVector = query;
|
||||
}
|
||||
else {
|
||||
// If query is not a vector, we can't perform vector search
|
||||
return {
|
||||
success: false,
|
||||
data: [],
|
||||
error: 'Query must be a vector (array of numbers) for vector search'
|
||||
};
|
||||
}
|
||||
// Process nodes in batches to avoid loading everything into memory
|
||||
const allResults = [];
|
||||
let hasMore = true;
|
||||
let cursor;
|
||||
while (hasMore) {
|
||||
// Get a batch of nodes
|
||||
const batchResult = await this.storage.getNouns({
|
||||
pagination: { limit: 100, cursor }
|
||||
});
|
||||
// Process this batch
|
||||
for (const noun of batchResult.items) {
|
||||
// Skip nodes that don't have a vector
|
||||
if (!noun.vector || !Array.isArray(noun.vector)) {
|
||||
continue;
|
||||
}
|
||||
// Get metadata for the node
|
||||
const metadata = await this.storage.getMetadata(noun.id);
|
||||
// Calculate distance between query vector and node vector
|
||||
const distance = cosineDistance(queryVector, noun.vector);
|
||||
// Convert distance to similarity score (1 - distance for cosine)
|
||||
// This way higher scores are better (more similar)
|
||||
const score = 1 - distance;
|
||||
allResults.push({
|
||||
id: noun.id,
|
||||
score,
|
||||
data: metadata
|
||||
});
|
||||
}
|
||||
// Update pagination state
|
||||
hasMore = batchResult.hasMore;
|
||||
cursor = batchResult.nextCursor;
|
||||
}
|
||||
// Sort results by score (descending) and take top k
|
||||
allResults.sort((a, b) => b.score - a.score);
|
||||
const topResults = allResults.slice(0, k);
|
||||
return {
|
||||
success: true,
|
||||
data: topResults
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to search in storage:`, error);
|
||||
return {
|
||||
success: false,
|
||||
data: [],
|
||||
error: `Failed to search in storage: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async ensureInitialized() {
|
||||
if (!this.isInitialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses in-memory storage
|
||||
*/
|
||||
export class MemoryStorageAugmentation extends BaseMemoryAugmentation {
|
||||
constructor(name) {
|
||||
super(name, new MemoryStorage());
|
||||
this.description = 'Memory augmentation that stores data in memory';
|
||||
this.enabled = true;
|
||||
}
|
||||
getType() {
|
||||
return AugmentationType.MEMORY;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses file system storage
|
||||
*/
|
||||
export class FileSystemStorageAugmentation extends BaseMemoryAugmentation {
|
||||
constructor(name, rootDirectory) {
|
||||
// Temporarily use MemoryStorage, will be replaced in initialize()
|
||||
super(name, new MemoryStorage());
|
||||
this.description = 'Memory augmentation that stores data in the file system';
|
||||
this.enabled = true;
|
||||
this.rootDirectory = rootDirectory || '.';
|
||||
}
|
||||
async initialize() {
|
||||
try {
|
||||
// Dynamically import FileSystemStorage
|
||||
const { FileSystemStorage } = await import('../storage/adapters/fileSystemStorage.js');
|
||||
this.storage = new FileSystemStorage(this.rootDirectory);
|
||||
await super.initialize();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to load FileSystemStorage:', error);
|
||||
throw new Error(`Failed to initialize FileSystemStorage: ${error}`);
|
||||
}
|
||||
}
|
||||
getType() {
|
||||
return AugmentationType.MEMORY;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Memory augmentation that uses OPFS (Origin Private File System) storage
|
||||
*/
|
||||
export class OPFSStorageAugmentation extends BaseMemoryAugmentation {
|
||||
constructor(name) {
|
||||
super(name, new OPFSStorage());
|
||||
this.description = 'Memory augmentation that stores data in the Origin Private File System';
|
||||
this.enabled = true;
|
||||
}
|
||||
getType() {
|
||||
return AugmentationType.MEMORY;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Factory function to create the appropriate memory augmentation based on the environment
|
||||
*/
|
||||
export async function createMemoryAugmentation(name, options = {}) {
|
||||
// If a specific storage type is requested, use that
|
||||
if (options.storageType) {
|
||||
switch (options.storageType) {
|
||||
case 'memory':
|
||||
return new MemoryStorageAugmentation(name);
|
||||
case 'filesystem':
|
||||
return new FileSystemStorageAugmentation(name, options.rootDirectory);
|
||||
case 'opfs':
|
||||
return new OPFSStorageAugmentation(name);
|
||||
}
|
||||
}
|
||||
// Otherwise, select based on environment
|
||||
// Use the global isNode variable from the environment detection
|
||||
const isNodeEnv = globalThis.__ENV__?.isNode || (typeof process !== 'undefined' &&
|
||||
process.versions != null &&
|
||||
process.versions.node != null);
|
||||
if (isNodeEnv) {
|
||||
// In Node.js, use FileSystemStorage
|
||||
return new FileSystemStorageAugmentation(name, options.rootDirectory);
|
||||
}
|
||||
else {
|
||||
// In browser, try OPFS first
|
||||
const opfsStorage = new OPFSStorage();
|
||||
if (opfsStorage.isOPFSAvailable()) {
|
||||
// Request persistent storage if specified
|
||||
if (options.requestPersistentStorage) {
|
||||
await opfsStorage.requestPersistentStorage();
|
||||
}
|
||||
return new OPFSStorageAugmentation(name);
|
||||
}
|
||||
else {
|
||||
// Fall back to memory storage
|
||||
return new MemoryStorageAugmentation(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=memoryAugmentations.js.map
|
||||
1
dist/augmentations/memoryAugmentations.js.map
vendored
Normal file
1
dist/augmentations/memoryAugmentations.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
199
dist/augmentations/neuralImport.d.ts
vendored
Normal file
199
dist/augmentations/neuralImport.d.ts
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* Neural Import Augmentation - AI-Powered Data Understanding
|
||||
*
|
||||
* 🧠 Built-in AI augmentation for intelligent data processing
|
||||
* ⚛️ Always free, always included, always enabled
|
||||
*
|
||||
* This is the default AI-powered augmentation that comes with every Brainy installation.
|
||||
* It provides intelligent data understanding, entity detection, and relationship analysis.
|
||||
*/
|
||||
import { ISenseAugmentation, AugmentationResponse } from '../types/augmentations.js';
|
||||
import { BrainyData } from '../brainyData.js';
|
||||
export interface NeuralAnalysisResult {
|
||||
detectedEntities: DetectedEntity[];
|
||||
detectedRelationships: DetectedRelationship[];
|
||||
confidence: number;
|
||||
insights: NeuralInsight[];
|
||||
}
|
||||
export interface DetectedEntity {
|
||||
originalData: any;
|
||||
nounType: string;
|
||||
confidence: number;
|
||||
suggestedId: string;
|
||||
reasoning: string;
|
||||
alternativeTypes: Array<{
|
||||
type: string;
|
||||
confidence: number;
|
||||
}>;
|
||||
}
|
||||
export interface DetectedRelationship {
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
verbType: string;
|
||||
confidence: number;
|
||||
weight: number;
|
||||
reasoning: string;
|
||||
context: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
export interface NeuralInsight {
|
||||
type: 'hierarchy' | 'cluster' | 'pattern' | 'anomaly' | 'opportunity';
|
||||
description: string;
|
||||
confidence: number;
|
||||
affectedEntities: string[];
|
||||
recommendation?: string;
|
||||
}
|
||||
export interface NeuralImportConfig {
|
||||
confidenceThreshold: number;
|
||||
enableWeights: boolean;
|
||||
skipDuplicates: boolean;
|
||||
categoryFilter?: string[];
|
||||
}
|
||||
/**
|
||||
* Neural Import SENSE Augmentation - The Brain's Perceptual System
|
||||
*/
|
||||
export declare class NeuralImportAugmentation implements ISenseAugmentation {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
enabled: boolean;
|
||||
private brainy;
|
||||
private config;
|
||||
constructor(brainy: BrainyData, config?: Partial<NeuralImportConfig>);
|
||||
initialize(): Promise<void>;
|
||||
shutDown(): Promise<void>;
|
||||
getStatus(): Promise<'active' | 'inactive' | 'error'>;
|
||||
/**
|
||||
* Process raw data into structured nouns and verbs using neural analysis
|
||||
*/
|
||||
processRawData(rawData: Buffer | string, dataType: string, options?: Record<string, unknown>): Promise<AugmentationResponse<{
|
||||
nouns: string[];
|
||||
verbs: string[];
|
||||
confidence?: number;
|
||||
insights?: Array<{
|
||||
type: string;
|
||||
description: string;
|
||||
confidence: number;
|
||||
}>;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>>;
|
||||
/**
|
||||
* Listen to real-time data feeds and process them
|
||||
*/
|
||||
listenToFeed(feedUrl: string, callback: (data: {
|
||||
nouns: string[];
|
||||
verbs: string[];
|
||||
confidence?: number;
|
||||
}) => void): Promise<void>;
|
||||
/**
|
||||
* Analyze data structure without processing (preview mode)
|
||||
*/
|
||||
analyzeStructure(rawData: Buffer | string, dataType: string, options?: Record<string, unknown>): Promise<AugmentationResponse<{
|
||||
entityTypes: Array<{
|
||||
type: string;
|
||||
count: number;
|
||||
confidence: number;
|
||||
}>;
|
||||
relationshipTypes: Array<{
|
||||
type: string;
|
||||
count: number;
|
||||
confidence: number;
|
||||
}>;
|
||||
dataQuality: {
|
||||
completeness: number;
|
||||
consistency: number;
|
||||
accuracy: number;
|
||||
};
|
||||
recommendations: string[];
|
||||
}>>;
|
||||
/**
|
||||
* Validate data compatibility with current knowledge base
|
||||
*/
|
||||
validateCompatibility(rawData: Buffer | string, dataType: string): Promise<AugmentationResponse<{
|
||||
compatible: boolean;
|
||||
issues: Array<{
|
||||
type: string;
|
||||
description: string;
|
||||
severity: 'low' | 'medium' | 'high';
|
||||
}>;
|
||||
suggestions: string[];
|
||||
}>>;
|
||||
/**
|
||||
* Get the full neural analysis result (custom method for Cortex integration)
|
||||
*/
|
||||
getNeuralAnalysis(rawData: Buffer | string, dataType: string): Promise<NeuralAnalysisResult>;
|
||||
/**
|
||||
* Parse raw data based on type
|
||||
*/
|
||||
private parseRawData;
|
||||
/**
|
||||
* Basic CSV parser
|
||||
*/
|
||||
private parseCSV;
|
||||
/**
|
||||
* Perform neural analysis on parsed data
|
||||
*/
|
||||
private performNeuralAnalysis;
|
||||
/**
|
||||
* Neural Entity Detection - The Core AI Engine
|
||||
*/
|
||||
private detectEntitiesWithNeuralAnalysis;
|
||||
/**
|
||||
* Calculate entity type confidence using AI
|
||||
*/
|
||||
private calculateEntityTypeConfidence;
|
||||
/**
|
||||
* Field-based confidence calculation
|
||||
*/
|
||||
private calculateFieldBasedConfidence;
|
||||
/**
|
||||
* Pattern-based confidence calculation
|
||||
*/
|
||||
private calculatePatternBasedConfidence;
|
||||
/**
|
||||
* Generate reasoning for entity type selection
|
||||
*/
|
||||
private generateEntityReasoning;
|
||||
/**
|
||||
* Neural Relationship Detection
|
||||
*/
|
||||
private detectRelationshipsWithNeuralAnalysis;
|
||||
/**
|
||||
* Calculate relationship confidence
|
||||
*/
|
||||
private calculateRelationshipConfidence;
|
||||
/**
|
||||
* Calculate relationship weight/strength
|
||||
*/
|
||||
private calculateRelationshipWeight;
|
||||
/**
|
||||
* Generate Neural Insights - The Intelligence Layer
|
||||
*/
|
||||
private generateNeuralInsights;
|
||||
/**
|
||||
* Helper methods for the neural system
|
||||
*/
|
||||
private extractMainText;
|
||||
private generateSmartId;
|
||||
private extractRelationshipContext;
|
||||
private calculateTypeCompatibility;
|
||||
private getVerbSpecificity;
|
||||
private getRelevantFields;
|
||||
private getMatchedPatterns;
|
||||
private pruneRelationships;
|
||||
private detectHierarchies;
|
||||
private detectClusters;
|
||||
private detectPatterns;
|
||||
private calculateOverallConfidence;
|
||||
private storeNeuralAnalysis;
|
||||
private getDataTypeFromPath;
|
||||
private generateRelationshipReasoning;
|
||||
private extractRelationshipMetadata;
|
||||
/**
|
||||
* Assess data quality metrics
|
||||
*/
|
||||
private assessDataQuality;
|
||||
/**
|
||||
* Generate recommendations based on analysis
|
||||
*/
|
||||
private generateRecommendations;
|
||||
}
|
||||
750
dist/augmentations/neuralImport.js
vendored
Normal file
750
dist/augmentations/neuralImport.js
vendored
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
/**
|
||||
* Neural Import Augmentation - AI-Powered Data Understanding
|
||||
*
|
||||
* 🧠 Built-in AI augmentation for intelligent data processing
|
||||
* ⚛️ Always free, always included, always enabled
|
||||
*
|
||||
* This is the default AI-powered augmentation that comes with every Brainy installation.
|
||||
* It provides intelligent data understanding, entity detection, and relationship analysis.
|
||||
*/
|
||||
import { NounType, VerbType } from '../types/graphTypes.js';
|
||||
import * as fs from '../universal/fs.js';
|
||||
import * as path from '../universal/path.js';
|
||||
/**
|
||||
* Neural Import SENSE Augmentation - The Brain's Perceptual System
|
||||
*/
|
||||
export class NeuralImportAugmentation {
|
||||
constructor(brainy, config = {}) {
|
||||
this.name = 'neural-import';
|
||||
this.description = 'Built-in AI-powered data understanding and entity detection';
|
||||
this.enabled = true;
|
||||
this.brainy = brainy;
|
||||
this.config = {
|
||||
confidenceThreshold: 0.7,
|
||||
enableWeights: true,
|
||||
skipDuplicates: true,
|
||||
...config
|
||||
};
|
||||
}
|
||||
async initialize() {
|
||||
// Initialize the cortex analysis system
|
||||
console.log('🧠 Neural Import augmentation initialized');
|
||||
}
|
||||
async shutDown() {
|
||||
console.log('🧠 Neural Import SENSE augmentation shut down');
|
||||
}
|
||||
async getStatus() {
|
||||
return this.enabled ? 'active' : 'inactive';
|
||||
}
|
||||
/**
|
||||
* Process raw data into structured nouns and verbs using neural analysis
|
||||
*/
|
||||
async processRawData(rawData, dataType, options) {
|
||||
try {
|
||||
// Merge options with config
|
||||
const mergedConfig = { ...this.config, ...options };
|
||||
// Parse the raw data based on type
|
||||
const parsedData = await this.parseRawData(rawData, dataType);
|
||||
// Perform neural analysis
|
||||
const analysis = await this.performNeuralAnalysis(parsedData, mergedConfig);
|
||||
// Extract nouns and verbs for the ISenseAugmentation interface
|
||||
const nouns = analysis.detectedEntities.map(entity => entity.suggestedId);
|
||||
const verbs = analysis.detectedRelationships.map(rel => `${rel.sourceId}->${rel.verbType}->${rel.targetId}`);
|
||||
// Store the full analysis for later retrieval
|
||||
await this.storeNeuralAnalysis(analysis);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
nouns,
|
||||
verbs,
|
||||
confidence: analysis.confidence,
|
||||
insights: analysis.insights.map((insight) => ({
|
||||
type: insight.type,
|
||||
description: insight.description,
|
||||
confidence: insight.confidence
|
||||
})),
|
||||
metadata: {
|
||||
detectedEntities: analysis.detectedEntities.length,
|
||||
detectedRelationships: analysis.detectedRelationships.length,
|
||||
timestamp: new Date().toISOString(),
|
||||
augmentation: 'neural-import-sense'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
data: { nouns: [], verbs: [] },
|
||||
error: error instanceof Error ? error.message : 'Neural analysis failed'
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Listen to real-time data feeds and process them
|
||||
*/
|
||||
async listenToFeed(feedUrl, callback) {
|
||||
// For file-based feeds, watch for changes
|
||||
if (feedUrl.startsWith('file://')) {
|
||||
const filePath = feedUrl.replace('file://', '');
|
||||
// Watch file for changes using Node.js fs.watch
|
||||
const fsWatch = require('fs');
|
||||
const watcher = fsWatch.watch(filePath, async (eventType) => {
|
||||
if (eventType === 'change') {
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath);
|
||||
const result = await this.processRawData(fileContent, this.getDataTypeFromPath(filePath));
|
||||
if (result.success) {
|
||||
callback({
|
||||
nouns: result.data.nouns,
|
||||
verbs: result.data.verbs,
|
||||
confidence: result.data.confidence
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Neural Import feed error:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// For other feed types, implement appropriate listeners
|
||||
console.log(`🧠 Neural Import listening to feed: ${feedUrl}`);
|
||||
}
|
||||
/**
|
||||
* Analyze data structure without processing (preview mode)
|
||||
*/
|
||||
async analyzeStructure(rawData, dataType, options) {
|
||||
try {
|
||||
// Parse the raw data
|
||||
const parsedData = await this.parseRawData(rawData, dataType);
|
||||
// Perform lightweight analysis for structure detection
|
||||
const analysis = await this.performNeuralAnalysis(parsedData, { ...this.config, ...options });
|
||||
// Summarize entity types
|
||||
const entityTypeCounts = new Map();
|
||||
analysis.detectedEntities.forEach(entity => {
|
||||
const existing = entityTypeCounts.get(entity.nounType) || { count: 0, totalConfidence: 0 };
|
||||
entityTypeCounts.set(entity.nounType, {
|
||||
count: existing.count + 1,
|
||||
totalConfidence: existing.totalConfidence + entity.confidence
|
||||
});
|
||||
});
|
||||
const entityTypes = Array.from(entityTypeCounts.entries()).map(([type, stats]) => ({
|
||||
type,
|
||||
count: stats.count,
|
||||
confidence: stats.totalConfidence / stats.count
|
||||
}));
|
||||
// Summarize relationship types
|
||||
const relationshipTypeCounts = new Map();
|
||||
analysis.detectedRelationships.forEach(rel => {
|
||||
const existing = relationshipTypeCounts.get(rel.verbType) || { count: 0, totalConfidence: 0 };
|
||||
relationshipTypeCounts.set(rel.verbType, {
|
||||
count: existing.count + 1,
|
||||
totalConfidence: existing.totalConfidence + rel.confidence
|
||||
});
|
||||
});
|
||||
const relationshipTypes = Array.from(relationshipTypeCounts.entries()).map(([type, stats]) => ({
|
||||
type,
|
||||
count: stats.count,
|
||||
confidence: stats.totalConfidence / stats.count
|
||||
}));
|
||||
// Assess data quality
|
||||
const dataQuality = this.assessDataQuality(parsedData, analysis);
|
||||
// Generate recommendations
|
||||
const recommendations = this.generateRecommendations(parsedData, analysis, entityTypes, relationshipTypes);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
entityTypes,
|
||||
relationshipTypes,
|
||||
dataQuality,
|
||||
recommendations
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
data: {
|
||||
entityTypes: [],
|
||||
relationshipTypes: [],
|
||||
dataQuality: { completeness: 0, consistency: 0, accuracy: 0 },
|
||||
recommendations: []
|
||||
},
|
||||
error: error instanceof Error ? error.message : 'Structure analysis failed'
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validate data compatibility with current knowledge base
|
||||
*/
|
||||
async validateCompatibility(rawData, dataType) {
|
||||
try {
|
||||
// Parse the raw data
|
||||
const parsedData = await this.parseRawData(rawData, dataType);
|
||||
// Perform neural analysis
|
||||
const analysis = await this.performNeuralAnalysis(parsedData);
|
||||
const issues = [];
|
||||
const suggestions = [];
|
||||
// Check for low confidence entities
|
||||
const lowConfidenceEntities = analysis.detectedEntities.filter((e) => e.confidence < 0.5);
|
||||
if (lowConfidenceEntities.length > 0) {
|
||||
issues.push({
|
||||
type: 'confidence',
|
||||
description: `${lowConfidenceEntities.length} entities have low confidence scores`,
|
||||
severity: 'medium'
|
||||
});
|
||||
suggestions.push('Consider reviewing field names and data structure for better entity detection');
|
||||
}
|
||||
// Check for missing relationships
|
||||
if (analysis.detectedRelationships.length === 0 && analysis.detectedEntities.length > 1) {
|
||||
issues.push({
|
||||
type: 'relationships',
|
||||
description: 'No relationships detected between entities',
|
||||
severity: 'low'
|
||||
});
|
||||
suggestions.push('Consider adding contextual fields that describe entity relationships');
|
||||
}
|
||||
// Check for data type compatibility
|
||||
const supportedTypes = ['json', 'csv', 'yaml', 'text'];
|
||||
if (!supportedTypes.includes(dataType.toLowerCase())) {
|
||||
issues.push({
|
||||
type: 'format',
|
||||
description: `Data type '${dataType}' may not be fully supported`,
|
||||
severity: 'high'
|
||||
});
|
||||
suggestions.push(`Convert data to one of: ${supportedTypes.join(', ')}`);
|
||||
}
|
||||
// Check for data completeness
|
||||
const incompleteEntities = analysis.detectedEntities.filter((e) => !e.originalData || Object.keys(e.originalData).length < 2);
|
||||
if (incompleteEntities.length > 0) {
|
||||
issues.push({
|
||||
type: 'completeness',
|
||||
description: `${incompleteEntities.length} entities have insufficient data`,
|
||||
severity: 'medium'
|
||||
});
|
||||
suggestions.push('Ensure each entity has multiple descriptive fields');
|
||||
}
|
||||
const compatible = issues.filter(i => i.severity === 'high').length === 0;
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
compatible,
|
||||
issues,
|
||||
suggestions
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
data: {
|
||||
compatible: false,
|
||||
issues: [{
|
||||
type: 'error',
|
||||
description: error instanceof Error ? error.message : 'Validation failed',
|
||||
severity: 'high'
|
||||
}],
|
||||
suggestions: []
|
||||
},
|
||||
error: error instanceof Error ? error.message : 'Compatibility validation failed'
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the full neural analysis result (custom method for Cortex integration)
|
||||
*/
|
||||
async getNeuralAnalysis(rawData, dataType) {
|
||||
const parsedData = await this.parseRawData(rawData, dataType);
|
||||
return await this.performNeuralAnalysis(parsedData);
|
||||
}
|
||||
/**
|
||||
* Parse raw data based on type
|
||||
*/
|
||||
async parseRawData(rawData, dataType) {
|
||||
const content = typeof rawData === 'string' ? rawData : rawData.toString('utf8');
|
||||
switch (dataType.toLowerCase()) {
|
||||
case 'json':
|
||||
const jsonData = JSON.parse(content);
|
||||
return Array.isArray(jsonData) ? jsonData : [jsonData];
|
||||
case 'csv':
|
||||
return this.parseCSV(content);
|
||||
case 'yaml':
|
||||
case 'yml':
|
||||
// For now, basic YAML support - in full implementation would use yaml parser
|
||||
return JSON.parse(content); // Placeholder
|
||||
case 'txt':
|
||||
case 'text':
|
||||
// Split text into sentences/paragraphs for analysis
|
||||
return content.split(/\n+/).filter(line => line.trim()).map(line => ({ text: line }));
|
||||
default:
|
||||
throw new Error(`Unsupported data type: ${dataType}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Basic CSV parser
|
||||
*/
|
||||
parseCSV(content) {
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
if (lines.length < 2)
|
||||
return [];
|
||||
const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, ''));
|
||||
const data = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const values = lines[i].split(',').map(v => v.trim().replace(/"/g, ''));
|
||||
const row = {};
|
||||
headers.forEach((header, index) => {
|
||||
row[header] = values[index] || '';
|
||||
});
|
||||
data.push(row);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Perform neural analysis on parsed data
|
||||
*/
|
||||
async performNeuralAnalysis(parsedData, config = this.config) {
|
||||
// Phase 1: Neural Entity Detection
|
||||
const detectedEntities = await this.detectEntitiesWithNeuralAnalysis(parsedData, config);
|
||||
// Phase 2: Neural Relationship Detection
|
||||
const detectedRelationships = await this.detectRelationshipsWithNeuralAnalysis(detectedEntities, parsedData, config);
|
||||
// Phase 3: Neural Insights Generation
|
||||
const insights = await this.generateNeuralInsights(detectedEntities, detectedRelationships);
|
||||
// Phase 4: Confidence Scoring
|
||||
const overallConfidence = this.calculateOverallConfidence(detectedEntities, detectedRelationships);
|
||||
return {
|
||||
detectedEntities,
|
||||
detectedRelationships,
|
||||
confidence: overallConfidence,
|
||||
insights
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Neural Entity Detection - The Core AI Engine
|
||||
*/
|
||||
async detectEntitiesWithNeuralAnalysis(rawData, config = this.config) {
|
||||
const entities = [];
|
||||
const nounTypes = Object.values(NounType);
|
||||
for (const [index, dataItem] of rawData.entries()) {
|
||||
const mainText = this.extractMainText(dataItem);
|
||||
const detections = [];
|
||||
// Test against all noun types using semantic similarity
|
||||
for (const nounType of nounTypes) {
|
||||
const confidence = await this.calculateEntityTypeConfidence(mainText, dataItem, nounType);
|
||||
if (confidence >= config.confidenceThreshold - 0.2) { // Allow slightly lower for alternatives
|
||||
const reasoning = await this.generateEntityReasoning(mainText, dataItem, nounType);
|
||||
detections.push({ type: nounType, confidence, reasoning });
|
||||
}
|
||||
}
|
||||
if (detections.length > 0) {
|
||||
// Sort by confidence
|
||||
detections.sort((a, b) => b.confidence - a.confidence);
|
||||
const primaryType = detections[0];
|
||||
const alternatives = detections.slice(1, 3); // Top 2 alternatives
|
||||
entities.push({
|
||||
originalData: dataItem,
|
||||
nounType: primaryType.type,
|
||||
confidence: primaryType.confidence,
|
||||
suggestedId: this.generateSmartId(dataItem, primaryType.type, index),
|
||||
reasoning: primaryType.reasoning,
|
||||
alternativeTypes: alternatives
|
||||
});
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
/**
|
||||
* Calculate entity type confidence using AI
|
||||
*/
|
||||
async calculateEntityTypeConfidence(text, data, nounType) {
|
||||
// Base semantic similarity using search
|
||||
const searchResults = await this.brainy.search(text + ' ' + nounType, 1);
|
||||
const textSimilarity = searchResults.length > 0 ? searchResults[0].score : 0.5;
|
||||
// Field-based confidence boost
|
||||
const fieldBoost = this.calculateFieldBasedConfidence(data, nounType);
|
||||
// Pattern-based confidence boost
|
||||
const patternBoost = this.calculatePatternBasedConfidence(text, data, nounType);
|
||||
// Combine confidences with weights
|
||||
const combined = (textSimilarity * 0.5) + (fieldBoost * 0.3) + (patternBoost * 0.2);
|
||||
return Math.min(combined, 1.0);
|
||||
}
|
||||
/**
|
||||
* Field-based confidence calculation
|
||||
*/
|
||||
calculateFieldBasedConfidence(data, nounType) {
|
||||
const fields = Object.keys(data);
|
||||
let boost = 0;
|
||||
// Field patterns that boost confidence for specific noun types
|
||||
const fieldPatterns = {
|
||||
[NounType.Person]: ['name', 'email', 'phone', 'age', 'firstname', 'lastname', 'employee'],
|
||||
[NounType.Organization]: ['company', 'organization', 'corp', 'inc', 'ltd', 'department', 'team'],
|
||||
[NounType.Project]: ['project', 'task', 'deadline', 'status', 'milestone', 'deliverable'],
|
||||
[NounType.Location]: ['address', 'city', 'country', 'state', 'zip', 'location', 'coordinates'],
|
||||
[NounType.Product]: ['product', 'price', 'sku', 'inventory', 'category', 'brand'],
|
||||
[NounType.Event]: ['date', 'time', 'venue', 'event', 'meeting', 'conference', 'schedule']
|
||||
};
|
||||
const relevantPatterns = fieldPatterns[nounType] || [];
|
||||
for (const field of fields) {
|
||||
for (const pattern of relevantPatterns) {
|
||||
if (field.toLowerCase().includes(pattern)) {
|
||||
boost += 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.min(boost, 0.5);
|
||||
}
|
||||
/**
|
||||
* Pattern-based confidence calculation
|
||||
*/
|
||||
calculatePatternBasedConfidence(text, data, nounType) {
|
||||
let boost = 0;
|
||||
// Content patterns that indicate entity types
|
||||
const patterns = {
|
||||
[NounType.Person]: [
|
||||
/@.*\.com/i, // Email pattern
|
||||
/\b[A-Z][a-z]+ [A-Z][a-z]+\b/, // Name pattern
|
||||
/Mr\.|Mrs\.|Dr\.|Prof\./i // Title pattern
|
||||
],
|
||||
[NounType.Organization]: [
|
||||
/\bInc\.|Corp\.|LLC\.|Ltd\./i, // Corporate suffixes
|
||||
/Company|Corporation|Enterprise/i
|
||||
],
|
||||
[NounType.Location]: [
|
||||
/\b\d{5}(-\d{4})?\b/, // ZIP code
|
||||
/Street|Ave|Road|Blvd/i
|
||||
]
|
||||
};
|
||||
const relevantPatterns = patterns[nounType] || [];
|
||||
for (const pattern of relevantPatterns) {
|
||||
if (pattern.test(text)) {
|
||||
boost += 0.15;
|
||||
}
|
||||
}
|
||||
return Math.min(boost, 0.3);
|
||||
}
|
||||
/**
|
||||
* Generate reasoning for entity type selection
|
||||
*/
|
||||
async generateEntityReasoning(text, data, nounType) {
|
||||
const reasons = [];
|
||||
// Semantic similarity reason
|
||||
const searchResults = await this.brainy.search(text + ' ' + nounType, 1);
|
||||
const similarity = searchResults.length > 0 ? searchResults[0].score : 0.5;
|
||||
if (similarity > 0.7) {
|
||||
reasons.push(`High semantic similarity (${(similarity * 100).toFixed(1)}%)`);
|
||||
}
|
||||
// Field-based reasons
|
||||
const relevantFields = this.getRelevantFields(data, nounType);
|
||||
if (relevantFields.length > 0) {
|
||||
reasons.push(`Contains ${nounType}-specific fields: ${relevantFields.join(', ')}`);
|
||||
}
|
||||
// Pattern-based reasons
|
||||
const matchedPatterns = this.getMatchedPatterns(text, data, nounType);
|
||||
if (matchedPatterns.length > 0) {
|
||||
reasons.push(`Matches ${nounType} patterns: ${matchedPatterns.join(', ')}`);
|
||||
}
|
||||
return reasons.length > 0 ? reasons.join('; ') : 'General semantic match';
|
||||
}
|
||||
/**
|
||||
* Neural Relationship Detection
|
||||
*/
|
||||
async detectRelationshipsWithNeuralAnalysis(entities, rawData, config = this.config) {
|
||||
const relationships = [];
|
||||
const verbTypes = Object.values(VerbType);
|
||||
// For each pair of entities, test relationship possibilities
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
for (let j = i + 1; j < entities.length; j++) {
|
||||
const sourceEntity = entities[i];
|
||||
const targetEntity = entities[j];
|
||||
// Extract context for relationship detection
|
||||
const context = this.extractRelationshipContext(sourceEntity.originalData, targetEntity.originalData, rawData);
|
||||
// Test all verb types
|
||||
for (const verbType of verbTypes) {
|
||||
const confidence = await this.calculateRelationshipConfidence(sourceEntity, targetEntity, verbType, context);
|
||||
if (confidence >= config.confidenceThreshold - 0.1) { // Slightly lower threshold for relationships
|
||||
const weight = config.enableWeights ?
|
||||
this.calculateRelationshipWeight(sourceEntity, targetEntity, verbType, context) :
|
||||
0.5;
|
||||
const reasoning = await this.generateRelationshipReasoning(sourceEntity, targetEntity, verbType, context);
|
||||
relationships.push({
|
||||
sourceId: sourceEntity.suggestedId,
|
||||
targetId: targetEntity.suggestedId,
|
||||
verbType,
|
||||
confidence,
|
||||
weight,
|
||||
reasoning,
|
||||
context,
|
||||
metadata: this.extractRelationshipMetadata(sourceEntity.originalData, targetEntity.originalData, verbType)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort by confidence and remove duplicates/conflicts
|
||||
return this.pruneRelationships(relationships);
|
||||
}
|
||||
/**
|
||||
* Calculate relationship confidence
|
||||
*/
|
||||
async calculateRelationshipConfidence(source, target, verbType, context) {
|
||||
// Semantic similarity between entities and verb type
|
||||
const relationshipText = `${this.extractMainText(source.originalData)} ${verbType} ${this.extractMainText(target.originalData)}`;
|
||||
const directResults = await this.brainy.search(relationshipText, 1);
|
||||
const directSimilarity = directResults.length > 0 ? directResults[0].score : 0.5;
|
||||
// Context-based similarity
|
||||
const contextResults = await this.brainy.search(context + ' ' + verbType, 1);
|
||||
const contextSimilarity = contextResults.length > 0 ? contextResults[0].score : 0.5;
|
||||
// Entity type compatibility
|
||||
const typeCompatibility = this.calculateTypeCompatibility(source.nounType, target.nounType, verbType);
|
||||
// Combine with weights
|
||||
return (directSimilarity * 0.4) + (contextSimilarity * 0.4) + (typeCompatibility * 0.2);
|
||||
}
|
||||
/**
|
||||
* Calculate relationship weight/strength
|
||||
*/
|
||||
calculateRelationshipWeight(source, target, verbType, context) {
|
||||
let weight = 0.5; // Base weight
|
||||
// Context richness (more descriptive = stronger)
|
||||
const contextWords = context.split(' ').length;
|
||||
weight += Math.min(contextWords / 20, 0.2);
|
||||
// Entity importance (higher confidence entities = stronger relationships)
|
||||
const avgEntityConfidence = (source.confidence + target.confidence) / 2;
|
||||
weight += avgEntityConfidence * 0.2;
|
||||
// Verb type specificity (more specific verbs = stronger)
|
||||
const verbSpecificity = this.getVerbSpecificity(verbType);
|
||||
weight += verbSpecificity * 0.1;
|
||||
return Math.min(weight, 1.0);
|
||||
}
|
||||
/**
|
||||
* Generate Neural Insights - The Intelligence Layer
|
||||
*/
|
||||
async generateNeuralInsights(entities, relationships) {
|
||||
const insights = [];
|
||||
// Detect hierarchies
|
||||
const hierarchies = this.detectHierarchies(relationships);
|
||||
hierarchies.forEach(hierarchy => {
|
||||
insights.push({
|
||||
type: 'hierarchy',
|
||||
description: `Detected ${hierarchy.type} hierarchy with ${hierarchy.levels} levels`,
|
||||
confidence: hierarchy.confidence,
|
||||
affectedEntities: hierarchy.entities,
|
||||
recommendation: `Consider visualizing the ${hierarchy.type} structure`
|
||||
});
|
||||
});
|
||||
// Detect clusters
|
||||
const clusters = this.detectClusters(entities, relationships);
|
||||
clusters.forEach(cluster => {
|
||||
insights.push({
|
||||
type: 'cluster',
|
||||
description: `Found cluster of ${cluster.size} ${cluster.primaryType} entities`,
|
||||
confidence: cluster.confidence,
|
||||
affectedEntities: cluster.entities,
|
||||
recommendation: `These ${cluster.primaryType}s might form a natural grouping`
|
||||
});
|
||||
});
|
||||
// Detect patterns
|
||||
const patterns = this.detectPatterns(relationships);
|
||||
patterns.forEach(pattern => {
|
||||
insights.push({
|
||||
type: 'pattern',
|
||||
description: `Common relationship pattern: ${pattern.description}`,
|
||||
confidence: pattern.confidence,
|
||||
affectedEntities: pattern.entities,
|
||||
recommendation: pattern.recommendation
|
||||
});
|
||||
});
|
||||
return insights;
|
||||
}
|
||||
/**
|
||||
* Helper methods for the neural system
|
||||
*/
|
||||
extractMainText(data) {
|
||||
// Extract the most relevant text from a data object
|
||||
const textFields = ['name', 'title', 'description', 'content', 'text', 'label'];
|
||||
for (const field of textFields) {
|
||||
if (data[field] && typeof data[field] === 'string') {
|
||||
return data[field];
|
||||
}
|
||||
}
|
||||
// Fallback: concatenate all string values
|
||||
return Object.values(data)
|
||||
.filter(v => typeof v === 'string')
|
||||
.join(' ')
|
||||
.substring(0, 200); // Limit length
|
||||
}
|
||||
generateSmartId(data, nounType, index) {
|
||||
const mainText = this.extractMainText(data);
|
||||
const cleanText = mainText.toLowerCase().replace(/[^a-z0-9]/g, '_').substring(0, 20);
|
||||
return `${nounType}_${cleanText}_${index}`;
|
||||
}
|
||||
extractRelationshipContext(source, target, allData) {
|
||||
// Extract context for relationship detection
|
||||
return [
|
||||
this.extractMainText(source),
|
||||
this.extractMainText(target),
|
||||
// Add more contextual information
|
||||
].join(' ');
|
||||
}
|
||||
calculateTypeCompatibility(sourceType, targetType, verbType) {
|
||||
// Define type compatibility matrix for relationships
|
||||
const compatibilityMatrix = {
|
||||
[NounType.Person]: {
|
||||
[NounType.Organization]: [VerbType.MemberOf, VerbType.WorksWith],
|
||||
[NounType.Project]: [VerbType.WorksWith, VerbType.Creates],
|
||||
[NounType.Person]: [VerbType.WorksWith, VerbType.Mentors, VerbType.ReportsTo]
|
||||
}
|
||||
// Add more compatibility rules
|
||||
};
|
||||
const sourceCompatibility = compatibilityMatrix[sourceType];
|
||||
if (sourceCompatibility && sourceCompatibility[targetType]) {
|
||||
return sourceCompatibility[targetType].includes(verbType) ? 1.0 : 0.3;
|
||||
}
|
||||
return 0.5; // Default compatibility
|
||||
}
|
||||
getVerbSpecificity(verbType) {
|
||||
// More specific verbs get higher scores
|
||||
const specificityScores = {
|
||||
[VerbType.RelatedTo]: 0.1, // Very generic
|
||||
[VerbType.WorksWith]: 0.7, // Specific
|
||||
[VerbType.Mentors]: 0.9, // Very specific
|
||||
[VerbType.ReportsTo]: 0.9, // Very specific
|
||||
[VerbType.Supervises]: 0.9 // Very specific
|
||||
};
|
||||
return specificityScores[verbType] || 0.5;
|
||||
}
|
||||
getRelevantFields(data, nounType) {
|
||||
// Implementation for finding relevant fields
|
||||
return [];
|
||||
}
|
||||
getMatchedPatterns(text, data, nounType) {
|
||||
// Implementation for finding matched patterns
|
||||
return [];
|
||||
}
|
||||
pruneRelationships(relationships) {
|
||||
// Remove duplicates and low-confidence relationships
|
||||
return relationships
|
||||
.sort((a, b) => b.confidence - a.confidence)
|
||||
.slice(0, 1000); // Limit to top 1000 relationships
|
||||
}
|
||||
detectHierarchies(relationships) {
|
||||
// Detect hierarchical structures
|
||||
return [];
|
||||
}
|
||||
detectClusters(entities, relationships) {
|
||||
// Detect entity clusters
|
||||
return [];
|
||||
}
|
||||
detectPatterns(relationships) {
|
||||
// Detect relationship patterns
|
||||
return [];
|
||||
}
|
||||
calculateOverallConfidence(entities, relationships) {
|
||||
if (entities.length === 0)
|
||||
return 0;
|
||||
const entityConfidence = entities.reduce((sum, e) => sum + e.confidence, 0) / entities.length;
|
||||
if (relationships.length === 0)
|
||||
return entityConfidence;
|
||||
const relationshipConfidence = relationships.reduce((sum, r) => sum + r.confidence, 0) / relationships.length;
|
||||
return (entityConfidence + relationshipConfidence) / 2;
|
||||
}
|
||||
async storeNeuralAnalysis(analysis) {
|
||||
// Store the full analysis result for later retrieval by Neural Import or other systems
|
||||
// This could be stored in the brainy instance metadata or a separate analysis store
|
||||
}
|
||||
getDataTypeFromPath(filePath) {
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
switch (ext) {
|
||||
case '.json': return 'json';
|
||||
case '.csv': return 'csv';
|
||||
case '.yaml':
|
||||
case '.yml': return 'yaml';
|
||||
case '.txt': return 'text';
|
||||
default: return 'text';
|
||||
}
|
||||
}
|
||||
async generateRelationshipReasoning(source, target, verbType, context) {
|
||||
return `Neural analysis detected ${verbType} relationship based on semantic context`;
|
||||
}
|
||||
extractRelationshipMetadata(sourceData, targetData, verbType) {
|
||||
return {
|
||||
sourceType: typeof sourceData,
|
||||
targetType: typeof targetData,
|
||||
detectedBy: 'neural-import-sense',
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Assess data quality metrics
|
||||
*/
|
||||
assessDataQuality(parsedData, analysis) {
|
||||
// Completeness: ratio of fields with data
|
||||
let totalFields = 0;
|
||||
let filledFields = 0;
|
||||
parsedData.forEach(item => {
|
||||
const fields = Object.keys(item);
|
||||
totalFields += fields.length;
|
||||
filledFields += fields.filter(field => item[field] !== null &&
|
||||
item[field] !== undefined &&
|
||||
item[field] !== '').length;
|
||||
});
|
||||
const completeness = totalFields > 0 ? filledFields / totalFields : 0;
|
||||
// Consistency: variance in field structure
|
||||
const fieldSets = parsedData.map(item => new Set(Object.keys(item)));
|
||||
const allFields = new Set(fieldSets.flatMap(set => Array.from(set)));
|
||||
let consistencyScore = 0;
|
||||
if (fieldSets.length > 0) {
|
||||
consistencyScore = Array.from(allFields).reduce((score, field) => {
|
||||
const hasField = fieldSets.filter(set => set.has(field)).length;
|
||||
return score + (hasField / fieldSets.length);
|
||||
}, 0) / allFields.size;
|
||||
}
|
||||
// Accuracy: average confidence of detected entities
|
||||
const accuracy = analysis.detectedEntities.length > 0 ?
|
||||
analysis.detectedEntities.reduce((sum, e) => sum + e.confidence, 0) / analysis.detectedEntities.length :
|
||||
0;
|
||||
return {
|
||||
completeness,
|
||||
consistency: consistencyScore,
|
||||
accuracy
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generate recommendations based on analysis
|
||||
*/
|
||||
generateRecommendations(parsedData, analysis, entityTypes, relationshipTypes) {
|
||||
const recommendations = [];
|
||||
// Low entity confidence recommendations
|
||||
const lowConfidenceEntities = entityTypes.filter(et => et.confidence < 0.7);
|
||||
if (lowConfidenceEntities.length > 0) {
|
||||
recommendations.push(`Consider improving field names for ${lowConfidenceEntities.map(e => e.type).join(', ')} entities`);
|
||||
}
|
||||
// Missing relationships recommendations
|
||||
if (relationshipTypes.length === 0 && entityTypes.length > 1) {
|
||||
recommendations.push('Add fields that describe how entities relate to each other');
|
||||
}
|
||||
// Data structure recommendations
|
||||
if (parsedData.length > 0) {
|
||||
const firstItem = parsedData[0];
|
||||
const fieldCount = Object.keys(firstItem).length;
|
||||
if (fieldCount < 3) {
|
||||
recommendations.push('Consider adding more descriptive fields to each entity');
|
||||
}
|
||||
if (fieldCount > 20) {
|
||||
recommendations.push('Consider grouping related fields or splitting complex entities');
|
||||
}
|
||||
}
|
||||
// Entity distribution recommendations
|
||||
const dominantEntityType = entityTypes.reduce((max, current) => current.count > max.count ? current : max, entityTypes[0] || { count: 0 });
|
||||
if (dominantEntityType && dominantEntityType.count > parsedData.length * 0.8) {
|
||||
recommendations.push(`Consider diversifying entity types - ${dominantEntityType.type} dominates the dataset`);
|
||||
}
|
||||
// Relationship quality recommendations
|
||||
const lowWeightRelationships = relationshipTypes.filter(rt => rt.confidence < 0.6);
|
||||
if (lowWeightRelationships.length > 0) {
|
||||
recommendations.push('Consider adding more contextual information to strengthen relationship detection');
|
||||
}
|
||||
return recommendations;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=neuralImport.js.map
|
||||
1
dist/augmentations/neuralImport.js.map
vendored
Normal file
1
dist/augmentations/neuralImport.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
167
dist/augmentations/serverSearchAugmentations.d.ts
vendored
Normal file
167
dist/augmentations/serverSearchAugmentations.d.ts
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* Server Search Augmentations
|
||||
*
|
||||
* This file implements conduit and activation augmentations for browser-server search functionality.
|
||||
* It allows Brainy to search a server-hosted instance and store results locally.
|
||||
*/
|
||||
import { AugmentationType, IActivationAugmentation, AugmentationResponse, WebSocketConnection } from '../types/augmentations.js';
|
||||
import { WebSocketConduitAugmentation } from './conduitAugmentations.js';
|
||||
import { BrainyDataInterface } from '../types/brainyDataInterface.js';
|
||||
/**
|
||||
* ServerSearchConduitAugmentation
|
||||
*
|
||||
* A specialized conduit augmentation that provides functionality for searching
|
||||
* a server-hosted Brainy instance and storing results locally.
|
||||
*/
|
||||
export declare class ServerSearchConduitAugmentation extends WebSocketConduitAugmentation {
|
||||
private localDb;
|
||||
constructor(name?: string);
|
||||
/**
|
||||
* Initialize the augmentation
|
||||
*/
|
||||
initialize(): Promise<void>;
|
||||
/**
|
||||
* Set the local Brainy instance
|
||||
* @param db The Brainy instance to use for local storage
|
||||
*/
|
||||
setLocalDb(db: BrainyDataInterface): void;
|
||||
/**
|
||||
* Get the local Brainy instance
|
||||
* @returns The local Brainy instance
|
||||
*/
|
||||
getLocalDb(): BrainyDataInterface | null;
|
||||
/**
|
||||
* Search the server-hosted Brainy instance and store results locally
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Search results
|
||||
*/
|
||||
searchServer(connectionId: string, query: string, limit?: number): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Search the local Brainy instance
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Search results
|
||||
*/
|
||||
searchLocal(query: string, limit?: number): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Search both server and local instances, combine results, and store server results locally
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Combined search results
|
||||
*/
|
||||
searchCombined(connectionId: string, query: string, limit?: number): Promise<AugmentationResponse<unknown>>;
|
||||
/**
|
||||
* Add data to both local and server instances
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param data Text or vector to add
|
||||
* @param metadata Metadata for the data
|
||||
* @returns ID of the added data
|
||||
*/
|
||||
addToBoth(connectionId: string, data: string | any[], metadata?: any): Promise<AugmentationResponse<string>>;
|
||||
}
|
||||
/**
|
||||
* ServerSearchActivationAugmentation
|
||||
*
|
||||
* An activation augmentation that provides actions for server search functionality.
|
||||
*/
|
||||
export declare class ServerSearchActivationAugmentation implements IActivationAugmentation {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
enabled: boolean;
|
||||
private isInitialized;
|
||||
private conduitAugmentation;
|
||||
private connections;
|
||||
constructor(name?: string);
|
||||
getType(): AugmentationType;
|
||||
/**
|
||||
* Initialize the augmentation
|
||||
*/
|
||||
initialize(): Promise<void>;
|
||||
/**
|
||||
* Shut down the augmentation
|
||||
*/
|
||||
shutDown(): Promise<void>;
|
||||
/**
|
||||
* Get the status of the augmentation
|
||||
*/
|
||||
getStatus(): Promise<'active' | 'inactive' | 'error'>;
|
||||
/**
|
||||
* Set the conduit augmentation to use for server search
|
||||
* @param conduit The ServerSearchConduitAugmentation to use
|
||||
*/
|
||||
setConduitAugmentation(conduit: ServerSearchConduitAugmentation): void;
|
||||
/**
|
||||
* Store a connection for later use
|
||||
* @param connectionId The ID to use for the connection
|
||||
* @param connection The WebSocket connection
|
||||
*/
|
||||
storeConnection(connectionId: string, connection: WebSocketConnection): void;
|
||||
/**
|
||||
* Get a stored connection
|
||||
* @param connectionId The ID of the connection to retrieve
|
||||
* @returns The WebSocket connection
|
||||
*/
|
||||
getConnection(connectionId: string): WebSocketConnection | undefined;
|
||||
/**
|
||||
* Trigger an action based on a processed command or internal state
|
||||
* @param actionName The name of the action to trigger
|
||||
* @param parameters Optional parameters for the action
|
||||
*/
|
||||
triggerAction(actionName: string, parameters?: Record<string, unknown>): AugmentationResponse<unknown>;
|
||||
/**
|
||||
* Handle the connectToServer action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
private handleConnectToServer;
|
||||
/**
|
||||
* Handle the searchServer action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
private handleSearchServer;
|
||||
/**
|
||||
* Handle the searchLocal action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
private handleSearchLocal;
|
||||
/**
|
||||
* Handle the searchCombined action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
private handleSearchCombined;
|
||||
/**
|
||||
* Handle the addToBoth action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
private handleAddToBoth;
|
||||
/**
|
||||
* Generates an expressive output or response from Brainy
|
||||
* @param knowledgeId The identifier of the knowledge to express
|
||||
* @param format The desired output format (e.g., 'text', 'json')
|
||||
*/
|
||||
generateOutput(knowledgeId: string, format: string): AugmentationResponse<string | Record<string, unknown>>;
|
||||
/**
|
||||
* Interacts with an external system or API
|
||||
* @param systemId The identifier of the external system
|
||||
* @param payload The data to send to the external system
|
||||
*/
|
||||
interactExternal(systemId: string, payload: Record<string, unknown>): AugmentationResponse<unknown>;
|
||||
}
|
||||
/**
|
||||
* Factory function to create server search augmentations
|
||||
* @param serverUrl The URL of the server to connect to
|
||||
* @param options Additional options
|
||||
* @returns An object containing the created augmentations
|
||||
*/
|
||||
export declare function createServerSearchAugmentations(serverUrl: string, options?: {
|
||||
conduitName?: string;
|
||||
activationName?: string;
|
||||
protocols?: string | string[];
|
||||
localDb?: BrainyDataInterface;
|
||||
}): Promise<{
|
||||
conduit: ServerSearchConduitAugmentation;
|
||||
activation: ServerSearchActivationAugmentation;
|
||||
connection: WebSocketConnection;
|
||||
}>;
|
||||
531
dist/augmentations/serverSearchAugmentations.js
vendored
Normal file
531
dist/augmentations/serverSearchAugmentations.js
vendored
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
/**
|
||||
* Server Search Augmentations
|
||||
*
|
||||
* This file implements conduit and activation augmentations for browser-server search functionality.
|
||||
* It allows Brainy to search a server-hosted instance and store results locally.
|
||||
*/
|
||||
import { AugmentationType } from '../types/augmentations.js';
|
||||
import { WebSocketConduitAugmentation } from './conduitAugmentations.js';
|
||||
/**
|
||||
* ServerSearchConduitAugmentation
|
||||
*
|
||||
* A specialized conduit augmentation that provides functionality for searching
|
||||
* a server-hosted Brainy instance and storing results locally.
|
||||
*/
|
||||
export class ServerSearchConduitAugmentation extends WebSocketConduitAugmentation {
|
||||
constructor(name = 'server-search-conduit') {
|
||||
super(name);
|
||||
this.localDb = null;
|
||||
// this.description = 'Conduit augmentation for server-hosted Brainy search'
|
||||
}
|
||||
/**
|
||||
* Initialize the augmentation
|
||||
*/
|
||||
async initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Initialize the base conduit
|
||||
await super.initialize();
|
||||
// Local DB must be set before initialization
|
||||
if (!this.localDb) {
|
||||
throw new Error('Local database not set. Call setLocalDb before initializing.');
|
||||
}
|
||||
this.isInitialized = true;
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to initialize ${this.name}:`, error);
|
||||
throw new Error(`Failed to initialize ${this.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the local Brainy instance
|
||||
* @param db The Brainy instance to use for local storage
|
||||
*/
|
||||
setLocalDb(db) {
|
||||
this.localDb = db;
|
||||
}
|
||||
/**
|
||||
* Get the local Brainy instance
|
||||
* @returns The local Brainy instance
|
||||
*/
|
||||
getLocalDb() {
|
||||
return this.localDb;
|
||||
}
|
||||
/**
|
||||
* Search the server-hosted Brainy instance and store results locally
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Search results
|
||||
*/
|
||||
async searchServer(connectionId, query, limit = 10) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
// Create a search request
|
||||
const readResult = await this.readData({
|
||||
connectionId,
|
||||
query: {
|
||||
type: 'search',
|
||||
query,
|
||||
limit
|
||||
}
|
||||
});
|
||||
if (readResult.success && readResult.data) {
|
||||
const searchResults = readResult.data;
|
||||
// Store the results in the local Brainy instance
|
||||
if (this.localDb) {
|
||||
for (const result of searchResults) {
|
||||
// Check if the noun already exists in the local database
|
||||
const existingNoun = await this.localDb.get(result.id);
|
||||
if (!existingNoun) {
|
||||
// Add the noun to the local database
|
||||
await this.localDb.add(result.vector, result.metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: searchResults
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: readResult.error || 'Unknown error searching server'
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error searching server:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: `Error searching server: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Search the local Brainy instance
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Search results
|
||||
*/
|
||||
async searchLocal(query, limit = 10) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
if (!this.localDb) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'Local database not initialized'
|
||||
};
|
||||
}
|
||||
const results = await this.localDb.searchText(query, limit);
|
||||
return {
|
||||
success: true,
|
||||
data: results
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error searching local database:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: `Error searching local database: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Search both server and local instances, combine results, and store server results locally
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param query The search query
|
||||
* @param limit Maximum number of results to return
|
||||
* @returns Combined search results
|
||||
*/
|
||||
async searchCombined(connectionId, query, limit = 10) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
// Search local first
|
||||
const localSearchResult = await this.searchLocal(query, limit);
|
||||
if (!localSearchResult.success) {
|
||||
return localSearchResult;
|
||||
}
|
||||
const localResults = localSearchResult.data;
|
||||
// If we have enough local results, return them
|
||||
if (localResults.length >= limit) {
|
||||
return localSearchResult;
|
||||
}
|
||||
// Otherwise, search server for additional results
|
||||
const serverSearchResult = await this.searchServer(connectionId, query, limit - localResults.length);
|
||||
if (!serverSearchResult.success) {
|
||||
// If server search fails, return local results
|
||||
return localSearchResult;
|
||||
}
|
||||
const serverResults = serverSearchResult.data;
|
||||
// Combine results, removing duplicates
|
||||
const combinedResults = [...localResults];
|
||||
const localIds = new Set(localResults.map((r) => r.id));
|
||||
for (const result of serverResults) {
|
||||
if (!localIds.has(result.id)) {
|
||||
combinedResults.push(result);
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: combinedResults
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error performing combined search:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: `Error performing combined search: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add data to both local and server instances
|
||||
* @param connectionId The ID of the established connection
|
||||
* @param data Text or vector to add
|
||||
* @param metadata Metadata for the data
|
||||
* @returns ID of the added data
|
||||
*/
|
||||
async addToBoth(connectionId, data, metadata = {}) {
|
||||
await this.ensureInitialized();
|
||||
try {
|
||||
if (!this.localDb) {
|
||||
return {
|
||||
success: false,
|
||||
data: '',
|
||||
error: 'Local database not initialized'
|
||||
};
|
||||
}
|
||||
// Add to local first
|
||||
const id = await this.localDb.add(data, metadata);
|
||||
// Get the vector and metadata
|
||||
const noun = (await this.localDb.get(id));
|
||||
if (!noun) {
|
||||
return {
|
||||
success: false,
|
||||
data: '',
|
||||
error: 'Failed to retrieve newly created noun'
|
||||
};
|
||||
}
|
||||
// Add to server
|
||||
const writeResult = await this.writeData({
|
||||
connectionId,
|
||||
data: {
|
||||
type: 'addNoun',
|
||||
vector: noun.vector,
|
||||
metadata: noun.metadata
|
||||
}
|
||||
});
|
||||
if (!writeResult.success) {
|
||||
return {
|
||||
success: true,
|
||||
data: id,
|
||||
error: `Added locally but failed to add to server: ${writeResult.error}`
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: id
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error adding data to both:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: '',
|
||||
error: `Error adding data to both: ${error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ServerSearchActivationAugmentation
|
||||
*
|
||||
* An activation augmentation that provides actions for server search functionality.
|
||||
*/
|
||||
export class ServerSearchActivationAugmentation {
|
||||
constructor(name = 'server-search-activation') {
|
||||
this.enabled = true;
|
||||
this.isInitialized = false;
|
||||
this.conduitAugmentation = null;
|
||||
this.connections = new Map();
|
||||
this.name = name;
|
||||
this.description = 'Activation augmentation for server-hosted Brainy search';
|
||||
}
|
||||
getType() {
|
||||
return AugmentationType.ACTIVATION;
|
||||
}
|
||||
/**
|
||||
* Initialize the augmentation
|
||||
*/
|
||||
async initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
}
|
||||
/**
|
||||
* Shut down the augmentation
|
||||
*/
|
||||
async shutDown() {
|
||||
this.isInitialized = false;
|
||||
}
|
||||
/**
|
||||
* Get the status of the augmentation
|
||||
*/
|
||||
async getStatus() {
|
||||
return this.isInitialized ? 'active' : 'inactive';
|
||||
}
|
||||
/**
|
||||
* Set the conduit augmentation to use for server search
|
||||
* @param conduit The ServerSearchConduitAugmentation to use
|
||||
*/
|
||||
setConduitAugmentation(conduit) {
|
||||
this.conduitAugmentation = conduit;
|
||||
}
|
||||
/**
|
||||
* Store a connection for later use
|
||||
* @param connectionId The ID to use for the connection
|
||||
* @param connection The WebSocket connection
|
||||
*/
|
||||
storeConnection(connectionId, connection) {
|
||||
this.connections.set(connectionId, connection);
|
||||
}
|
||||
/**
|
||||
* Get a stored connection
|
||||
* @param connectionId The ID of the connection to retrieve
|
||||
* @returns The WebSocket connection
|
||||
*/
|
||||
getConnection(connectionId) {
|
||||
return this.connections.get(connectionId);
|
||||
}
|
||||
/**
|
||||
* Trigger an action based on a processed command or internal state
|
||||
* @param actionName The name of the action to trigger
|
||||
* @param parameters Optional parameters for the action
|
||||
*/
|
||||
triggerAction(actionName, parameters) {
|
||||
if (!this.conduitAugmentation) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'Conduit augmentation not set'
|
||||
};
|
||||
}
|
||||
// Handle different actions
|
||||
switch (actionName) {
|
||||
case 'connectToServer':
|
||||
return this.handleConnectToServer(parameters || {});
|
||||
case 'searchServer':
|
||||
return this.handleSearchServer(parameters || {});
|
||||
case 'searchLocal':
|
||||
return this.handleSearchLocal(parameters || {});
|
||||
case 'searchCombined':
|
||||
return this.handleSearchCombined(parameters || {});
|
||||
case 'addToBoth':
|
||||
return this.handleAddToBoth(parameters || {});
|
||||
default:
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: `Unknown action: ${actionName}`
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handle the connectToServer action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
handleConnectToServer(parameters) {
|
||||
const serverUrl = parameters.serverUrl;
|
||||
const protocols = parameters.protocols;
|
||||
if (!serverUrl) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'serverUrl parameter is required'
|
||||
};
|
||||
}
|
||||
// Return a promise that will be resolved when the connection is established
|
||||
return {
|
||||
success: true,
|
||||
data: this.conduitAugmentation.establishConnection(serverUrl, {
|
||||
protocols
|
||||
})
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Handle the searchServer action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
handleSearchServer(parameters) {
|
||||
const connectionId = parameters.connectionId;
|
||||
const query = parameters.query;
|
||||
const limit = parameters.limit || 10;
|
||||
if (!connectionId) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'connectionId parameter is required'
|
||||
};
|
||||
}
|
||||
if (!query) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'query parameter is required'
|
||||
};
|
||||
}
|
||||
// Return a promise that will be resolved when the search is complete
|
||||
return {
|
||||
success: true,
|
||||
data: this.conduitAugmentation.searchServer(connectionId, query, limit)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Handle the searchLocal action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
handleSearchLocal(parameters) {
|
||||
const query = parameters.query;
|
||||
const limit = parameters.limit || 10;
|
||||
if (!query) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'query parameter is required'
|
||||
};
|
||||
}
|
||||
// Return a promise that will be resolved when the search is complete
|
||||
return {
|
||||
success: true,
|
||||
data: this.conduitAugmentation.searchLocal(query, limit)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Handle the searchCombined action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
handleSearchCombined(parameters) {
|
||||
const connectionId = parameters.connectionId;
|
||||
const query = parameters.query;
|
||||
const limit = parameters.limit || 10;
|
||||
if (!connectionId) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'connectionId parameter is required'
|
||||
};
|
||||
}
|
||||
if (!query) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'query parameter is required'
|
||||
};
|
||||
}
|
||||
// Return a promise that will be resolved when the search is complete
|
||||
return {
|
||||
success: true,
|
||||
data: this.conduitAugmentation.searchCombined(connectionId, query, limit)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Handle the addToBoth action
|
||||
* @param parameters Action parameters
|
||||
*/
|
||||
handleAddToBoth(parameters) {
|
||||
const connectionId = parameters.connectionId;
|
||||
const data = parameters.data;
|
||||
const metadata = parameters.metadata || {};
|
||||
if (!connectionId) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'connectionId parameter is required'
|
||||
};
|
||||
}
|
||||
if (!data) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'data parameter is required'
|
||||
};
|
||||
}
|
||||
// Return a promise that will be resolved when the add is complete
|
||||
return {
|
||||
success: true,
|
||||
data: this.conduitAugmentation.addToBoth(connectionId, data, metadata)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generates an expressive output or response from Brainy
|
||||
* @param knowledgeId The identifier of the knowledge to express
|
||||
* @param format The desired output format (e.g., 'text', 'json')
|
||||
*/
|
||||
generateOutput(knowledgeId, format) {
|
||||
// This method is not used for server search functionality
|
||||
return {
|
||||
success: false,
|
||||
data: '',
|
||||
error: 'generateOutput is not implemented for ServerSearchActivationAugmentation'
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Interacts with an external system or API
|
||||
* @param systemId The identifier of the external system
|
||||
* @param payload The data to send to the external system
|
||||
*/
|
||||
interactExternal(systemId, payload) {
|
||||
// This method is not used for server search functionality
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'interactExternal is not implemented for ServerSearchActivationAugmentation'
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Factory function to create server search augmentations
|
||||
* @param serverUrl The URL of the server to connect to
|
||||
* @param options Additional options
|
||||
* @returns An object containing the created augmentations
|
||||
*/
|
||||
export async function createServerSearchAugmentations(serverUrl, options = {}) {
|
||||
// Create the conduit augmentation
|
||||
const conduit = new ServerSearchConduitAugmentation(options.conduitName);
|
||||
await conduit.initialize();
|
||||
// Set the local database if provided
|
||||
if (options.localDb) {
|
||||
conduit.setLocalDb(options.localDb);
|
||||
}
|
||||
// Create the activation augmentation
|
||||
const activation = new ServerSearchActivationAugmentation(options.activationName);
|
||||
await activation.initialize();
|
||||
// Link the augmentations
|
||||
activation.setConduitAugmentation(conduit);
|
||||
// Connect to the server
|
||||
const connectionResult = await conduit.establishConnection(serverUrl, {
|
||||
protocols: options.protocols
|
||||
});
|
||||
if (!connectionResult.success || !connectionResult.data) {
|
||||
throw new Error(`Failed to connect to server: ${connectionResult.error}`);
|
||||
}
|
||||
const connection = connectionResult.data;
|
||||
// Store the connection in the activation augmentation
|
||||
activation.storeConnection(connection.connectionId, connection);
|
||||
return {
|
||||
conduit,
|
||||
activation,
|
||||
connection
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=serverSearchAugmentations.js.map
|
||||
1
dist/augmentations/serverSearchAugmentations.js.map
vendored
Normal file
1
dist/augmentations/serverSearchAugmentations.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue