Initial commit: Brainy - Multi-Dimensional AI Database

Open source vector database with HNSW indexing, graph relationships,
and metadata facets. Features CLI with professional augmentation registry
integration for discovering extensions and capabilities.
This commit is contained in:
David Snelling 2025-08-18 17:35:06 -07:00
commit f8c45f2d8d
448 changed files with 103294 additions and 0 deletions

548
dist/storage/adapters/memoryStorage.js vendored Normal file
View file

@ -0,0 +1,548 @@
/**
* Memory Storage Adapter
* In-memory storage adapter for environments where persistent storage is not available or needed
*/
import { BaseStorage } from '../baseStorage.js';
// No type aliases needed - using the original types directly
/**
* In-memory storage adapter
* Uses Maps to store data in memory
*/
export class MemoryStorage extends BaseStorage {
constructor() {
super();
// Single map of noun ID to noun
this.nouns = new Map();
this.verbs = new Map();
this.metadata = new Map();
this.nounMetadata = new Map();
this.verbMetadata = new Map();
this.statistics = null;
}
/**
* Initialize the storage adapter
* Nothing to initialize for in-memory storage
*/
async init() {
this.isInitialized = true;
}
/**
* Save a noun to storage
*/
async saveNoun_internal(noun) {
// Create a deep copy to avoid reference issues
const nounCopy = {
id: noun.id,
vector: [...noun.vector],
connections: new Map(),
level: noun.level || 0
};
// Copy connections
for (const [level, connections] of noun.connections.entries()) {
nounCopy.connections.set(level, new Set(connections));
}
// Save the noun directly in the nouns map
this.nouns.set(noun.id, nounCopy);
}
/**
* Get a noun from storage
*/
async getNoun_internal(id) {
// Get the noun directly from the nouns map
const noun = this.nouns.get(id);
// If not found, return null
if (!noun) {
return null;
}
// Return a deep copy to avoid reference issues
const nounCopy = {
id: noun.id,
vector: [...noun.vector],
connections: new Map(),
level: noun.level || 0
};
// Copy connections
for (const [level, connections] of noun.connections.entries()) {
nounCopy.connections.set(level, new Set(connections));
}
return nounCopy;
}
/**
* Get nouns with pagination and filtering
* @param options Pagination and filtering options
* @returns Promise that resolves to a paginated result of nouns
*/
async getNouns(options = {}) {
const pagination = options.pagination || {};
const filter = options.filter || {};
// Default values
const offset = pagination.offset || 0;
const limit = pagination.limit || 100;
// Convert string types to arrays for consistent handling
const nounTypes = filter.nounType
? Array.isArray(filter.nounType) ? filter.nounType : [filter.nounType]
: undefined;
const services = filter.service
? Array.isArray(filter.service) ? filter.service : [filter.service]
: undefined;
// First, collect all noun IDs that match the filter criteria
const matchingIds = [];
// Iterate through all nouns to find matches
for (const [nounId, noun] of this.nouns.entries()) {
// Get the metadata to check filters
const metadata = await this.getMetadata(nounId);
if (!metadata)
continue;
// Filter by noun type if specified
if (nounTypes && !nounTypes.includes(metadata.noun)) {
continue;
}
// Filter by service if specified
if (services && metadata.service && !services.includes(metadata.service)) {
continue;
}
// Filter by metadata fields if specified
if (filter.metadata) {
let metadataMatch = true;
for (const [key, value] of Object.entries(filter.metadata)) {
if (metadata[key] !== value) {
metadataMatch = false;
break;
}
}
if (!metadataMatch)
continue;
}
// If we got here, the noun matches all filters
matchingIds.push(nounId);
}
// Calculate pagination
const totalCount = matchingIds.length;
const paginatedIds = matchingIds.slice(offset, offset + limit);
const hasMore = offset + limit < totalCount;
// Create cursor for next page if there are more results
const nextCursor = hasMore ? `${offset + limit}` : undefined;
// Fetch the actual nouns for the current page
const items = [];
for (const id of paginatedIds) {
const noun = this.nouns.get(id);
if (!noun)
continue;
// Create a deep copy to avoid reference issues
const nounCopy = {
id: noun.id,
vector: [...noun.vector],
connections: new Map(),
level: noun.level || 0
};
// Copy connections
for (const [level, connections] of noun.connections.entries()) {
nounCopy.connections.set(level, new Set(connections));
}
items.push(nounCopy);
}
return {
items,
totalCount,
hasMore,
nextCursor
};
}
/**
* Get nouns with pagination - simplified interface for compatibility
*/
async getNounsWithPagination(options = {}) {
// Convert to the getNouns format
const result = await this.getNouns({
pagination: {
offset: options.cursor ? parseInt(options.cursor) : 0,
limit: options.limit || 100
},
filter: options.filter
});
return {
items: result.items,
totalCount: result.totalCount || 0,
hasMore: result.hasMore,
nextCursor: result.nextCursor
};
}
/**
* Get nouns by noun type
* @param nounType The noun type to filter by
* @returns Promise that resolves to an array of nouns of the specified noun type
* @deprecated Use getNouns() with filter.nounType instead
*/
async getNounsByNounType_internal(nounType) {
const result = await this.getNouns({
filter: {
nounType
}
});
return result.items;
}
/**
* Delete a noun from storage
*/
async deleteNoun_internal(id) {
this.nouns.delete(id);
}
/**
* Save a verb to storage
*/
async saveVerb_internal(verb) {
// Create a deep copy to avoid reference issues
const verbCopy = {
id: verb.id,
vector: [...verb.vector],
connections: new Map()
};
// Copy connections
for (const [level, connections] of verb.connections.entries()) {
verbCopy.connections.set(level, new Set(connections));
}
// Save the verb directly in the verbs map
this.verbs.set(verb.id, verbCopy);
}
/**
* Get a verb from storage
*/
async getVerb_internal(id) {
// Get the verb directly from the verbs map
const verb = this.verbs.get(id);
// If not found, return null
if (!verb) {
return null;
}
// Create default timestamp if not present
const defaultTimestamp = {
seconds: Math.floor(Date.now() / 1000),
nanoseconds: (Date.now() % 1000) * 1000000
};
// Create default createdBy if not present
const defaultCreatedBy = {
augmentation: 'unknown',
version: '1.0'
};
// Return a deep copy of the HNSWVerb
const verbCopy = {
id: verb.id,
vector: [...verb.vector],
connections: new Map()
};
// Copy connections
for (const [level, connections] of verb.connections.entries()) {
verbCopy.connections.set(level, new Set(connections));
}
return verbCopy;
}
/**
* Get verbs with pagination and filtering
* @param options Pagination and filtering options
* @returns Promise that resolves to a paginated result of verbs
*/
async getVerbs(options = {}) {
const pagination = options.pagination || {};
const filter = options.filter || {};
// Default values
const offset = pagination.offset || 0;
const limit = pagination.limit || 100;
// Convert string types to arrays for consistent handling
const verbTypes = filter.verbType
? Array.isArray(filter.verbType) ? filter.verbType : [filter.verbType]
: undefined;
const sourceIds = filter.sourceId
? Array.isArray(filter.sourceId) ? filter.sourceId : [filter.sourceId]
: undefined;
const targetIds = filter.targetId
? Array.isArray(filter.targetId) ? filter.targetId : [filter.targetId]
: undefined;
const services = filter.service
? Array.isArray(filter.service) ? filter.service : [filter.service]
: undefined;
// First, collect all verb IDs that match the filter criteria
const matchingIds = [];
// Iterate through all verbs to find matches
for (const [verbId, hnswVerb] of this.verbs.entries()) {
// Get the metadata for this verb to do filtering
const metadata = this.verbMetadata.get(verbId);
// Filter by verb type if specified
if (verbTypes && metadata && !verbTypes.includes(metadata.type || metadata.verb || '')) {
continue;
}
// Filter by source ID if specified
if (sourceIds && metadata && !sourceIds.includes(metadata.sourceId || metadata.source || '')) {
continue;
}
// Filter by target ID if specified
if (targetIds && metadata && !targetIds.includes(metadata.targetId || metadata.target || '')) {
continue;
}
// Filter by metadata fields if specified
if (filter.metadata && metadata && metadata.data) {
let metadataMatch = true;
for (const [key, value] of Object.entries(filter.metadata)) {
if (metadata.data[key] !== value) {
metadataMatch = false;
break;
}
}
if (!metadataMatch)
continue;
}
// Filter by service if specified
if (services && metadata && metadata.createdBy && metadata.createdBy.augmentation &&
!services.includes(metadata.createdBy.augmentation)) {
continue;
}
// If we got here, the verb matches all filters
matchingIds.push(verbId);
}
// Calculate pagination
const totalCount = matchingIds.length;
const paginatedIds = matchingIds.slice(offset, offset + limit);
const hasMore = offset + limit < totalCount;
// Create cursor for next page if there are more results
const nextCursor = hasMore ? `${offset + limit}` : undefined;
// Fetch the actual verbs for the current page
const items = [];
for (const id of paginatedIds) {
const hnswVerb = this.verbs.get(id);
const metadata = this.verbMetadata.get(id);
if (!hnswVerb)
continue;
if (!metadata) {
console.warn(`Verb ${id} found but no metadata - creating minimal GraphVerb`);
// Return minimal GraphVerb if metadata is missing
items.push({
id: hnswVerb.id,
vector: hnswVerb.vector,
sourceId: '',
targetId: ''
});
continue;
}
// Create a complete GraphVerb by combining HNSWVerb with metadata
const graphVerb = {
id: hnswVerb.id,
vector: [...hnswVerb.vector],
sourceId: metadata.sourceId,
targetId: metadata.targetId,
source: metadata.source,
target: metadata.target,
verb: metadata.verb,
type: metadata.type,
weight: metadata.weight,
createdAt: metadata.createdAt,
updatedAt: metadata.updatedAt,
createdBy: metadata.createdBy,
data: metadata.data,
metadata: metadata.data // Alias for backward compatibility
};
items.push(graphVerb);
}
return {
items,
totalCount,
hasMore,
nextCursor
};
}
/**
* Get verbs by source
* @deprecated Use getVerbs() with filter.sourceId instead
*/
async getVerbsBySource_internal(sourceId) {
const result = await this.getVerbs({
filter: {
sourceId
}
});
return result.items;
}
/**
* Get verbs by target
* @deprecated Use getVerbs() with filter.targetId instead
*/
async getVerbsByTarget_internal(targetId) {
const result = await this.getVerbs({
filter: {
targetId
}
});
return result.items;
}
/**
* Get verbs by type
* @deprecated Use getVerbs() with filter.verbType instead
*/
async getVerbsByType_internal(type) {
const result = await this.getVerbs({
filter: {
verbType: type
}
});
return result.items;
}
/**
* Delete a verb from storage
*/
async deleteVerb_internal(id) {
// Delete the verb directly from the verbs map
this.verbs.delete(id);
}
/**
* Save metadata to storage
*/
async saveMetadata(id, metadata) {
this.metadata.set(id, JSON.parse(JSON.stringify(metadata)));
}
/**
* Get metadata from storage
*/
async getMetadata(id) {
const metadata = this.metadata.get(id);
if (!metadata) {
return null;
}
return JSON.parse(JSON.stringify(metadata));
}
/**
* Get multiple metadata objects in batches (CRITICAL: Prevents socket exhaustion)
* Memory storage implementation is simple since all data is already in memory
*/
async getMetadataBatch(ids) {
const results = new Map();
// Memory storage can handle all IDs at once since it's in-memory
for (const id of ids) {
const metadata = this.metadata.get(id);
if (metadata) {
// Deep clone to prevent mutation
results.set(id, JSON.parse(JSON.stringify(metadata)));
}
}
return results;
}
/**
* Save noun metadata to storage
*/
async saveNounMetadata(id, metadata) {
this.nounMetadata.set(id, JSON.parse(JSON.stringify(metadata)));
}
/**
* Get noun metadata from storage
*/
async getNounMetadata(id) {
const metadata = this.nounMetadata.get(id);
if (!metadata) {
return null;
}
return JSON.parse(JSON.stringify(metadata));
}
/**
* Save verb metadata to storage
*/
async saveVerbMetadata(id, metadata) {
this.verbMetadata.set(id, JSON.parse(JSON.stringify(metadata)));
}
/**
* Get verb metadata from storage
*/
async getVerbMetadata(id) {
const metadata = this.verbMetadata.get(id);
if (!metadata) {
return null;
}
return JSON.parse(JSON.stringify(metadata));
}
/**
* Clear all data from storage
*/
async clear() {
this.nouns.clear();
this.verbs.clear();
this.metadata.clear();
this.nounMetadata.clear();
this.verbMetadata.clear();
this.statistics = null;
// Clear the statistics cache
this.statisticsCache = null;
this.statisticsModified = false;
}
/**
* Get information about storage usage and capacity
*/
async getStorageStatus() {
return {
type: 'memory',
used: 0, // In-memory storage doesn't have a meaningful size
quota: null, // In-memory storage doesn't have a quota
details: {
nodeCount: this.nouns.size,
edgeCount: this.verbs.size,
metadataCount: this.metadata.size
}
};
}
/**
* Save statistics data to storage
* @param statistics The statistics data to save
*/
async saveStatisticsData(statistics) {
// For memory storage, we just need to store the statistics in memory
// Create a deep copy to avoid reference issues
this.statistics = {
nounCount: { ...statistics.nounCount },
verbCount: { ...statistics.verbCount },
metadataCount: { ...statistics.metadataCount },
hnswIndexSize: statistics.hnswIndexSize,
lastUpdated: statistics.lastUpdated,
// Include serviceActivity if present
...(statistics.serviceActivity && {
serviceActivity: Object.fromEntries(Object.entries(statistics.serviceActivity).map(([k, v]) => [k, { ...v }]))
}),
// Include services if present
...(statistics.services && {
services: statistics.services.map(s => ({ ...s }))
}),
// Include distributedConfig if present
...(statistics.distributedConfig && {
distributedConfig: JSON.parse(JSON.stringify(statistics.distributedConfig))
})
};
// Since this is in-memory, there's no need for time-based partitioning
// or legacy file handling
}
/**
* Get statistics data from storage
* @returns Promise that resolves to the statistics data or null if not found
*/
async getStatisticsData() {
if (!this.statistics) {
return null;
}
// Return a deep copy to avoid reference issues
return {
nounCount: { ...this.statistics.nounCount },
verbCount: { ...this.statistics.verbCount },
metadataCount: { ...this.statistics.metadataCount },
hnswIndexSize: this.statistics.hnswIndexSize,
lastUpdated: this.statistics.lastUpdated,
// Include serviceActivity if present
...(this.statistics.serviceActivity && {
serviceActivity: Object.fromEntries(Object.entries(this.statistics.serviceActivity).map(([k, v]) => [k, { ...v }]))
}),
// Include services if present
...(this.statistics.services && {
services: this.statistics.services.map(s => ({ ...s }))
}),
// Include distributedConfig if present
...(this.statistics.distributedConfig && {
distributedConfig: JSON.parse(JSON.stringify(this.statistics.distributedConfig))
})
};
// Since this is in-memory, there's no need for fallback mechanisms
// to check multiple storage locations
}
}
//# sourceMappingURL=memoryStorage.js.map