| | "use strict"; |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | Object.defineProperty(exports, "__esModule", { value: true }); |
| | exports.FastAgentDB = void 0; |
| | exports.createFastAgentDB = createFastAgentDB; |
| | exports.getDefaultAgentDB = getDefaultAgentDB; |
| | |
| | let coreModule = null; |
| | function getCoreModule() { |
| | if (coreModule) |
| | return coreModule; |
| | try { |
| | coreModule = require('@ruvector/core'); |
| | return coreModule; |
| | } |
| | catch { |
| | |
| | try { |
| | coreModule = require('ruvector'); |
| | return coreModule; |
| | } |
| | catch (e) { |
| | throw new Error(`Neither @ruvector/core nor ruvector is available: ${e.message}`); |
| | } |
| | } |
| | } |
| | |
| | |
| | |
| | class FastAgentDB { |
| | |
| | |
| | |
| | |
| | |
| | |
| | constructor(dimensions = 128, maxEpisodes = 100000) { |
| | this.episodes = new Map(); |
| | this.trajectories = new Map(); |
| | this.vectorDb = null; |
| | this.episodeOrder = []; |
| | this.dimensions = dimensions; |
| | this.maxEpisodes = maxEpisodes; |
| | } |
| | |
| | |
| | |
| | async initVectorDb() { |
| | if (this.vectorDb) |
| | return; |
| | try { |
| | const core = getCoreModule(); |
| | this.vectorDb = new core.VectorDB({ |
| | dimensions: this.dimensions, |
| | distanceMetric: 'Cosine', |
| | }); |
| | } |
| | catch (e) { |
| | |
| | console.warn(`VectorDB not available, using fallback similarity: ${e.message}`); |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | async storeEpisode(episode) { |
| | await this.initVectorDb(); |
| | const id = episode.id ?? this.generateId(); |
| | const fullEpisode = { |
| | ...episode, |
| | id, |
| | timestamp: episode.timestamp ?? Date.now(), |
| | }; |
| | |
| | if (this.episodes.size >= this.maxEpisodes) { |
| | const oldestId = this.episodeOrder.shift(); |
| | if (oldestId) { |
| | this.episodes.delete(oldestId); |
| | } |
| | } |
| | this.episodes.set(id, fullEpisode); |
| | this.episodeOrder.push(id); |
| | |
| | if (this.vectorDb && fullEpisode.state.length === this.dimensions) { |
| | try { |
| | await this.vectorDb.insert({ |
| | id, |
| | vector: new Float32Array(fullEpisode.state), |
| | }); |
| | } |
| | catch { |
| | |
| | } |
| | } |
| | return id; |
| | } |
| | |
| | |
| | |
| | async storeEpisodes(episodes) { |
| | const ids = []; |
| | for (const episode of episodes) { |
| | const id = await this.storeEpisode(episode); |
| | ids.push(id); |
| | } |
| | return ids; |
| | } |
| | |
| | |
| | |
| | async getEpisode(id) { |
| | const episode = this.episodes.get(id); |
| | if (episode) { |
| | |
| | const idx = this.episodeOrder.indexOf(id); |
| | if (idx > -1) { |
| | this.episodeOrder.splice(idx, 1); |
| | this.episodeOrder.push(id); |
| | } |
| | } |
| | return episode ?? null; |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | async searchByState(queryState, k = 10) { |
| | await this.initVectorDb(); |
| | const query = Array.isArray(queryState) ? queryState : Array.from(queryState); |
| | |
| | if (this.vectorDb && query.length === this.dimensions) { |
| | try { |
| | const results = await this.vectorDb.search({ |
| | vector: new Float32Array(query), |
| | k, |
| | }); |
| | return results |
| | .map((r) => { |
| | const episode = this.episodes.get(r.id); |
| | if (!episode) |
| | return null; |
| | return { |
| | episode, |
| | similarity: 1 - r.score, |
| | }; |
| | }) |
| | .filter((r) => r !== null); |
| | } |
| | catch { |
| | |
| | } |
| | } |
| | |
| | return this.fallbackSearch(query, k); |
| | } |
| | |
| | |
| | |
| | fallbackSearch(query, k) { |
| | const results = []; |
| | for (const episode of this.episodes.values()) { |
| | if (episode.state.length !== query.length) |
| | continue; |
| | const similarity = this.cosineSimilarity(query, episode.state); |
| | results.push({ episode, similarity }); |
| | } |
| | return results |
| | .sort((a, b) => b.similarity - a.similarity) |
| | .slice(0, k); |
| | } |
| | |
| | |
| | |
| | cosineSimilarity(a, b) { |
| | let dotProduct = 0; |
| | let normA = 0; |
| | let normB = 0; |
| | for (let i = 0; i < a.length; i++) { |
| | dotProduct += a[i] * b[i]; |
| | normA += a[i] * a[i]; |
| | normB += b[i] * b[i]; |
| | } |
| | const denom = Math.sqrt(normA) * Math.sqrt(normB); |
| | return denom === 0 ? 0 : dotProduct / denom; |
| | } |
| | |
| | |
| | |
| | async storeTrajectory(episodes, metadata) { |
| | const trajectoryId = this.generateId(); |
| | const storedEpisodes = []; |
| | let totalReward = 0; |
| | for (const episode of episodes) { |
| | const id = await this.storeEpisode(episode); |
| | const stored = await this.getEpisode(id); |
| | if (stored) { |
| | storedEpisodes.push(stored); |
| | totalReward += stored.reward; |
| | } |
| | } |
| | const trajectory = { |
| | id: trajectoryId, |
| | episodes: storedEpisodes, |
| | totalReward, |
| | metadata, |
| | }; |
| | this.trajectories.set(trajectoryId, trajectory); |
| | return trajectoryId; |
| | } |
| | |
| | |
| | |
| | async getTrajectory(id) { |
| | return this.trajectories.get(id) ?? null; |
| | } |
| | |
| | |
| | |
| | async getTopTrajectories(k = 10) { |
| | return Array.from(this.trajectories.values()) |
| | .sort((a, b) => b.totalReward - a.totalReward) |
| | .slice(0, k); |
| | } |
| | |
| | |
| | |
| | async sampleEpisodes(n) { |
| | const allEpisodes = Array.from(this.episodes.values()); |
| | const sampled = []; |
| | for (let i = 0; i < Math.min(n, allEpisodes.length); i++) { |
| | const idx = Math.floor(Math.random() * allEpisodes.length); |
| | sampled.push(allEpisodes[idx]); |
| | } |
| | return sampled; |
| | } |
| | |
| | |
| | |
| | getStats() { |
| | return { |
| | episodeCount: this.episodes.size, |
| | trajectoryCount: this.trajectories.size, |
| | dimensions: this.dimensions, |
| | maxEpisodes: this.maxEpisodes, |
| | vectorDbAvailable: this.vectorDb !== null, |
| | }; |
| | } |
| | |
| | |
| | |
| | clear() { |
| | this.episodes.clear(); |
| | this.trajectories.clear(); |
| | this.episodeOrder = []; |
| | } |
| | |
| | |
| | |
| | generateId() { |
| | return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; |
| | } |
| | } |
| | exports.FastAgentDB = FastAgentDB; |
| | |
| | |
| | |
| | function createFastAgentDB(dimensions = 128, maxEpisodes = 100000) { |
| | return new FastAgentDB(dimensions, maxEpisodes); |
| | } |
| | |
| | let defaultInstance = null; |
| | |
| | |
| | |
| | function getDefaultAgentDB() { |
| | if (!defaultInstance) { |
| | defaultInstance = new FastAgentDB(); |
| | } |
| | return defaultInstance; |
| | } |
| | exports.default = { |
| | FastAgentDB, |
| | createFastAgentDB, |
| | getDefaultAgentDB, |
| | }; |
| |
|