tasq/node_modules/agentic-flow/dist/intelligence/IntelligenceStore.js

364 lines
11 KiB
JavaScript

/**
* IntelligenceStore - SQLite persistence for RuVector intelligence layer
*
* Cross-platform (Linux, macOS, Windows) persistent storage for:
* - Learning trajectories
* - Routing patterns
* - SONA adaptations
* - HNSW vectors
*/
import Database from 'better-sqlite3';
import { existsSync, mkdirSync } from 'fs';
import { dirname, join } from 'path';
import { homedir } from 'os';
export class IntelligenceStore {
db;
static instance = null;
constructor(dbPath) {
// Ensure directory exists
const dir = dirname(dbPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
this.db = new Database(dbPath);
this.db.pragma('journal_mode = WAL'); // Better concurrent access
this.db.pragma('synchronous = NORMAL'); // Good balance of speed/safety
this.initSchema();
}
/**
* Get singleton instance
*/
static getInstance(dbPath) {
if (!IntelligenceStore.instance) {
const path = dbPath || IntelligenceStore.getDefaultPath();
IntelligenceStore.instance = new IntelligenceStore(path);
}
return IntelligenceStore.instance;
}
/**
* Get default database path (cross-platform)
*/
static getDefaultPath() {
// Check for project-local .agentic-flow directory first
const localPath = join(process.cwd(), '.agentic-flow', 'intelligence.db');
const localDir = dirname(localPath);
if (existsSync(localDir)) {
return localPath;
}
// Fall back to home directory
const homeDir = homedir();
return join(homeDir, '.agentic-flow', 'intelligence.db');
}
/**
* Initialize database schema
*/
initSchema() {
this.db.exec(`
-- Trajectories table
CREATE TABLE IF NOT EXISTS trajectories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_description TEXT NOT NULL,
agent TEXT NOT NULL,
steps INTEGER DEFAULT 0,
outcome TEXT DEFAULT 'partial',
start_time INTEGER NOT NULL,
end_time INTEGER,
metadata TEXT,
created_at INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Patterns table (for ReasoningBank)
CREATE TABLE IF NOT EXISTS patterns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_type TEXT NOT NULL,
approach TEXT NOT NULL,
embedding BLOB,
similarity REAL DEFAULT 0,
usage_count INTEGER DEFAULT 0,
success_rate REAL DEFAULT 0,
created_at INTEGER DEFAULT (strftime('%s', 'now')),
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Routings table
CREATE TABLE IF NOT EXISTS routings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task TEXT NOT NULL,
recommended_agent TEXT NOT NULL,
confidence REAL NOT NULL,
latency_ms INTEGER NOT NULL,
was_successful INTEGER DEFAULT 0,
timestamp INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Stats table (single row)
CREATE TABLE IF NOT EXISTS stats (
id INTEGER PRIMARY KEY CHECK (id = 1),
total_trajectories INTEGER DEFAULT 0,
successful_trajectories INTEGER DEFAULT 0,
total_routings INTEGER DEFAULT 0,
successful_routings INTEGER DEFAULT 0,
total_patterns INTEGER DEFAULT 0,
sona_adaptations INTEGER DEFAULT 0,
hnsw_queries INTEGER DEFAULT 0,
last_updated INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Initialize stats row if not exists
INSERT OR IGNORE INTO stats (id) VALUES (1);
-- Indexes for faster queries
CREATE INDEX IF NOT EXISTS idx_trajectories_agent ON trajectories(agent);
CREATE INDEX IF NOT EXISTS idx_trajectories_outcome ON trajectories(outcome);
CREATE INDEX IF NOT EXISTS idx_patterns_task_type ON patterns(task_type);
CREATE INDEX IF NOT EXISTS idx_routings_agent ON routings(recommended_agent);
CREATE INDEX IF NOT EXISTS idx_routings_timestamp ON routings(timestamp);
`);
}
// ============ Trajectory Methods ============
/**
* Start a new trajectory
*/
startTrajectory(taskDescription, agent) {
const stmt = this.db.prepare(`
INSERT INTO trajectories (task_description, agent, start_time)
VALUES (?, ?, ?)
`);
const result = stmt.run(taskDescription, agent, Date.now());
this.incrementStat('total_trajectories');
return result.lastInsertRowid;
}
/**
* Add step to trajectory
*/
addTrajectoryStep(trajectoryId) {
const stmt = this.db.prepare(`
UPDATE trajectories SET steps = steps + 1 WHERE id = ?
`);
stmt.run(trajectoryId);
}
/**
* End trajectory with outcome
*/
endTrajectory(trajectoryId, outcome, metadata) {
const stmt = this.db.prepare(`
UPDATE trajectories
SET outcome = ?, end_time = ?, metadata = ?
WHERE id = ?
`);
stmt.run(outcome, Date.now(), metadata ? JSON.stringify(metadata) : null, trajectoryId);
if (outcome === 'success') {
this.incrementStat('successful_trajectories');
}
}
/**
* Get active trajectories (no end_time)
*/
getActiveTrajectories() {
const stmt = this.db.prepare(`
SELECT * FROM trajectories WHERE end_time IS NULL
`);
return stmt.all();
}
/**
* Get recent trajectories
*/
getRecentTrajectories(limit = 10) {
const stmt = this.db.prepare(`
SELECT * FROM trajectories ORDER BY start_time DESC LIMIT ?
`);
return stmt.all(limit);
}
// ============ Pattern Methods ============
/**
* Store a pattern
*/
storePattern(taskType, approach, embedding) {
const stmt = this.db.prepare(`
INSERT INTO patterns (task_type, approach, embedding)
VALUES (?, ?, ?)
`);
const embeddingBuffer = embedding ? Buffer.from(embedding.buffer) : null;
const result = stmt.run(taskType, approach, embeddingBuffer);
this.incrementStat('total_patterns');
return result.lastInsertRowid;
}
/**
* Update pattern usage
*/
updatePatternUsage(patternId, wasSuccessful) {
const stmt = this.db.prepare(`
UPDATE patterns
SET usage_count = usage_count + 1,
success_rate = (success_rate * usage_count + ?) / (usage_count + 1),
updated_at = strftime('%s', 'now')
WHERE id = ?
`);
stmt.run(wasSuccessful ? 1 : 0, patternId);
}
/**
* Find patterns by task type
*/
findPatterns(taskType, limit = 5) {
const stmt = this.db.prepare(`
SELECT * FROM patterns
WHERE task_type LIKE ?
ORDER BY success_rate DESC, usage_count DESC
LIMIT ?
`);
return stmt.all(`%${taskType}%`, limit);
}
// ============ Routing Methods ============
/**
* Record a routing decision
*/
recordRouting(task, recommendedAgent, confidence, latencyMs) {
const stmt = this.db.prepare(`
INSERT INTO routings (task, recommended_agent, confidence, latency_ms)
VALUES (?, ?, ?, ?)
`);
const result = stmt.run(task, recommendedAgent, confidence, latencyMs);
this.incrementStat('total_routings');
return result.lastInsertRowid;
}
/**
* Update routing outcome
*/
updateRoutingOutcome(routingId, wasSuccessful) {
const stmt = this.db.prepare(`
UPDATE routings SET was_successful = ? WHERE id = ?
`);
stmt.run(wasSuccessful ? 1 : 0, routingId);
if (wasSuccessful) {
this.incrementStat('successful_routings');
}
}
/**
* Get routing accuracy for an agent
*/
getAgentAccuracy(agent) {
const stmt = this.db.prepare(`
SELECT
COUNT(*) as total,
SUM(was_successful) as successful
FROM routings
WHERE recommended_agent = ?
`);
const result = stmt.get(agent);
return {
total: result.total || 0,
successful: result.successful || 0,
accuracy: result.total > 0 ? (result.successful || 0) / result.total : 0,
};
}
// ============ Stats Methods ============
/**
* Get all stats
*/
getStats() {
const stmt = this.db.prepare(`SELECT * FROM stats WHERE id = 1`);
const row = stmt.get();
return {
totalTrajectories: row?.total_trajectories || 0,
successfulTrajectories: row?.successful_trajectories || 0,
totalRoutings: row?.total_routings || 0,
successfulRoutings: row?.successful_routings || 0,
totalPatterns: row?.total_patterns || 0,
sonaAdaptations: row?.sona_adaptations || 0,
hnswQueries: row?.hnsw_queries || 0,
lastUpdated: row?.last_updated || Date.now(),
};
}
/**
* Increment a stat counter
*/
incrementStat(statName, amount = 1) {
const stmt = this.db.prepare(`
UPDATE stats SET ${statName} = ${statName} + ?, last_updated = strftime('%s', 'now')
WHERE id = 1
`);
stmt.run(amount);
}
/**
* Record SONA adaptation
*/
recordSonaAdaptation() {
this.incrementStat('sona_adaptations');
}
/**
* Record HNSW query
*/
recordHnswQuery() {
this.incrementStat('hnsw_queries');
}
// ============ Utility Methods ============
/**
* Get summary for display (simplified for UI)
*/
getSummary() {
const stats = this.getStats();
return {
trajectories: stats.totalTrajectories,
routings: stats.totalRoutings,
patterns: stats.totalPatterns,
operations: stats.sonaAdaptations + stats.hnswQueries,
};
}
/**
* Get detailed summary for reports
*/
getDetailedSummary() {
const stats = this.getStats();
const activeCount = this.getActiveTrajectories().length;
return {
trajectories: {
total: stats.totalTrajectories,
active: activeCount,
successful: stats.successfulTrajectories,
},
routings: {
total: stats.totalRoutings,
accuracy: stats.totalRoutings > 0
? stats.successfulRoutings / stats.totalRoutings
: 0,
},
patterns: stats.totalPatterns,
operations: {
sona: stats.sonaAdaptations,
hnsw: stats.hnswQueries,
},
};
}
/**
* Close database connection
*/
close() {
this.db.close();
IntelligenceStore.instance = null;
}
/**
* Reset all data (for testing)
*/
reset() {
this.db.exec(`
DELETE FROM trajectories;
DELETE FROM patterns;
DELETE FROM routings;
UPDATE stats SET
total_trajectories = 0,
successful_trajectories = 0,
total_routings = 0,
successful_routings = 0,
total_patterns = 0,
sona_adaptations = 0,
hnsw_queries = 0,
last_updated = strftime('%s', 'now')
WHERE id = 1;
`);
}
}
// Export singleton getter
export function getIntelligenceStore(dbPath) {
return IntelligenceStore.getInstance(dbPath);
}
//# sourceMappingURL=IntelligenceStore.js.map