tasq/node_modules/agentic-flow/dist/reasoningbank/db/queries.js

340 lines
11 KiB
JavaScript

/**
* Database queries for ReasoningBank
* Operates on Claude Flow's memory.db at .swarm/memory.db
*/
import Database from 'better-sqlite3';
import { existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
// Simple logger for database operations
const logger = {
info: (msg, data) => console.log(`[INFO] ${msg}`, data || ''),
error: (msg, data) => console.error(`[ERROR] ${msg}`, data || '')
};
let dbInstance = null;
/**
* Run database migrations (create tables)
*/
export async function runMigrations() {
const dbPath = process.env.CLAUDE_FLOW_DB_PATH || join(process.cwd(), '.swarm', 'memory.db');
// Create directory if it doesn't exist
const dbDir = dirname(dbPath);
if (!existsSync(dbDir)) {
mkdirSync(dbDir, { recursive: true });
logger.info('Created database directory', { path: dbDir });
}
// Create database file
const db = new Database(dbPath);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
// Create tables
db.exec(`
CREATE TABLE IF NOT EXISTS patterns (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
pattern_data TEXT NOT NULL,
confidence REAL NOT NULL DEFAULT 0.5,
usage_count INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_used TEXT
);
CREATE TABLE IF NOT EXISTS pattern_embeddings (
id TEXT PRIMARY KEY,
model TEXT NOT NULL,
dims INTEGER NOT NULL,
vector BLOB NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (id) REFERENCES patterns(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS pattern_links (
src_id TEXT NOT NULL,
dst_id TEXT NOT NULL,
relation TEXT NOT NULL,
weight REAL NOT NULL DEFAULT 1.0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (src_id, dst_id, relation),
FOREIGN KEY (src_id) REFERENCES patterns(id) ON DELETE CASCADE,
FOREIGN KEY (dst_id) REFERENCES patterns(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS task_trajectories (
task_id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
query TEXT NOT NULL,
trajectory_json TEXT NOT NULL,
started_at TEXT,
ended_at TEXT,
judge_label TEXT,
judge_conf REAL,
judge_reasons TEXT,
matts_run_id TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS matts_runs (
run_id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
mode TEXT NOT NULL,
k INTEGER NOT NULL,
status TEXT NOT NULL,
summary TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS consolidation_runs (
run_id TEXT PRIMARY KEY,
items_processed INTEGER NOT NULL,
duplicates_found INTEGER NOT NULL,
contradictions_found INTEGER NOT NULL,
items_pruned INTEGER NOT NULL,
duration_ms INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS metrics_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
metric_name TEXT NOT NULL,
value REAL NOT NULL,
timestamp TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_patterns_type ON patterns(type);
CREATE INDEX IF NOT EXISTS idx_patterns_confidence ON patterns(confidence DESC);
CREATE INDEX IF NOT EXISTS idx_patterns_created_at ON patterns(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_pattern_links_relation ON pattern_links(relation);
CREATE INDEX IF NOT EXISTS idx_trajectories_agent ON task_trajectories(agent_id);
`);
db.close();
dbInstance = null; // Reset instance to force reconnection
logger.info('Database migrations completed', { path: dbPath });
}
/**
* Get database connection (singleton)
*/
export function getDb() {
if (dbInstance)
return dbInstance;
const dbPath = process.env.CLAUDE_FLOW_DB_PATH || join(process.cwd(), '.swarm', 'memory.db');
if (!existsSync(dbPath)) {
throw new Error(`Database not found at ${dbPath}. Run migrations first.`);
}
dbInstance = new Database(dbPath);
dbInstance.pragma('journal_mode = WAL');
dbInstance.pragma('foreign_keys = ON');
logger.info('Connected to ReasoningBank database', { path: dbPath });
return dbInstance;
}
/**
* Fetch reasoning memory candidates for retrieval
*/
export function fetchMemoryCandidates(options) {
const db = getDb();
let query = `
SELECT
p.*,
pe.vector as embedding,
CAST((julianday('now') - julianday(p.created_at)) AS INTEGER) as age_days
FROM patterns p
JOIN pattern_embeddings pe ON p.id = pe.id
WHERE p.type = 'reasoning_memory'
AND p.confidence >= ?
`;
const params = [options.minConfidence || 0.3];
if (options.domain) {
query += ` AND json_extract(p.pattern_data, '$.domain') = ?`;
params.push(options.domain);
}
query += ` ORDER BY p.confidence DESC, p.usage_count DESC`;
const stmt = db.prepare(query);
const rows = stmt.all(...params);
return rows.map((row) => {
const buffer = Buffer.from(row.embedding);
// Create Float32Array from buffer - buffer length / 4 bytes per float
const float32Array = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.length / 4);
return {
...row,
pattern_data: JSON.parse(row.pattern_data),
embedding: float32Array
};
});
}
/**
* Store a new reasoning memory
*/
export function upsertMemory(memory) {
const db = getDb();
const stmt = db.prepare(`
INSERT OR REPLACE INTO patterns (id, type, pattern_data, confidence, usage_count, created_at)
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`);
stmt.run(memory.id, memory.type, JSON.stringify(memory.pattern_data), memory.confidence, memory.usage_count);
logger.info('Upserted reasoning memory', { id: memory.id, title: memory.pattern_data.title });
return memory.id;
}
/**
* Store embedding for a memory
*/
export function upsertEmbedding(embedding) {
const db = getDb();
const buffer = Buffer.from(embedding.vector.buffer);
const stmt = db.prepare(`
INSERT OR REPLACE INTO pattern_embeddings (id, model, dims, vector, created_at)
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
`);
stmt.run(embedding.id, embedding.model, embedding.dims, buffer);
}
/**
* Increment usage count for a memory
*/
export function incrementUsage(memoryId) {
const db = getDb();
db.prepare(`
UPDATE patterns
SET usage_count = usage_count + 1,
last_used = CURRENT_TIMESTAMP
WHERE id = ?
`).run(memoryId);
}
/**
* Store task trajectory
*/
export function storeTrajectory(trajectory) {
const db = getDb();
db.prepare(`
INSERT OR REPLACE INTO task_trajectories
(task_id, agent_id, query, trajectory_json, started_at, ended_at,
judge_label, judge_conf, judge_reasons, matts_run_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`).run(trajectory.task_id, trajectory.agent_id, trajectory.query, trajectory.trajectory_json, trajectory.started_at || null, trajectory.ended_at || null, trajectory.judge_label || null, trajectory.judge_conf || null, trajectory.judge_reasons || null, trajectory.matts_run_id || null);
}
/**
* Store MaTTS run
*/
export function storeMattsRun(run) {
const db = getDb();
db.prepare(`
INSERT INTO matts_runs (run_id, task_id, mode, k, status, summary, created_at)
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`).run(run.run_id, run.task_id, run.mode, run.k, run.status, run.summary || null);
}
/**
* Log performance metric
*/
export function logMetric(name, value) {
try {
const db = getDb();
// Log to metrics_log table (our own table)
db.prepare(`
INSERT INTO metrics_log (metric_name, value, timestamp)
VALUES (?, ?, CURRENT_TIMESTAMP)
`).run(name, value);
}
catch (error) {
// Silently fail if metrics table doesn't exist
// This is optional logging, not critical
}
}
/**
* Count new memories since last consolidation
*/
export function countNewMemoriesSinceConsolidation() {
const db = getDb();
const lastRun = db.prepare(`
SELECT created_at
FROM consolidation_runs
ORDER BY created_at DESC
LIMIT 1
`).get();
if (!lastRun) {
// No consolidation yet, count all memories
const result = db.prepare(`
SELECT COUNT(*) as count
FROM patterns
WHERE type = 'reasoning_memory'
`).get();
return result.count;
}
const result = db.prepare(`
SELECT COUNT(*) as count
FROM patterns
WHERE type = 'reasoning_memory'
AND created_at > ?
`).get(lastRun.created_at);
return result.count;
}
/**
* Get all active reasoning memories
*/
export function getAllActiveMemories() {
const db = getDb();
const rows = db.prepare(`
SELECT *
FROM patterns
WHERE type = 'reasoning_memory'
AND confidence >= 0.3
ORDER BY confidence DESC, usage_count DESC
`).all();
return rows.map((row) => ({
...row,
pattern_data: JSON.parse(row.pattern_data)
}));
}
/**
* Store memory link (relationship)
*/
export function storeLink(srcId, dstId, relation, weight) {
const db = getDb();
db.prepare(`
INSERT OR REPLACE INTO pattern_links (src_id, dst_id, relation, weight, created_at)
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
`).run(srcId, dstId, relation, weight);
}
/**
* Get contradictions for a memory
*/
export function getContradictions(memoryId) {
const db = getDb();
const rows = db.prepare(`
SELECT dst_id
FROM pattern_links
WHERE src_id = ? AND relation = 'contradicts'
`).all(memoryId);
return rows.map(r => r.dst_id);
}
/**
* Store consolidation run
*/
export function storeConsolidationRun(run) {
const db = getDb();
db.prepare(`
INSERT INTO consolidation_runs
(run_id, items_processed, duplicates_found, contradictions_found, items_pruned, duration_ms, created_at)
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`).run(run.run_id, run.items_processed, run.duplicates_found, run.contradictions_found, run.items_pruned, run.duration_ms);
}
/**
* Prune old, unused memories
*/
export function pruneOldMemories(options) {
const db = getDb();
const result = db.prepare(`
DELETE FROM patterns
WHERE type = 'reasoning_memory'
AND usage_count = 0
AND confidence < ?
AND CAST((julianday('now') - julianday(created_at)) AS INTEGER) > ?
`).run(options.minConfidence, options.maxAgeDays);
return result.changes;
}
/**
* Close database connection
*/
export function closeDb() {
if (dbInstance) {
dbInstance.close();
dbInstance = null;
logger.info('Closed ReasoningBank database connection');
}
}
//# sourceMappingURL=queries.js.map