tasq/node_modules/@claude-flow/memory/dist/learning-bridge.js

335 lines
12 KiB
JavaScript

/**
* LearningBridge - Connects AutoMemoryBridge to NeuralLearningSystem
*
* When insights are recorded via AutoMemoryBridge, this module triggers
* neural learning trajectories so the system continuously improves from
* its own discoveries. The NeuralLearningSystem dependency is optional:
* when unavailable, all operations degrade gracefully to no-ops.
*
* @module @claude-flow/memory/learning-bridge
*/
import { EventEmitter } from 'node:events';
const DEFAULT_CONFIG = {
sonaMode: 'balanced',
confidenceDecayRate: 0.005,
accessBoostAmount: 0.03,
maxConfidence: 1.0,
minConfidence: 0.1,
ewcLambda: 2000,
consolidationThreshold: 10,
enabled: true,
};
const MS_PER_HOUR = 3_600_000;
// ===== LearningBridge =====
/**
* Connects AutoMemoryBridge insights to the NeuralLearningSystem.
*
* @example
* ```typescript
* const bridge = new LearningBridge(memoryBackend);
* await bridge.onInsightRecorded(insight, entryId);
* await bridge.onInsightAccessed(entryId);
* const result = await bridge.consolidate();
* ```
*/
export class LearningBridge extends EventEmitter {
neural = null;
backend;
config;
activeTrajectories = new Map();
stats = {
totalTrajectories: 0,
completedTrajectories: 0,
totalConsolidations: 0,
totalDecays: 0,
confidenceBoosts: 0,
totalBoostAmount: 0,
};
destroyed = false;
neuralInitPromise = null;
constructor(backend, config) {
super();
this.backend = backend;
this.config = { ...DEFAULT_CONFIG, ...config };
}
// ===== Public API =====
/**
* Notify the bridge that an insight has been recorded in AgentDB.
* Creates a learning trajectory so the neural system can track the
* insight's lifecycle.
*/
async onInsightRecorded(insight, entryId) {
if (!this.config.enabled || this.destroyed)
return;
await this.initNeural();
if (this.neural) {
try {
const trajectoryId = this.neural.beginTask(insight.summary, 'general');
this.activeTrajectories.set(entryId, trajectoryId);
this.stats.totalTrajectories++;
const embedding = this.createHashEmbedding(insight.summary);
this.neural.recordStep(trajectoryId, {
action: `record:${insight.category}`,
reward: insight.confidence,
stateEmbedding: embedding,
});
}
catch {
// Neural system failure is non-fatal
}
}
this.emit('insight:learning-started', { entryId, category: insight.category });
}
/**
* Notify the bridge that an insight entry was accessed.
* Boosts confidence in the backend and records a step in the
* trajectory if one exists.
*/
async onInsightAccessed(entryId) {
if (!this.config.enabled || this.destroyed)
return;
const entry = await this.backend.get(entryId);
if (!entry)
return;
const currentConf = entry.metadata?.confidence ?? 0.5;
const newConf = Math.min(this.config.maxConfidence, currentConf + this.config.accessBoostAmount);
await this.backend.update(entryId, {
metadata: { ...entry.metadata, confidence: newConf },
});
this.stats.confidenceBoosts++;
this.stats.totalBoostAmount += this.config.accessBoostAmount;
if (this.neural && this.activeTrajectories.has(entryId)) {
try {
const trajectoryId = this.activeTrajectories.get(entryId);
this.neural.recordStep(trajectoryId, {
action: 'access',
reward: this.config.accessBoostAmount,
});
}
catch {
// Non-fatal
}
}
this.emit('insight:accessed', { entryId, newConfidence: newConf });
}
/**
* Consolidate active trajectories by completing them in the neural system.
* Only runs when there are enough active trajectories to justify the cost.
*/
async consolidate() {
const startTime = Date.now();
const earlyResult = {
trajectoriesCompleted: 0,
patternsLearned: 0,
entriesUpdated: 0,
durationMs: 0,
};
if (!this.config.enabled || this.destroyed) {
return earlyResult;
}
if (!this.neural || this.activeTrajectories.size < this.config.consolidationThreshold) {
earlyResult.durationMs = Date.now() - startTime;
return earlyResult;
}
let completed = 0;
let patternsLearned = 0;
const toRemove = [];
const entries = Array.from(this.activeTrajectories.entries());
for (const [entryId, trajectoryId] of entries) {
try {
await this.neural.completeTask(trajectoryId, 1.0);
completed++;
patternsLearned++;
toRemove.push(entryId);
}
catch {
// Skip failed completions
}
}
for (const key of toRemove) {
this.activeTrajectories.delete(key);
}
this.stats.completedTrajectories += completed;
this.stats.totalConsolidations++;
const result = {
trajectoriesCompleted: completed,
patternsLearned,
entriesUpdated: completed,
durationMs: Date.now() - startTime,
};
this.emit('consolidation:completed', result);
return result;
}
/**
* Apply time-based confidence decay to entries in the given namespace.
* Entries not accessed for more than one hour see their confidence reduced
* proportionally to the hours elapsed, down to minConfidence.
*
* @returns number of entries whose confidence was lowered
*/
async decayConfidences(namespace) {
if (!this.config.enabled || this.destroyed)
return 0;
let entries;
try {
entries = await this.backend.query({
type: 'hybrid',
namespace,
limit: 1000,
});
}
catch {
return 0;
}
const now = Date.now();
let decayed = 0;
for (const entry of entries) {
const hoursSinceUpdate = (now - entry.updatedAt) / MS_PER_HOUR;
if (hoursSinceUpdate < 1)
continue;
const currentConf = entry.metadata?.confidence ?? 0.5;
const newConf = Math.max(this.config.minConfidence, currentConf - this.config.confidenceDecayRate * hoursSinceUpdate);
if (newConf < currentConf) {
try {
await this.backend.update(entry.id, {
metadata: { ...entry.metadata, confidence: newConf },
});
decayed++;
}
catch {
// Skip failed updates
}
}
}
this.stats.totalDecays += decayed;
return decayed;
}
/**
* Find patterns similar to the given content using the neural system.
* Returns an empty array when the neural system is unavailable.
*/
async findSimilarPatterns(content, k = 5) {
if (!this.config.enabled || this.destroyed)
return [];
await this.initNeural();
if (!this.neural)
return [];
try {
const embedding = this.createHashEmbedding(content);
const results = await this.neural.findPatterns(embedding, k);
if (!Array.isArray(results))
return [];
return results.map((r) => ({
content: r.content ?? r.data ?? '',
similarity: r.similarity ?? r.score ?? 0,
category: r.category ?? 'unknown',
confidence: r.confidence ?? r.reward ?? 0,
}));
}
catch {
return [];
}
}
/** Return aggregated learning statistics */
getStats() {
const avgBoost = this.stats.confidenceBoosts > 0
? this.stats.totalBoostAmount / this.stats.confidenceBoosts
: 0;
return {
totalTrajectories: this.stats.totalTrajectories,
completedTrajectories: this.stats.completedTrajectories,
activeTrajectories: this.activeTrajectories.size,
totalConsolidations: this.stats.totalConsolidations,
totalDecays: this.stats.totalDecays,
avgConfidenceBoost: avgBoost,
neuralAvailable: this.neural !== null,
};
}
/** Tear down the bridge. Subsequent method calls become no-ops. */
destroy() {
this.destroyed = true;
this.activeTrajectories.clear();
if (this.neural && typeof this.neural.cleanup === 'function') {
try {
this.neural.cleanup();
}
catch {
// Best-effort cleanup
}
}
this.neural = null;
this.neuralInitPromise = null;
this.removeAllListeners();
}
// ===== Private =====
/**
* Lazily attempt to load and initialize the NeuralLearningSystem.
* The promise is cached so that repeated calls do not re-attempt
* after a failure.
*/
async initNeural() {
if (this.neural)
return;
if (this.neuralInitPromise) {
await this.neuralInitPromise;
return;
}
this.neuralInitPromise = this.loadNeural();
await this.neuralInitPromise;
}
async loadNeural() {
try {
if (this.config.neuralLoader) {
// Use injected loader (test / custom integrations)
this.neural = await this.config.neuralLoader();
return;
}
const mod = await import('@claude-flow/neural');
const NeuralLearningSystem = mod.NeuralLearningSystem ?? mod.default;
if (!NeuralLearningSystem)
return;
const instance = new NeuralLearningSystem({
mode: this.config.sonaMode,
ewcLambda: this.config.ewcLambda,
});
if (typeof instance.initialize === 'function') {
await instance.initialize();
}
this.neural = instance;
}
catch {
// @claude-flow/neural not installed or failed to initialize.
// This is expected in many environments; degrade silently.
this.neural = null;
}
}
/**
* Create a deterministic hash-based embedding for content.
* This is a lightweight stand-in for a real embedding model,
* suitable for pattern matching within the neural trajectory system.
*/
createHashEmbedding(text, dimensions = 768) {
const embedding = new Float32Array(dimensions);
const normalized = text.toLowerCase().trim();
for (let i = 0; i < dimensions; i++) {
let hash = 0;
for (let j = 0; j < normalized.length; j++) {
hash = ((hash << 5) - hash + normalized.charCodeAt(j) * (i + 1)) | 0;
}
embedding[i] = (Math.sin(hash) + 1) / 2;
}
let norm = 0;
for (let i = 0; i < dimensions; i++) {
norm += embedding[i] * embedding[i];
}
norm = Math.sqrt(norm);
if (norm > 0) {
for (let i = 0; i < dimensions; i++) {
embedding[i] /= norm;
}
}
return embedding;
}
}
export default LearningBridge;
//# sourceMappingURL=learning-bridge.js.map