tasq/node_modules/agentic-flow/dist/core/agentdb-fast.js

308 lines
10 KiB
JavaScript

/**
* AgentDB Fast API
*
* Provides programmatic access to AgentDB without CLI overhead
* Eliminates 2.3s overhead from process spawning and transformers.js init
*/
import { AgentDB } from 'agentdb';
import { EventEmitter } from 'events';
/**
* Fast AgentDB client that avoids CLI overhead
*
* Performance:
* - CLI: ~2,350ms per operation
* - Direct API: ~10-50ms per operation
* - Speedup: ~50-200x faster
*/
export class AgentDBFast extends EventEmitter {
db = null;
backend = null;
config;
initialized = false;
embeddingCache = new Map();
constructor(config = {}) {
super();
this.config = {
path: config.path || '.agentdb-fast',
vectorDimensions: config.vectorDimensions || 384,
enableHNSW: config.enableHNSW !== false,
hnswM: config.hnswM || 16,
hnswEfConstruction: config.hnswEfConstruction || 200
};
}
/**
* Initialize database connection (lazy)
*/
async initialize() {
if (this.initialized)
return;
try {
// Create AgentDB instance
this.db = new AgentDB({
path: this.config.path,
dimensions: this.config.vectorDimensions
});
await this.db.initialize();
// Access the vector backend as a direct property
if (this.db.vectorBackend) {
this.backend = this.db.vectorBackend;
}
else {
throw new Error('Vector backend not available');
}
this.initialized = true;
this.emit('initialized');
}
catch (error) {
throw new Error(`Failed to initialize AgentDB: ${error.message}`);
}
}
/**
* Store an episode (fast, no CLI overhead)
*/
async storeEpisode(episode) {
await this.initialize();
// Generate embedding if not provided
if (!episode.embedding) {
episode.embedding = await this.getEmbedding(episode.task);
}
const episodeId = episode.id || this.generateId('episode');
// Use backend insert method
if (!this.backend) {
throw new Error('Backend not initialized');
}
await this.backend.insert(episodeId, new Float32Array(episode.embedding), {
type: 'episode',
sessionId: episode.sessionId,
task: episode.task,
trajectory: JSON.stringify(episode.trajectory),
reward: episode.reward,
quality: episode.quality,
context: episode.context,
timestamp: episode.timestamp || Date.now()
});
this.emit('episode:stored', episodeId);
return episodeId;
}
/**
* Retrieve episodes by task similarity (fast)
*/
async retrieveEpisodes(options) {
await this.initialize();
const dimensions = this.config.vectorDimensions ?? 384;
const queryEmbedding = options.task
? await this.getEmbedding(options.task)
: Array(dimensions).fill(0);
const k = options.k || 5;
// Build filter
const filter = { type: 'episode' };
if (options.sessionId)
filter.sessionId = options.sessionId;
if (options.minReward !== undefined)
filter.reward = { $gte: options.minReward };
if (options.maxReward !== undefined) {
filter.reward = { ...(filter.reward || {}), $lte: options.maxReward };
}
if (!this.backend) {
throw new Error('Backend not initialized');
}
const results = await this.backend.search(new Float32Array(queryEmbedding), k, { filter: Object.keys(filter).length > 1 ? filter : undefined });
return results.map((result) => ({
id: result.id,
sessionId: result.metadata.sessionId,
task: result.metadata.task,
trajectory: JSON.parse(result.metadata.trajectory || '[]'),
reward: result.metadata.reward,
quality: result.metadata.quality,
embedding: result.vector,
context: result.metadata.context,
timestamp: result.metadata.timestamp
}));
}
/**
* Store a pattern (for ReasoningBank)
*/
async storePattern(pattern) {
await this.initialize();
if (!pattern.embedding) {
pattern.embedding = await this.getEmbedding(`${pattern.input} ${pattern.output}`);
}
const patternId = pattern.id || this.generateId('pattern');
if (!this.backend) {
throw new Error('Backend not initialized');
}
await this.backend.insert(patternId, new Float32Array(pattern.embedding), {
type: 'pattern',
task: pattern.task,
input: pattern.input,
output: pattern.output,
quality: pattern.quality,
context: pattern.context,
timestamp: pattern.timestamp || Date.now()
});
this.emit('pattern:stored', patternId);
return patternId;
}
/**
* Search for similar patterns
*/
async searchPatterns(query, k = 5, minQuality) {
await this.initialize();
const queryEmbedding = await this.getEmbedding(query);
const filter = { type: 'pattern' };
if (minQuality !== undefined) {
filter.quality = { $gte: minQuality };
}
if (!this.backend) {
throw new Error('Backend not initialized');
}
const results = await this.backend.search(new Float32Array(queryEmbedding), k, { filter: Object.keys(filter).length > 1 ? filter : undefined });
return results.map((result) => ({
id: result.id,
task: result.metadata.task,
input: result.metadata.input,
output: result.metadata.output,
quality: result.metadata.quality,
embedding: result.vector,
context: result.metadata.context,
timestamp: result.metadata.timestamp
}));
}
/**
* Get database statistics
*/
async getStats() {
await this.initialize();
if (!this.backend) {
throw new Error('Backend not initialized');
}
const stats = await this.backend.stats();
// Count by type - get all results
const allResults = await this.backend.search(new Float32Array(this.config.vectorDimensions).fill(0), 10000, // Get all
{});
const episodes = allResults.filter((r) => r.metadata?.type === 'episode');
const patterns = allResults.filter((r) => r.metadata?.type === 'pattern');
const avgQuality = patterns.reduce((sum, p) => sum + (p.metadata?.quality || 0), 0) /
(patterns.length || 1);
return {
totalVectors: stats.totalVectors || allResults.length || 0,
totalEpisodes: episodes.length,
totalPatterns: patterns.length,
avgQuality
};
}
/**
* Close database connection
*/
async close() {
this.removeAllListeners();
this.embeddingCache.clear();
if (this.db) {
await this.db.close();
this.db = null;
this.backend = null;
}
this.initialized = false;
}
/**
* Generate embedding for text (with caching)
*
* Note: This is a simple mock. In production, replace with:
* - OpenAI embeddings API
* - Local transformer model
* - SentenceTransformers
*/
async getEmbedding(text) {
// Check cache
if (this.embeddingCache.has(text)) {
return this.embeddingCache.get(text);
}
// Simple hash-based embedding (REPLACE IN PRODUCTION)
const embedding = this.simpleHashEmbedding(text);
// Cache it
this.embeddingCache.set(text, embedding);
if (this.embeddingCache.size > 1000) {
// LRU-style cleanup
const firstKey = this.embeddingCache.keys().next().value;
if (firstKey) {
this.embeddingCache.delete(firstKey);
}
}
return embedding;
}
/**
* Simple hash-based embedding (MOCK - REPLACE IN PRODUCTION)
*
* Production alternatives:
* 1. OpenAI: https://platform.openai.com/docs/guides/embeddings
* 2. Transformers.js: https://huggingface.co/docs/transformers.js
* 3. SBERT: https://www.sbert.net/
*/
simpleHashEmbedding(text) {
const embedding = new Array(this.config.vectorDimensions);
// Seed with text hash
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = (hash << 5) - hash + text.charCodeAt(i);
hash = hash & hash;
}
// Generate pseudo-random embedding
for (let i = 0; i < this.config.vectorDimensions; i++) {
const seed = hash + i * 2654435761;
const x = Math.sin(seed) * 10000;
embedding[i] = x - Math.floor(x);
}
// Normalize
const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
return embedding.map(v => v / norm);
}
/**
* Generate unique ID
*/
generateId(prefix) {
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
/**
* Convenience function to create a fast AgentDB client
*/
export function createFastAgentDB(config) {
return new AgentDBFast(config);
}
/**
* Performance comparison helper
*/
export async function benchmarkAgentDB() {
const client = createFastAgentDB({ path: '.agentdb-benchmark' });
// API benchmark
const apiStoreStart = Date.now();
const episodeId = await client.storeEpisode({
sessionId: 'test-session',
task: 'test-task',
trajectory: ['step1', 'step2'],
reward: 0.8
});
const apiStoreTime = Date.now() - apiStoreStart;
const apiRetrieveStart = Date.now();
await client.retrieveEpisodes({ task: 'test-task', k: 5 });
const apiRetrieveTime = Date.now() - apiRetrieveStart;
await client.close();
// CLI times from benchmarks
const cliStoreTime = 2350;
const cliRetrieveTime = 2400;
return {
cli: {
store: cliStoreTime,
retrieve: cliRetrieveTime
},
api: {
store: apiStoreTime,
retrieve: apiRetrieveTime
},
speedup: {
store: cliStoreTime / apiStoreTime,
retrieve: cliRetrieveTime / apiRetrieveTime
}
};
}
//# sourceMappingURL=agentdb-fast.js.map