import { Embeddings } from '@langchain/core/embeddings' import { BaseStore } from '@langchain/core/stores' import { CacheBackedEmbeddings } from 'langchain/embeddings/cache_backed' import { getBaseClasses, ICommonObject, INode, INodeData, INodeParams } from '../../../src' class InMemoryEmbeddingCache implements INode { label: string name: string version: number description: string type: string icon: string category: string baseClasses: string[] inputs: INodeParams[] credential: INodeParams constructor() { this.label = 'InMemory Embedding Cache' this.name = 'inMemoryEmbeddingCache' this.version = 1.0 this.type = 'InMemoryEmbeddingCache' this.description = 'Cache generated Embeddings in memory to avoid needing to recompute them.' this.icon = 'Memory.svg' this.category = 'Cache' this.baseClasses = [this.type, ...getBaseClasses(CacheBackedEmbeddings)] this.inputs = [ { label: 'Embeddings', name: 'embeddings', type: 'Embeddings' }, { label: 'Namespace', name: 'namespace', type: 'string', optional: true, additionalParams: true } ] } async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { const namespace = nodeData.inputs?.namespace as string const underlyingEmbeddings = nodeData.inputs?.embeddings as Embeddings const memoryMap = (await options.cachePool.getEmbeddingCache(options.chatflowid)) ?? {} const inMemCache = new InMemoryEmbeddingCacheExtended(memoryMap) inMemCache.mget = async (keys: string[]) => { const memory = (await options.cachePool.getEmbeddingCache(options.chatflowid)) ?? inMemCache.store return keys.map((key) => memory[key]) } inMemCache.mset = async (keyValuePairs: [string, any][]): Promise => { for (const [key, value] of keyValuePairs) { inMemCache.store[key] = value } await options.cachePool.addEmbeddingCache(options.chatflowid, inMemCache.store) } inMemCache.mdelete = async (keys: string[]): Promise => { for (const key of keys) { delete inMemCache.store[key] } await options.cachePool.addEmbeddingCache(options.chatflowid, inMemCache.store) } return CacheBackedEmbeddings.fromBytesStore(underlyingEmbeddings, inMemCache, { namespace: namespace }) } } class InMemoryEmbeddingCacheExtended extends BaseStore { lc_namespace = ['langchain', 'storage', 'in_memory'] store: Record = {} constructor(map: Record) { super() this.store = map } /** * Retrieves the values associated with the given keys from the store. * @param keys Keys to retrieve values for. * @returns Array of values associated with the given keys. */ async mget(keys: string[]) { return keys.map((key) => this.store[key]) } /** * Sets the values for the given keys in the store. * @param keyValuePairs Array of key-value pairs to set in the store. * @returns Promise that resolves when all key-value pairs have been set. */ async mset(keyValuePairs: [string, T][]): Promise { for (const [key, value] of keyValuePairs) { this.store[key] = value } } /** * Deletes the given keys and their associated values from the store. * @param keys Keys to delete from the store. * @returns Promise that resolves when all keys have been deleted. */ async mdelete(keys: string[]): Promise { for (const key of keys) { delete this.store[key] } } /** * Asynchronous generator that yields keys from the store. If a prefix is * provided, it only yields keys that start with the prefix. * @param prefix Optional prefix to filter keys. * @returns AsyncGenerator that yields keys from the store. */ async *yieldKeys(prefix?: string | undefined): AsyncGenerator { const keys = Object.keys(this.store) for (const key of keys) { if (prefix === undefined || key.startsWith(prefix)) { yield key } } } } module.exports = { nodeClass: InMemoryEmbeddingCache }