@nxuss/lemma 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -181
- package/dist/cjs/cloud/KeyManager.d.ts.map +1 -0
- package/dist/cjs/cloud/KeyManager.js.map +1 -0
- package/dist/cjs/cloud/TenantCache.d.ts.map +1 -0
- package/dist/cjs/cloud/TenantCache.js.map +1 -0
- package/dist/cjs/cloud/index.d.ts.map +1 -0
- package/dist/cjs/cloud/index.js.map +1 -0
- package/dist/cjs/cloud/server.d.ts.map +1 -0
- package/dist/cjs/cloud/server.js.map +1 -0
- package/dist/cjs/cloud/types.d.ts.map +1 -0
- package/dist/cjs/cloud/types.js.map +1 -0
- package/dist/cjs/config/index.d.ts.map +1 -0
- package/dist/cjs/config/index.js.map +1 -0
- package/dist/cjs/consensus/ConsensusEngine.d.ts.map +1 -0
- package/dist/cjs/consensus/ConsensusEngine.js.map +1 -0
- package/dist/cjs/consensus/ModelPool.d.ts.map +1 -0
- package/dist/cjs/consensus/ModelPool.js.map +1 -0
- package/dist/cjs/consensus/index.d.ts.map +1 -0
- package/dist/cjs/consensus/index.js.map +1 -0
- package/dist/cjs/core/AgentRegistry.d.ts.map +1 -0
- package/dist/cjs/core/AgentRegistry.js.map +1 -0
- package/dist/cjs/core/DashboardBroadcaster.d.ts.map +1 -0
- package/dist/cjs/core/DashboardBroadcaster.js.map +1 -0
- package/dist/{core → cjs/core}/OrchestrationEngine.d.ts +1 -0
- package/dist/cjs/core/OrchestrationEngine.d.ts.map +1 -0
- package/dist/{core → cjs/core}/OrchestrationEngine.js +29 -23
- package/dist/cjs/core/OrchestrationEngine.js.map +1 -0
- package/dist/cjs/core/SubconsciousEngine.d.ts.map +1 -0
- package/dist/{core → cjs/core}/SubconsciousEngine.js +15 -3
- package/dist/cjs/core/SubconsciousEngine.js.map +1 -0
- package/dist/cjs/core/WebSocketServer.d.ts.map +1 -0
- package/dist/{core → cjs/core}/WebSocketServer.js +2 -1
- package/dist/cjs/core/WebSocketServer.js.map +1 -0
- package/dist/cjs/core/index.d.ts.map +1 -0
- package/dist/cjs/core/index.js.map +1 -0
- package/dist/cjs/core/router.d.ts.map +1 -0
- package/dist/cjs/core/router.js.map +1 -0
- package/dist/{embed → cjs/embed}/index.d.ts +66 -1
- package/dist/cjs/embed/index.d.ts.map +1 -0
- package/dist/cjs/embed/index.js +979 -0
- package/dist/cjs/embed/index.js.map +1 -0
- package/dist/cjs/embed.d.ts.map +1 -0
- package/dist/cjs/embed.js.map +1 -0
- package/dist/cjs/examples/basic-usage.d.ts +12 -0
- package/dist/cjs/examples/basic-usage.d.ts.map +1 -0
- package/dist/cjs/examples/basic-usage.js +128 -0
- package/dist/cjs/examples/basic-usage.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/protocol/flows.d.ts.map +1 -0
- package/dist/cjs/protocol/flows.js.map +1 -0
- package/dist/cjs/protocol/iap.d.ts.map +1 -0
- package/dist/cjs/protocol/iap.js.map +1 -0
- package/dist/cjs/protocol/index.d.ts.map +1 -0
- package/dist/cjs/protocol/index.js.map +1 -0
- package/dist/cjs/protocol/types.d.ts.map +1 -0
- package/dist/cjs/protocol/types.js.map +1 -0
- package/dist/cjs/protocol/utils.d.ts.map +1 -0
- package/dist/cjs/protocol/utils.js.map +1 -0
- package/dist/cjs/protocol/validators.d.ts.map +1 -0
- package/dist/cjs/protocol/validators.js.map +1 -0
- package/dist/cjs/security/AuthManager.d.ts.map +1 -0
- package/dist/cjs/security/AuthManager.js.map +1 -0
- package/dist/cjs/security/MessageSanitizer.d.ts.map +1 -0
- package/dist/{security → cjs/security}/MessageSanitizer.js +22 -6
- package/dist/cjs/security/MessageSanitizer.js.map +1 -0
- package/dist/cjs/security/RateLimiter.d.ts.map +1 -0
- package/dist/cjs/security/RateLimiter.js.map +1 -0
- package/dist/{security → cjs/security}/SecurityMiddleware.d.ts +3 -1
- package/dist/cjs/security/SecurityMiddleware.d.ts.map +1 -0
- package/dist/{security → cjs/security}/SecurityMiddleware.js +5 -3
- package/dist/cjs/security/SecurityMiddleware.js.map +1 -0
- package/dist/cjs/security/index.d.ts.map +1 -0
- package/dist/cjs/security/index.js.map +1 -0
- package/dist/cjs/speculative/PredictionEngine.d.ts.map +1 -0
- package/dist/cjs/speculative/PredictionEngine.js.map +1 -0
- package/dist/cjs/speculative/SpeculativeCache.d.ts.map +1 -0
- package/dist/cjs/speculative/SpeculativeCache.js.map +1 -0
- package/dist/cjs/speculative/SpeculativeEngine.d.ts.map +1 -0
- package/dist/cjs/speculative/SpeculativeEngine.js.map +1 -0
- package/dist/cjs/speculative/WorkerPool.d.ts.map +1 -0
- package/dist/cjs/speculative/WorkerPool.js.map +1 -0
- package/dist/cjs/speculative/index.d.ts.map +1 -0
- package/dist/cjs/speculative/index.js.map +1 -0
- package/dist/cjs/subconscious/EmbeddingService.d.ts.map +1 -0
- package/dist/cjs/subconscious/EmbeddingService.js.map +1 -0
- package/dist/cjs/subconscious/SemanticCache.d.ts.map +1 -0
- package/dist/cjs/subconscious/SemanticCache.js.map +1 -0
- package/dist/cjs/subconscious/SubconsciousEngine.d.ts.map +1 -0
- package/dist/cjs/subconscious/SubconsciousEngine.js.map +1 -0
- package/dist/cjs/subconscious/VectorStore.d.ts.map +1 -0
- package/dist/{subconscious → cjs/subconscious}/VectorStore.js +3 -3
- package/dist/cjs/subconscious/VectorStore.js.map +1 -0
- package/dist/cjs/subconscious/cache.d.ts.map +1 -0
- package/dist/cjs/subconscious/cache.js.map +1 -0
- package/dist/cjs/subconscious/embeddings.d.ts.map +1 -0
- package/dist/cjs/subconscious/embeddings.js.map +1 -0
- package/dist/cjs/subconscious/index.d.ts.map +1 -0
- package/dist/cjs/subconscious/index.js.map +1 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/utils/logger.d.ts.map +1 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/esm/cloud/KeyManager.d.ts +29 -0
- package/dist/esm/cloud/KeyManager.d.ts.map +1 -0
- package/dist/esm/cloud/KeyManager.js +135 -0
- package/dist/esm/cloud/KeyManager.js.map +1 -0
- package/dist/esm/cloud/TenantCache.d.ts +29 -0
- package/dist/esm/cloud/TenantCache.d.ts.map +1 -0
- package/dist/esm/cloud/TenantCache.js +125 -0
- package/dist/esm/cloud/TenantCache.js.map +1 -0
- package/dist/esm/cloud/index.d.ts +4 -0
- package/dist/esm/cloud/index.d.ts.map +1 -0
- package/dist/esm/cloud/index.js +4 -0
- package/dist/esm/cloud/index.js.map +1 -0
- package/dist/esm/cloud/server.d.ts +17 -0
- package/dist/esm/cloud/server.d.ts.map +1 -0
- package/dist/esm/cloud/server.js +181 -0
- package/dist/esm/cloud/server.js.map +1 -0
- package/dist/esm/cloud/types.d.ts +35 -0
- package/dist/esm/cloud/types.d.ts.map +1 -0
- package/dist/esm/cloud/types.js +11 -0
- package/dist/esm/cloud/types.js.map +1 -0
- package/dist/esm/config/index.d.ts +44 -0
- package/dist/esm/config/index.d.ts.map +1 -0
- package/dist/esm/config/index.js +151 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/consensus/ConsensusEngine.d.ts +119 -0
- package/dist/esm/consensus/ConsensusEngine.d.ts.map +1 -0
- package/dist/esm/consensus/ConsensusEngine.js +310 -0
- package/dist/esm/consensus/ConsensusEngine.js.map +1 -0
- package/dist/esm/consensus/ModelPool.d.ts +103 -0
- package/dist/esm/consensus/ModelPool.d.ts.map +1 -0
- package/dist/esm/consensus/ModelPool.js +269 -0
- package/dist/esm/consensus/ModelPool.js.map +1 -0
- package/dist/esm/consensus/index.d.ts +8 -0
- package/dist/esm/consensus/index.d.ts.map +1 -0
- package/dist/esm/consensus/index.js +8 -0
- package/dist/esm/consensus/index.js.map +1 -0
- package/dist/esm/core/AgentRegistry.d.ts +89 -0
- package/dist/esm/core/AgentRegistry.d.ts.map +1 -0
- package/dist/esm/core/AgentRegistry.js +260 -0
- package/dist/esm/core/AgentRegistry.js.map +1 -0
- package/dist/esm/core/DashboardBroadcaster.d.ts +67 -0
- package/dist/esm/core/DashboardBroadcaster.d.ts.map +1 -0
- package/dist/esm/core/DashboardBroadcaster.js +260 -0
- package/dist/esm/core/DashboardBroadcaster.js.map +1 -0
- package/dist/esm/core/OrchestrationEngine.d.ts +85 -0
- package/dist/esm/core/OrchestrationEngine.d.ts.map +1 -0
- package/dist/esm/core/OrchestrationEngine.js +457 -0
- package/dist/esm/core/OrchestrationEngine.js.map +1 -0
- package/dist/esm/core/SubconsciousEngine.d.ts +53 -0
- package/dist/esm/core/SubconsciousEngine.d.ts.map +1 -0
- package/dist/esm/core/SubconsciousEngine.js +278 -0
- package/dist/esm/core/SubconsciousEngine.js.map +1 -0
- package/dist/esm/core/WebSocketServer.d.ts +89 -0
- package/dist/esm/core/WebSocketServer.d.ts.map +1 -0
- package/dist/esm/core/WebSocketServer.js +380 -0
- package/dist/esm/core/WebSocketServer.js.map +1 -0
- package/dist/esm/core/index.d.ts +15 -0
- package/dist/esm/core/index.d.ts.map +1 -0
- package/dist/esm/core/index.js +15 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/core/router.d.ts +105 -0
- package/dist/esm/core/router.d.ts.map +1 -0
- package/dist/esm/core/router.js +416 -0
- package/dist/esm/core/router.js.map +1 -0
- package/dist/esm/embed/index.d.ts +218 -0
- package/dist/esm/embed/index.d.ts.map +1 -0
- package/dist/esm/embed/index.js +939 -0
- package/dist/esm/embed/index.js.map +1 -0
- package/dist/esm/embed.d.ts +11 -0
- package/dist/esm/embed.d.ts.map +1 -0
- package/dist/esm/embed.js +11 -0
- package/dist/esm/embed.js.map +1 -0
- package/dist/esm/examples/basic-usage.d.ts +12 -0
- package/dist/esm/examples/basic-usage.d.ts.map +1 -0
- package/dist/esm/examples/basic-usage.js +126 -0
- package/dist/esm/examples/basic-usage.js.map +1 -0
- package/dist/esm/index.d.ts +83 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +132 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/protocol/flows.d.ts +126 -0
- package/dist/esm/protocol/flows.d.ts.map +1 -0
- package/dist/esm/protocol/flows.js +338 -0
- package/dist/esm/protocol/flows.js.map +1 -0
- package/dist/esm/protocol/iap.d.ts +54 -0
- package/dist/esm/protocol/iap.d.ts.map +1 -0
- package/dist/esm/protocol/iap.js +104 -0
- package/dist/esm/protocol/iap.js.map +1 -0
- package/dist/esm/protocol/index.d.ts +23 -0
- package/dist/esm/protocol/index.d.ts.map +1 -0
- package/dist/esm/protocol/index.js +28 -0
- package/dist/esm/protocol/index.js.map +1 -0
- package/dist/esm/protocol/types.d.ts +332 -0
- package/dist/esm/protocol/types.d.ts.map +1 -0
- package/dist/esm/protocol/types.js +54 -0
- package/dist/esm/protocol/types.js.map +1 -0
- package/dist/esm/protocol/utils.d.ts +130 -0
- package/dist/esm/protocol/utils.d.ts.map +1 -0
- package/dist/esm/protocol/utils.js +260 -0
- package/dist/esm/protocol/utils.js.map +1 -0
- package/dist/esm/protocol/validators.d.ts +54 -0
- package/dist/esm/protocol/validators.d.ts.map +1 -0
- package/dist/esm/protocol/validators.js +332 -0
- package/dist/esm/protocol/validators.js.map +1 -0
- package/dist/esm/security/AuthManager.d.ts +73 -0
- package/dist/esm/security/AuthManager.d.ts.map +1 -0
- package/dist/esm/security/AuthManager.js +95 -0
- package/dist/esm/security/AuthManager.js.map +1 -0
- package/dist/esm/security/MessageSanitizer.d.ts +51 -0
- package/dist/esm/security/MessageSanitizer.d.ts.map +1 -0
- package/dist/esm/security/MessageSanitizer.js +178 -0
- package/dist/esm/security/MessageSanitizer.js.map +1 -0
- package/dist/esm/security/RateLimiter.d.ts +46 -0
- package/dist/esm/security/RateLimiter.d.ts.map +1 -0
- package/dist/esm/security/RateLimiter.js +129 -0
- package/dist/esm/security/RateLimiter.js.map +1 -0
- package/dist/esm/security/SecurityMiddleware.d.ts +90 -0
- package/dist/esm/security/SecurityMiddleware.d.ts.map +1 -0
- package/dist/esm/security/SecurityMiddleware.js +144 -0
- package/dist/esm/security/SecurityMiddleware.js.map +1 -0
- package/dist/esm/security/index.d.ts +35 -0
- package/dist/esm/security/index.d.ts.map +1 -0
- package/dist/esm/security/index.js +35 -0
- package/dist/esm/security/index.js.map +1 -0
- package/dist/esm/speculative/PredictionEngine.d.ts +99 -0
- package/dist/esm/speculative/PredictionEngine.d.ts.map +1 -0
- package/dist/esm/speculative/PredictionEngine.js +284 -0
- package/dist/esm/speculative/PredictionEngine.js.map +1 -0
- package/dist/esm/speculative/SpeculativeCache.d.ts +117 -0
- package/dist/esm/speculative/SpeculativeCache.d.ts.map +1 -0
- package/dist/esm/speculative/SpeculativeCache.js +288 -0
- package/dist/esm/speculative/SpeculativeCache.js.map +1 -0
- package/dist/esm/speculative/SpeculativeEngine.d.ts +114 -0
- package/dist/esm/speculative/SpeculativeEngine.d.ts.map +1 -0
- package/dist/esm/speculative/SpeculativeEngine.js +240 -0
- package/dist/esm/speculative/SpeculativeEngine.js.map +1 -0
- package/dist/esm/speculative/WorkerPool.d.ts +109 -0
- package/dist/esm/speculative/WorkerPool.d.ts.map +1 -0
- package/dist/esm/speculative/WorkerPool.js +323 -0
- package/dist/esm/speculative/WorkerPool.js.map +1 -0
- package/dist/esm/speculative/index.d.ts +10 -0
- package/dist/esm/speculative/index.d.ts.map +1 -0
- package/dist/esm/speculative/index.js +10 -0
- package/dist/esm/speculative/index.js.map +1 -0
- package/dist/esm/subconscious/EmbeddingService.d.ts +73 -0
- package/dist/esm/subconscious/EmbeddingService.d.ts.map +1 -0
- package/dist/esm/subconscious/EmbeddingService.js +189 -0
- package/dist/esm/subconscious/EmbeddingService.js.map +1 -0
- package/dist/esm/subconscious/SemanticCache.d.ts +82 -0
- package/dist/esm/subconscious/SemanticCache.d.ts.map +1 -0
- package/dist/esm/subconscious/SemanticCache.js +160 -0
- package/dist/esm/subconscious/SemanticCache.js.map +1 -0
- package/dist/esm/subconscious/SubconsciousEngine.d.ts +121 -0
- package/dist/esm/subconscious/SubconsciousEngine.d.ts.map +1 -0
- package/dist/esm/subconscious/SubconsciousEngine.js +237 -0
- package/dist/esm/subconscious/SubconsciousEngine.js.map +1 -0
- package/dist/esm/subconscious/VectorStore.d.ts +54 -0
- package/dist/esm/subconscious/VectorStore.d.ts.map +1 -0
- package/dist/esm/subconscious/VectorStore.js +164 -0
- package/dist/esm/subconscious/VectorStore.js.map +1 -0
- package/dist/esm/subconscious/cache.d.ts +34 -0
- package/dist/esm/subconscious/cache.d.ts.map +1 -0
- package/dist/esm/subconscious/cache.js +152 -0
- package/dist/{subconscious → esm/subconscious}/cache.js.map +1 -1
- package/dist/esm/subconscious/embeddings.d.ts +25 -0
- package/dist/esm/subconscious/embeddings.d.ts.map +1 -0
- package/dist/esm/subconscious/embeddings.js +58 -0
- package/dist/esm/subconscious/embeddings.js.map +1 -0
- package/dist/esm/subconscious/index.d.ts +12 -0
- package/dist/esm/subconscious/index.d.ts.map +1 -0
- package/dist/esm/subconscious/index.js +12 -0
- package/dist/esm/subconscious/index.js.map +1 -0
- package/dist/esm/types/index.d.ts +286 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +40 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +63 -0
- package/dist/esm/utils/logger.d.ts.map +1 -0
- package/dist/esm/utils/logger.js +122 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/package.json +77 -13
- package/dist/cloud/KeyManager.d.ts.map +0 -1
- package/dist/cloud/KeyManager.js.map +0 -1
- package/dist/cloud/TenantCache.d.ts.map +0 -1
- package/dist/cloud/TenantCache.js.map +0 -1
- package/dist/cloud/index.d.ts.map +0 -1
- package/dist/cloud/index.js.map +0 -1
- package/dist/cloud/server.d.ts.map +0 -1
- package/dist/cloud/server.js.map +0 -1
- package/dist/cloud/types.d.ts.map +0 -1
- package/dist/cloud/types.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/consensus/ConsensusEngine.d.ts.map +0 -1
- package/dist/consensus/ConsensusEngine.js.map +0 -1
- package/dist/consensus/ModelPool.d.ts.map +0 -1
- package/dist/consensus/ModelPool.js.map +0 -1
- package/dist/consensus/index.d.ts.map +0 -1
- package/dist/consensus/index.js.map +0 -1
- package/dist/core/AgentRegistry.d.ts.map +0 -1
- package/dist/core/AgentRegistry.js.map +0 -1
- package/dist/core/DashboardBroadcaster.d.ts.map +0 -1
- package/dist/core/DashboardBroadcaster.js.map +0 -1
- package/dist/core/OrchestrationEngine.d.ts.map +0 -1
- package/dist/core/OrchestrationEngine.js.map +0 -1
- package/dist/core/SubconsciousEngine.d.ts.map +0 -1
- package/dist/core/SubconsciousEngine.js.map +0 -1
- package/dist/core/WebSocketServer.d.ts.map +0 -1
- package/dist/core/WebSocketServer.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/router.d.ts.map +0 -1
- package/dist/core/router.js.map +0 -1
- package/dist/embed/index.d.ts.map +0 -1
- package/dist/embed/index.js +0 -408
- package/dist/embed/index.js.map +0 -1
- package/dist/embed.d.ts.map +0 -1
- package/dist/embed.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/protocol/flows.d.ts.map +0 -1
- package/dist/protocol/flows.js.map +0 -1
- package/dist/protocol/iap.d.ts.map +0 -1
- package/dist/protocol/iap.js.map +0 -1
- package/dist/protocol/index.d.ts.map +0 -1
- package/dist/protocol/index.js.map +0 -1
- package/dist/protocol/types.d.ts.map +0 -1
- package/dist/protocol/types.js.map +0 -1
- package/dist/protocol/utils.d.ts.map +0 -1
- package/dist/protocol/utils.js.map +0 -1
- package/dist/protocol/validators.d.ts.map +0 -1
- package/dist/protocol/validators.js.map +0 -1
- package/dist/security/AuthManager.d.ts.map +0 -1
- package/dist/security/AuthManager.js.map +0 -1
- package/dist/security/MessageSanitizer.d.ts.map +0 -1
- package/dist/security/MessageSanitizer.js.map +0 -1
- package/dist/security/RateLimiter.d.ts.map +0 -1
- package/dist/security/RateLimiter.js.map +0 -1
- package/dist/security/SecurityMiddleware.d.ts.map +0 -1
- package/dist/security/SecurityMiddleware.js.map +0 -1
- package/dist/security/index.d.ts.map +0 -1
- package/dist/security/index.js.map +0 -1
- package/dist/speculative/PredictionEngine.d.ts.map +0 -1
- package/dist/speculative/PredictionEngine.js.map +0 -1
- package/dist/speculative/SpeculativeCache.d.ts.map +0 -1
- package/dist/speculative/SpeculativeCache.js.map +0 -1
- package/dist/speculative/SpeculativeEngine.d.ts.map +0 -1
- package/dist/speculative/SpeculativeEngine.js.map +0 -1
- package/dist/speculative/WorkerPool.d.ts.map +0 -1
- package/dist/speculative/WorkerPool.js.map +0 -1
- package/dist/speculative/index.d.ts.map +0 -1
- package/dist/speculative/index.js.map +0 -1
- package/dist/subconscious/EmbeddingService.d.ts.map +0 -1
- package/dist/subconscious/EmbeddingService.js.map +0 -1
- package/dist/subconscious/SemanticCache.d.ts.map +0 -1
- package/dist/subconscious/SemanticCache.js.map +0 -1
- package/dist/subconscious/SubconsciousEngine.d.ts.map +0 -1
- package/dist/subconscious/SubconsciousEngine.js.map +0 -1
- package/dist/subconscious/VectorStore.d.ts.map +0 -1
- package/dist/subconscious/VectorStore.js.map +0 -1
- package/dist/subconscious/cache.d.ts.map +0 -1
- package/dist/subconscious/embeddings.d.ts.map +0 -1
- package/dist/subconscious/embeddings.js.map +0 -1
- package/dist/subconscious/index.d.ts.map +0 -1
- package/dist/subconscious/index.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- /package/dist/{cloud → cjs/cloud}/KeyManager.d.ts +0 -0
- /package/dist/{cloud → cjs/cloud}/KeyManager.js +0 -0
- /package/dist/{cloud → cjs/cloud}/TenantCache.d.ts +0 -0
- /package/dist/{cloud → cjs/cloud}/TenantCache.js +0 -0
- /package/dist/{cloud → cjs/cloud}/index.d.ts +0 -0
- /package/dist/{cloud → cjs/cloud}/index.js +0 -0
- /package/dist/{cloud → cjs/cloud}/server.d.ts +0 -0
- /package/dist/{cloud → cjs/cloud}/server.js +0 -0
- /package/dist/{cloud → cjs/cloud}/types.d.ts +0 -0
- /package/dist/{cloud → cjs/cloud}/types.js +0 -0
- /package/dist/{config → cjs/config}/index.d.ts +0 -0
- /package/dist/{config → cjs/config}/index.js +0 -0
- /package/dist/{consensus → cjs/consensus}/ConsensusEngine.d.ts +0 -0
- /package/dist/{consensus → cjs/consensus}/ConsensusEngine.js +0 -0
- /package/dist/{consensus → cjs/consensus}/ModelPool.d.ts +0 -0
- /package/dist/{consensus → cjs/consensus}/ModelPool.js +0 -0
- /package/dist/{consensus → cjs/consensus}/index.d.ts +0 -0
- /package/dist/{consensus → cjs/consensus}/index.js +0 -0
- /package/dist/{core → cjs/core}/AgentRegistry.d.ts +0 -0
- /package/dist/{core → cjs/core}/AgentRegistry.js +0 -0
- /package/dist/{core → cjs/core}/DashboardBroadcaster.d.ts +0 -0
- /package/dist/{core → cjs/core}/DashboardBroadcaster.js +0 -0
- /package/dist/{core → cjs/core}/SubconsciousEngine.d.ts +0 -0
- /package/dist/{core → cjs/core}/WebSocketServer.d.ts +0 -0
- /package/dist/{core → cjs/core}/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/index.js +0 -0
- /package/dist/{core → cjs/core}/router.d.ts +0 -0
- /package/dist/{core → cjs/core}/router.js +0 -0
- /package/dist/{embed.d.ts → cjs/embed.d.ts} +0 -0
- /package/dist/{embed.js → cjs/embed.js} +0 -0
- /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{protocol → cjs/protocol}/flows.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/flows.js +0 -0
- /package/dist/{protocol → cjs/protocol}/iap.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/iap.js +0 -0
- /package/dist/{protocol → cjs/protocol}/index.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/index.js +0 -0
- /package/dist/{protocol → cjs/protocol}/types.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/types.js +0 -0
- /package/dist/{protocol → cjs/protocol}/utils.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/utils.js +0 -0
- /package/dist/{protocol → cjs/protocol}/validators.d.ts +0 -0
- /package/dist/{protocol → cjs/protocol}/validators.js +0 -0
- /package/dist/{security → cjs/security}/AuthManager.d.ts +0 -0
- /package/dist/{security → cjs/security}/AuthManager.js +0 -0
- /package/dist/{security → cjs/security}/MessageSanitizer.d.ts +0 -0
- /package/dist/{security → cjs/security}/RateLimiter.d.ts +0 -0
- /package/dist/{security → cjs/security}/RateLimiter.js +0 -0
- /package/dist/{security → cjs/security}/index.d.ts +0 -0
- /package/dist/{security → cjs/security}/index.js +0 -0
- /package/dist/{speculative → cjs/speculative}/PredictionEngine.d.ts +0 -0
- /package/dist/{speculative → cjs/speculative}/PredictionEngine.js +0 -0
- /package/dist/{speculative → cjs/speculative}/SpeculativeCache.d.ts +0 -0
- /package/dist/{speculative → cjs/speculative}/SpeculativeCache.js +0 -0
- /package/dist/{speculative → cjs/speculative}/SpeculativeEngine.d.ts +0 -0
- /package/dist/{speculative → cjs/speculative}/SpeculativeEngine.js +0 -0
- /package/dist/{speculative → cjs/speculative}/WorkerPool.d.ts +0 -0
- /package/dist/{speculative → cjs/speculative}/WorkerPool.js +0 -0
- /package/dist/{speculative → cjs/speculative}/index.d.ts +0 -0
- /package/dist/{speculative → cjs/speculative}/index.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/EmbeddingService.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/EmbeddingService.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/SemanticCache.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/SemanticCache.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/SubconsciousEngine.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/SubconsciousEngine.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/VectorStore.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/cache.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/cache.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/embeddings.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/embeddings.js +0 -0
- /package/dist/{subconscious → cjs/subconscious}/index.d.ts +0 -0
- /package/dist/{subconscious → cjs/subconscious}/index.js +0 -0
- /package/dist/{types → cjs/types}/index.d.ts +0 -0
- /package/dist/{types → cjs/types}/index.js +0 -0
- /package/dist/{utils → cjs/utils}/logger.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/logger.js +0 -0
|
@@ -0,0 +1,939 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lemma Embedded Mode
|
|
3
|
+
*
|
|
4
|
+
* Zero-config semantic cache for any project.
|
|
5
|
+
* No separate server, no WebSocket, no agents required.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { Lemma } from 'lemma/embed';
|
|
9
|
+
*
|
|
10
|
+
* const lemma = await Lemma.create();
|
|
11
|
+
* const suggest = lemma.wrap(myOpenAIFunction);
|
|
12
|
+
* const result = await suggest('pollo y arroz'); // cached after first call
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
import crypto from 'crypto';
|
|
16
|
+
// ─── Circuit Breaker ──────────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Circuit Breaker implementation for backend resilience.
|
|
19
|
+
*
|
|
20
|
+
* States:
|
|
21
|
+
* - CLOSED: Normal operation, requests pass through
|
|
22
|
+
* - OPEN: Too many failures, requests fail fast
|
|
23
|
+
* - HALF_OPEN: Testing if backend recovered, limited requests allowed
|
|
24
|
+
*/
|
|
25
|
+
class BackendCircuitBreaker {
|
|
26
|
+
constructor(failureThreshold = 3, recoveryTimeout = 30000, successThreshold = 2) {
|
|
27
|
+
this.state = 'CLOSED';
|
|
28
|
+
this.failureCount = 0;
|
|
29
|
+
this.successCount = 0;
|
|
30
|
+
this.lastFailureTime = null;
|
|
31
|
+
this.failureThreshold = failureThreshold;
|
|
32
|
+
this.recoveryTimeout = recoveryTimeout;
|
|
33
|
+
this.successThreshold = successThreshold;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if a request should be allowed through.
|
|
37
|
+
*/
|
|
38
|
+
canAttempt() {
|
|
39
|
+
if (this.state === 'CLOSED')
|
|
40
|
+
return true;
|
|
41
|
+
if (this.state === 'OPEN') {
|
|
42
|
+
// Check if enough time has passed to try recovery
|
|
43
|
+
if (this.lastFailureTime && Date.now() - this.lastFailureTime >= this.recoveryTimeout) {
|
|
44
|
+
this.state = 'HALF_OPEN';
|
|
45
|
+
this.successCount = 0;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// HALF_OPEN: allow limited requests to test recovery
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Record a successful operation.
|
|
55
|
+
*/
|
|
56
|
+
recordSuccess() {
|
|
57
|
+
this.failureCount = 0;
|
|
58
|
+
if (this.state === 'HALF_OPEN') {
|
|
59
|
+
this.successCount++;
|
|
60
|
+
if (this.successCount >= this.successThreshold) {
|
|
61
|
+
this.state = 'CLOSED';
|
|
62
|
+
this.successCount = 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Record a failed operation.
|
|
68
|
+
*/
|
|
69
|
+
recordFailure() {
|
|
70
|
+
this.failureCount++;
|
|
71
|
+
this.lastFailureTime = Date.now();
|
|
72
|
+
if (this.state === 'HALF_OPEN') {
|
|
73
|
+
// Failed during recovery test, go back to OPEN
|
|
74
|
+
this.state = 'OPEN';
|
|
75
|
+
this.successCount = 0;
|
|
76
|
+
}
|
|
77
|
+
else if (this.failureCount >= this.failureThreshold) {
|
|
78
|
+
this.state = 'OPEN';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get current health status.
|
|
83
|
+
*/
|
|
84
|
+
getHealth() {
|
|
85
|
+
return {
|
|
86
|
+
state: this.state,
|
|
87
|
+
failureCount: this.failureCount,
|
|
88
|
+
lastFailure: this.lastFailureTime,
|
|
89
|
+
successCount: this.successCount,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Reset the circuit breaker to initial state.
|
|
94
|
+
*/
|
|
95
|
+
reset() {
|
|
96
|
+
this.state = 'CLOSED';
|
|
97
|
+
this.failureCount = 0;
|
|
98
|
+
this.successCount = 0;
|
|
99
|
+
this.lastFailureTime = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resilient wrapper for backends with circuit breaker, retries, and fallback.
|
|
104
|
+
*
|
|
105
|
+
* Features:
|
|
106
|
+
* - Circuit breaker to prevent cascading failures
|
|
107
|
+
* - Exponential backoff retries
|
|
108
|
+
* - Automatic fallback: Cloud → Chroma → Memory
|
|
109
|
+
* - Health monitoring and events
|
|
110
|
+
*/
|
|
111
|
+
class ResilientBackend extends EventEmitter {
|
|
112
|
+
constructor(primary, fallbacks, options = {}) {
|
|
113
|
+
super();
|
|
114
|
+
this.totalFailures = 0;
|
|
115
|
+
this.primary = primary;
|
|
116
|
+
this.fallbacks = fallbacks;
|
|
117
|
+
this.currentBackend = primary;
|
|
118
|
+
this.enableFallback = options.enableFallback ?? true;
|
|
119
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
120
|
+
this.retryDelay = options.retryDelay ?? 1000;
|
|
121
|
+
this.circuitBreaker = new BackendCircuitBreaker(options.failureThreshold ?? 3, options.recoveryTimeout ?? 30000);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Execute an operation with retry logic and circuit breaker.
|
|
125
|
+
*/
|
|
126
|
+
async executeWithRetry(operation, operationName) {
|
|
127
|
+
let lastError = null;
|
|
128
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
129
|
+
// Check circuit breaker
|
|
130
|
+
if (!this.circuitBreaker.canAttempt()) {
|
|
131
|
+
const error = new Error(`Circuit breaker OPEN for ${operationName}`);
|
|
132
|
+
this.emit('backend-error', { operation: operationName, error, attempt });
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const result = await operation();
|
|
137
|
+
this.circuitBreaker.recordSuccess();
|
|
138
|
+
// If we recovered from a previous failure
|
|
139
|
+
if (this.totalFailures > 0 && this.currentBackend === this.primary) {
|
|
140
|
+
this.emit('backend-recovered', { backend: this.getBackendName() });
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
lastError = error;
|
|
146
|
+
this.circuitBreaker.recordFailure();
|
|
147
|
+
this.totalFailures++;
|
|
148
|
+
this.emit('backend-error', {
|
|
149
|
+
operation: operationName,
|
|
150
|
+
error,
|
|
151
|
+
attempt: attempt + 1,
|
|
152
|
+
maxRetries: this.maxRetries,
|
|
153
|
+
});
|
|
154
|
+
// Don't retry on last attempt
|
|
155
|
+
if (attempt < this.maxRetries) {
|
|
156
|
+
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
157
|
+
await this.sleep(delay);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// All retries exhausted, try fallback
|
|
162
|
+
if (this.enableFallback && this.fallbacks.length > 0) {
|
|
163
|
+
return this.tryFallback(operation, operationName);
|
|
164
|
+
}
|
|
165
|
+
throw lastError || new Error(`${operationName} failed after ${this.maxRetries} retries`);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Try fallback backends in order.
|
|
169
|
+
*/
|
|
170
|
+
async tryFallback(operation, operationName) {
|
|
171
|
+
for (const fallback of this.fallbacks) {
|
|
172
|
+
try {
|
|
173
|
+
this.currentBackend = fallback;
|
|
174
|
+
this.emit('backend-degraded', {
|
|
175
|
+
from: this.getBackendName(this.primary),
|
|
176
|
+
to: this.getBackendName(fallback),
|
|
177
|
+
});
|
|
178
|
+
const result = await operation();
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
this.emit('backend-error', {
|
|
183
|
+
operation: operationName,
|
|
184
|
+
error,
|
|
185
|
+
backend: this.getBackendName(fallback),
|
|
186
|
+
});
|
|
187
|
+
// Continue to next fallback
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`All backends failed for ${operationName}`);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Set operation with resilience.
|
|
194
|
+
*/
|
|
195
|
+
async set(key, input, data, ttl) {
|
|
196
|
+
return this.executeWithRetry(() => this.currentBackend.set(key, input, data, ttl), 'set');
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Find similar operation with resilience.
|
|
200
|
+
*/
|
|
201
|
+
async findSimilar(input, threshold) {
|
|
202
|
+
return this.executeWithRetry(() => this.currentBackend.findSimilar(input, threshold), 'findSimilar');
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get backend health status.
|
|
206
|
+
*/
|
|
207
|
+
getHealth() {
|
|
208
|
+
return {
|
|
209
|
+
...this.circuitBreaker.getHealth(),
|
|
210
|
+
currentBackend: this.getBackendName(),
|
|
211
|
+
totalFailures: this.totalFailures,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get current backend name.
|
|
216
|
+
*/
|
|
217
|
+
getBackendName(backend) {
|
|
218
|
+
const b = backend || this.currentBackend;
|
|
219
|
+
if (b instanceof MemoryBackend)
|
|
220
|
+
return 'memory';
|
|
221
|
+
if (b instanceof ChromaBackend)
|
|
222
|
+
return 'chroma';
|
|
223
|
+
if (b instanceof CloudBackend)
|
|
224
|
+
return 'cloud';
|
|
225
|
+
return 'unknown';
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Reset circuit breaker and switch back to primary.
|
|
229
|
+
*/
|
|
230
|
+
reset() {
|
|
231
|
+
this.circuitBreaker.reset();
|
|
232
|
+
this.currentBackend = this.primary;
|
|
233
|
+
this.totalFailures = 0;
|
|
234
|
+
}
|
|
235
|
+
size() {
|
|
236
|
+
return this.currentBackend.size();
|
|
237
|
+
}
|
|
238
|
+
clear() {
|
|
239
|
+
this.currentBackend.clear();
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Stop all backends and cleanup resources.
|
|
243
|
+
*/
|
|
244
|
+
stop() {
|
|
245
|
+
// Stop primary backend if it has a stop method
|
|
246
|
+
if (this.primary instanceof MemoryBackend) {
|
|
247
|
+
this.primary.stop();
|
|
248
|
+
}
|
|
249
|
+
// Stop fallback backends
|
|
250
|
+
for (const fallback of this.fallbacks) {
|
|
251
|
+
if (fallback instanceof MemoryBackend) {
|
|
252
|
+
fallback.stop();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
sleep(ms) {
|
|
257
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
class MemoryBackend {
|
|
261
|
+
constructor(maxSize = 10000, cleanupInterval = 60000, debug = false) {
|
|
262
|
+
this.store = new Map();
|
|
263
|
+
this.cleanupTimer = null;
|
|
264
|
+
this.evictedCount = 0;
|
|
265
|
+
this.lastCleanupAt = 0;
|
|
266
|
+
this.maxSize = maxSize;
|
|
267
|
+
this.cleanupInterval = cleanupInterval;
|
|
268
|
+
this.debug = debug;
|
|
269
|
+
// Start cleanup timer if interval > 0
|
|
270
|
+
if (this.cleanupInterval > 0) {
|
|
271
|
+
this.startCleanupTimer();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
startCleanupTimer() {
|
|
275
|
+
this.cleanupTimer = setInterval(() => {
|
|
276
|
+
this.cleanup();
|
|
277
|
+
}, this.cleanupInterval);
|
|
278
|
+
// Prevent timer from keeping process alive
|
|
279
|
+
if (this.cleanupTimer.unref) {
|
|
280
|
+
this.cleanupTimer.unref();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Clean up expired entries from the store
|
|
285
|
+
*/
|
|
286
|
+
cleanup() {
|
|
287
|
+
const now = Date.now();
|
|
288
|
+
let evicted = 0;
|
|
289
|
+
for (const [key, entry] of this.store.entries()) {
|
|
290
|
+
if (entry.ttl > 0 && now - entry.createdAt > entry.ttl) {
|
|
291
|
+
this.store.delete(key);
|
|
292
|
+
evicted++;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (evicted > 0) {
|
|
296
|
+
this.evictedCount += evicted;
|
|
297
|
+
this.lastCleanupAt = now;
|
|
298
|
+
if (this.debug) {
|
|
299
|
+
console.log(`[Lemma] 🧹 Cleanup: evicted ${evicted} expired entries (total: ${this.evictedCount})`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return evicted;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Stop cleanup timer for graceful shutdown
|
|
306
|
+
*/
|
|
307
|
+
stop() {
|
|
308
|
+
if (this.cleanupTimer) {
|
|
309
|
+
clearInterval(this.cleanupTimer);
|
|
310
|
+
this.cleanupTimer = null;
|
|
311
|
+
if (this.debug) {
|
|
312
|
+
console.log('[Lemma] 🛑 Cleanup timer stopped');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async set(key, input, data, ttl) {
|
|
317
|
+
// Evict oldest entry if at capacity
|
|
318
|
+
if (this.store.size >= this.maxSize) {
|
|
319
|
+
const oldest = this.store.keys().next().value;
|
|
320
|
+
if (oldest) {
|
|
321
|
+
this.store.delete(oldest);
|
|
322
|
+
this.evictedCount++;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
this.store.set(key, { key, input, data, createdAt: Date.now(), ttl });
|
|
326
|
+
}
|
|
327
|
+
async get(key) {
|
|
328
|
+
const entry = this.store.get(key);
|
|
329
|
+
if (!entry)
|
|
330
|
+
return null;
|
|
331
|
+
if (entry.ttl > 0 && Date.now() - entry.createdAt > entry.ttl) {
|
|
332
|
+
this.store.delete(key);
|
|
333
|
+
this.evictedCount++;
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
return entry.data;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Exact key lookup — used for memory mode (no vector similarity)
|
|
340
|
+
*/
|
|
341
|
+
async findSimilar(input, _threshold) {
|
|
342
|
+
// In memory mode we do exact normalized match
|
|
343
|
+
const normalized = normalizeText(input);
|
|
344
|
+
for (const entry of this.store.values()) {
|
|
345
|
+
// Check if expired
|
|
346
|
+
if (entry.ttl > 0 && Date.now() - entry.createdAt > entry.ttl) {
|
|
347
|
+
this.store.delete(entry.key);
|
|
348
|
+
this.evictedCount++;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (normalizeText(entry.input) === normalized) {
|
|
352
|
+
return { data: entry.data, similarity: 1.0 };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
size() {
|
|
358
|
+
return this.store.size;
|
|
359
|
+
}
|
|
360
|
+
getEvictedCount() {
|
|
361
|
+
return this.evictedCount;
|
|
362
|
+
}
|
|
363
|
+
getLastCleanupAt() {
|
|
364
|
+
return this.lastCleanupAt;
|
|
365
|
+
}
|
|
366
|
+
clear() {
|
|
367
|
+
this.store.clear();
|
|
368
|
+
this.evictedCount = 0;
|
|
369
|
+
this.lastCleanupAt = 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
class SemanticMemoryBackend {
|
|
373
|
+
constructor(maxSize = 10000, modelName = 'Xenova/all-MiniLM-L6-v2') {
|
|
374
|
+
this.store = new Map();
|
|
375
|
+
this.pipeline = null;
|
|
376
|
+
this.embeddingCache = new Map();
|
|
377
|
+
this.evictedCount = 0;
|
|
378
|
+
this.maxSize = maxSize;
|
|
379
|
+
this.modelName = modelName;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Lazy-load the transformers.js pipeline
|
|
383
|
+
*/
|
|
384
|
+
async initialize() {
|
|
385
|
+
if (this.pipeline)
|
|
386
|
+
return;
|
|
387
|
+
try {
|
|
388
|
+
// @ts-ignore - Optional dependency
|
|
389
|
+
const { pipeline } = await import('@xenova/transformers');
|
|
390
|
+
this.pipeline = await pipeline('feature-extraction', this.modelName);
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
throw new Error(`Lemma: Could not load @xenova/transformers.\n` +
|
|
394
|
+
`Install it with: npm install @xenova/transformers\n` +
|
|
395
|
+
`Original error: ${err.message}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async set(key, input, data, ttl) {
|
|
399
|
+
await this.initialize();
|
|
400
|
+
// Evict oldest entry if at capacity
|
|
401
|
+
if (this.store.size >= this.maxSize) {
|
|
402
|
+
const oldest = this.store.keys().next().value;
|
|
403
|
+
if (oldest) {
|
|
404
|
+
this.store.delete(oldest);
|
|
405
|
+
this.evictedCount++;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const embedding = await this.embed(input);
|
|
409
|
+
this.store.set(key, {
|
|
410
|
+
key,
|
|
411
|
+
input,
|
|
412
|
+
embedding,
|
|
413
|
+
data,
|
|
414
|
+
createdAt: Date.now(),
|
|
415
|
+
ttl,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
async findSimilar(input, threshold) {
|
|
419
|
+
await this.initialize();
|
|
420
|
+
if (this.store.size === 0)
|
|
421
|
+
return null;
|
|
422
|
+
const queryEmbedding = await this.embed(input);
|
|
423
|
+
let bestMatch = null;
|
|
424
|
+
let bestSimilarity = -1;
|
|
425
|
+
for (const entry of this.store.values()) {
|
|
426
|
+
// Check TTL
|
|
427
|
+
if (entry.ttl > 0 && Date.now() - entry.createdAt > entry.ttl) {
|
|
428
|
+
this.store.delete(entry.key);
|
|
429
|
+
this.evictedCount++;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const similarity = cosineSimilarity(queryEmbedding, entry.embedding);
|
|
433
|
+
if (similarity > bestSimilarity && similarity >= threshold) {
|
|
434
|
+
bestSimilarity = similarity;
|
|
435
|
+
bestMatch = { data: entry.data, similarity };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return bestMatch;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Generate embedding with caching
|
|
442
|
+
*/
|
|
443
|
+
async embed(text) {
|
|
444
|
+
const normalized = normalizeText(text);
|
|
445
|
+
if (this.embeddingCache.has(normalized)) {
|
|
446
|
+
return this.embeddingCache.get(normalized);
|
|
447
|
+
}
|
|
448
|
+
const output = await this.pipeline(text, {
|
|
449
|
+
pooling: 'mean',
|
|
450
|
+
normalize: true,
|
|
451
|
+
});
|
|
452
|
+
// Convert tensor to array
|
|
453
|
+
const embedding = Array.from(output.data);
|
|
454
|
+
// Cache the embedding
|
|
455
|
+
this.embeddingCache.set(normalized, embedding);
|
|
456
|
+
// Limit cache size
|
|
457
|
+
if (this.embeddingCache.size > this.maxSize) {
|
|
458
|
+
const firstKey = this.embeddingCache.keys().next().value;
|
|
459
|
+
if (firstKey)
|
|
460
|
+
this.embeddingCache.delete(firstKey);
|
|
461
|
+
}
|
|
462
|
+
return embedding;
|
|
463
|
+
}
|
|
464
|
+
size() {
|
|
465
|
+
return this.store.size;
|
|
466
|
+
}
|
|
467
|
+
getEvictedCount() {
|
|
468
|
+
return this.evictedCount;
|
|
469
|
+
}
|
|
470
|
+
getLastCleanupAt() {
|
|
471
|
+
return 0; // Semantic backend doesn't have scheduled cleanup
|
|
472
|
+
}
|
|
473
|
+
stop() {
|
|
474
|
+
// No cleanup timer to stop in semantic backend
|
|
475
|
+
}
|
|
476
|
+
clear() {
|
|
477
|
+
this.store.clear();
|
|
478
|
+
this.embeddingCache.clear();
|
|
479
|
+
this.evictedCount = 0;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Calculate cosine similarity between two vectors
|
|
484
|
+
*/
|
|
485
|
+
function cosineSimilarity(a, b) {
|
|
486
|
+
if (a.length !== b.length) {
|
|
487
|
+
throw new Error('Vectors must have the same length');
|
|
488
|
+
}
|
|
489
|
+
let dotProduct = 0;
|
|
490
|
+
let normA = 0;
|
|
491
|
+
let normB = 0;
|
|
492
|
+
for (let i = 0; i < a.length; i++) {
|
|
493
|
+
dotProduct += a[i] * b[i];
|
|
494
|
+
normA += a[i] * a[i];
|
|
495
|
+
normB += b[i] * b[i];
|
|
496
|
+
}
|
|
497
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
498
|
+
if (denominator === 0)
|
|
499
|
+
return 0;
|
|
500
|
+
return dotProduct / denominator;
|
|
501
|
+
}
|
|
502
|
+
// ─── ChromaDB + Ollama Backend ────────────────────────────────────────────────
|
|
503
|
+
class ChromaBackend {
|
|
504
|
+
constructor(chromaUrl, ollamaUrl, model, collection) {
|
|
505
|
+
this.embeddingCache = new Map();
|
|
506
|
+
// Lazy-loaded to avoid import errors when not using chroma
|
|
507
|
+
this.chromaClient = null;
|
|
508
|
+
this.chromaCollection = null;
|
|
509
|
+
this.chromaUrl = chromaUrl;
|
|
510
|
+
this.ollamaUrl = ollamaUrl;
|
|
511
|
+
this.model = model;
|
|
512
|
+
this.collection = collection;
|
|
513
|
+
}
|
|
514
|
+
async initialize() {
|
|
515
|
+
try {
|
|
516
|
+
const { ChromaClient } = await import('chromadb');
|
|
517
|
+
this.chromaClient = new ChromaClient({ path: this.chromaUrl });
|
|
518
|
+
this.chromaCollection = await this.chromaClient.getOrCreateCollection({
|
|
519
|
+
name: this.collection,
|
|
520
|
+
metadata: { 'hnsw:space': 'cosine' },
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
throw new Error(`Lemma: Could not connect to ChromaDB at ${this.chromaUrl}.\n` +
|
|
525
|
+
`Make sure ChromaDB is running: chroma run --path ./chroma_data\n` +
|
|
526
|
+
`Original error: ${err.message}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async set(key, input, data, _ttl) {
|
|
530
|
+
const embedding = await this.embed(input);
|
|
531
|
+
await this.chromaCollection.add({
|
|
532
|
+
ids: [key],
|
|
533
|
+
embeddings: [embedding],
|
|
534
|
+
documents: [input],
|
|
535
|
+
metadatas: [{ data: JSON.stringify(data), timestamp: Date.now() }],
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
async findSimilar(input, threshold) {
|
|
539
|
+
const embedding = await this.embed(input);
|
|
540
|
+
const results = await this.chromaCollection.query({
|
|
541
|
+
queryEmbeddings: [embedding],
|
|
542
|
+
nResults: 1,
|
|
543
|
+
include: ['metadatas', 'distances'],
|
|
544
|
+
});
|
|
545
|
+
if (!results.ids?.[0]?.length)
|
|
546
|
+
return null;
|
|
547
|
+
const distance = results.distances[0][0];
|
|
548
|
+
const similarity = 1 - distance;
|
|
549
|
+
if (similarity < threshold)
|
|
550
|
+
return null;
|
|
551
|
+
const meta = results.metadatas[0][0];
|
|
552
|
+
return { data: JSON.parse(meta.data), similarity };
|
|
553
|
+
}
|
|
554
|
+
async embed(text) {
|
|
555
|
+
const key = normalizeText(text);
|
|
556
|
+
if (this.embeddingCache.has(key))
|
|
557
|
+
return this.embeddingCache.get(key);
|
|
558
|
+
const axios = (await import('axios')).default;
|
|
559
|
+
const res = await axios.post(`${this.ollamaUrl}/api/embeddings`, {
|
|
560
|
+
model: this.model,
|
|
561
|
+
prompt: text,
|
|
562
|
+
});
|
|
563
|
+
const embedding = res.data.embedding;
|
|
564
|
+
this.embeddingCache.set(key, embedding);
|
|
565
|
+
return embedding;
|
|
566
|
+
}
|
|
567
|
+
size() {
|
|
568
|
+
return 0; // ChromaDB doesn't expose sync count
|
|
569
|
+
}
|
|
570
|
+
getEvictedCount() {
|
|
571
|
+
return 0;
|
|
572
|
+
}
|
|
573
|
+
getLastCleanupAt() {
|
|
574
|
+
return 0;
|
|
575
|
+
}
|
|
576
|
+
stop() {
|
|
577
|
+
// No cleanup needed for ChromaDB
|
|
578
|
+
}
|
|
579
|
+
clear() {
|
|
580
|
+
this.embeddingCache.clear();
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// ─── Cloud Backend ────────────────────────────────────────────────────────────
|
|
584
|
+
class CloudBackend {
|
|
585
|
+
constructor(apiKey, cloudUrl) {
|
|
586
|
+
this.axios = null;
|
|
587
|
+
this.apiKey = apiKey;
|
|
588
|
+
this.cloudUrl = cloudUrl.replace(/\/$/, '');
|
|
589
|
+
}
|
|
590
|
+
async initialize() {
|
|
591
|
+
this.axios = (await import('axios')).default;
|
|
592
|
+
// Validate key on startup
|
|
593
|
+
try {
|
|
594
|
+
await this.axios.get(`${this.cloudUrl}/v1/ping`, {
|
|
595
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
596
|
+
timeout: 5000,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
catch (err) {
|
|
600
|
+
const status = err.response?.status;
|
|
601
|
+
if (status === 401)
|
|
602
|
+
throw new Error('Lemma Cloud: Invalid API key. Check your key at https://lemma.dev');
|
|
603
|
+
if (status === 403)
|
|
604
|
+
throw new Error('Lemma Cloud: API key suspended or plan limit reached.');
|
|
605
|
+
throw new Error(`Lemma Cloud: Could not connect to ${this.cloudUrl}. ${err.message}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async set(key, input, data, ttl) {
|
|
609
|
+
await this.axios.post(`${this.cloudUrl}/v1/cache`, { key, input, data, ttl }, { headers: { Authorization: `Bearer ${this.apiKey}` }, timeout: 10000 });
|
|
610
|
+
}
|
|
611
|
+
async findSimilar(input, threshold) {
|
|
612
|
+
try {
|
|
613
|
+
const res = await this.axios.post(`${this.cloudUrl}/v1/query`, { input, threshold }, { headers: { Authorization: `Bearer ${this.apiKey}` }, timeout: 10000 });
|
|
614
|
+
if (res.data?.hit) {
|
|
615
|
+
return { data: res.data.data, similarity: res.data.similarity };
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
catch (err) {
|
|
620
|
+
// On cloud error, degrade gracefully — treat as miss
|
|
621
|
+
if (err.response?.status === 429)
|
|
622
|
+
throw new Error('Lemma Cloud: Rate limit exceeded. Upgrade your plan at https://lemma.dev');
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
size() { return -1; }
|
|
627
|
+
getEvictedCount() { return 0; }
|
|
628
|
+
getLastCleanupAt() { return 0; }
|
|
629
|
+
stop() {
|
|
630
|
+
// No cleanup needed for cloud backend
|
|
631
|
+
}
|
|
632
|
+
clear() { }
|
|
633
|
+
}
|
|
634
|
+
export class Lemma extends EventEmitter {
|
|
635
|
+
constructor(config) {
|
|
636
|
+
super();
|
|
637
|
+
this.metrics = { hits: 0, misses: 0, totalLatency: 0 };
|
|
638
|
+
this.ready = false;
|
|
639
|
+
this.config = config;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Create and initialize a Lemma instance.
|
|
643
|
+
*
|
|
644
|
+
* @example
|
|
645
|
+
* // Zero config — in-memory cache
|
|
646
|
+
* const lemma = await Lemma.create();
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* // Semantic cache with lightweight embeddings
|
|
650
|
+
* const lemma = await Lemma.create({
|
|
651
|
+
* storage: 'semantic',
|
|
652
|
+
* threshold: 0.85,
|
|
653
|
+
* });
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* // Persistent semantic cache with ChromaDB
|
|
657
|
+
* const lemma = await Lemma.create({
|
|
658
|
+
* storage: 'chroma',
|
|
659
|
+
* threshold: 0.90,
|
|
660
|
+
* });
|
|
661
|
+
*/
|
|
662
|
+
static async create(config = {}) {
|
|
663
|
+
const resolved = {
|
|
664
|
+
storage: config.storage ?? 'memory',
|
|
665
|
+
threshold: config.threshold ?? 0.92,
|
|
666
|
+
apiKey: config.apiKey ?? '',
|
|
667
|
+
cloudUrl: config.cloudUrl ?? 'https://api.lemma.dev',
|
|
668
|
+
chromaUrl: config.chromaUrl ?? 'http://localhost:8000',
|
|
669
|
+
ollamaUrl: config.ollamaUrl ?? 'http://localhost:11434',
|
|
670
|
+
ollamaModel: config.ollamaModel ?? 'llama3',
|
|
671
|
+
embeddingModel: config.embeddingModel ?? 'Xenova/all-MiniLM-L6-v2',
|
|
672
|
+
collection: config.collection ?? 'lemma_cache',
|
|
673
|
+
ttl: config.ttl ?? 0,
|
|
674
|
+
maxSize: config.maxSize ?? 10000,
|
|
675
|
+
cleanupInterval: config.cleanupInterval ?? 60000,
|
|
676
|
+
debug: config.debug ?? false,
|
|
677
|
+
enableFallback: config.enableFallback ?? true,
|
|
678
|
+
maxRetries: config.maxRetries ?? 3,
|
|
679
|
+
retryDelay: config.retryDelay ?? 1000,
|
|
680
|
+
};
|
|
681
|
+
const instance = new Lemma(resolved);
|
|
682
|
+
await instance.init();
|
|
683
|
+
return instance;
|
|
684
|
+
}
|
|
685
|
+
async init() {
|
|
686
|
+
let primary;
|
|
687
|
+
const fallbacks = [];
|
|
688
|
+
// Initialize primary backend based on storage config
|
|
689
|
+
if (this.config.storage === 'cloud') {
|
|
690
|
+
if (!this.config.apiKey) {
|
|
691
|
+
throw new Error('Lemma Cloud requires an API key. Get yours at https://lemma.dev\nUsage: Lemma.create({ storage: \'cloud\', apiKey: \'lk_...\' })');
|
|
692
|
+
}
|
|
693
|
+
const cloudBackend = new CloudBackend(this.config.apiKey, this.config.cloudUrl);
|
|
694
|
+
await cloudBackend.initialize();
|
|
695
|
+
primary = cloudBackend;
|
|
696
|
+
this.log('✅ Primary backend: cloud');
|
|
697
|
+
// Add fallbacks: Cloud → Chroma → Memory
|
|
698
|
+
if (this.config.enableFallback) {
|
|
699
|
+
try {
|
|
700
|
+
const chromaBackend = new ChromaBackend(this.config.chromaUrl, this.config.ollamaUrl, this.config.ollamaModel, this.config.collection);
|
|
701
|
+
await chromaBackend.initialize();
|
|
702
|
+
fallbacks.push(chromaBackend);
|
|
703
|
+
this.log('✅ Fallback backend: chroma');
|
|
704
|
+
}
|
|
705
|
+
catch (err) {
|
|
706
|
+
this.log(`⚠️ Chroma fallback unavailable: ${err.message}`);
|
|
707
|
+
}
|
|
708
|
+
const memoryBackend = new MemoryBackend(this.config.maxSize, this.config.cleanupInterval, this.config.debug);
|
|
709
|
+
fallbacks.push(memoryBackend);
|
|
710
|
+
this.log('✅ Fallback backend: memory');
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
else if (this.config.storage === 'semantic') {
|
|
714
|
+
// Try to use semantic backend, fallback to memory if transformers not available
|
|
715
|
+
try {
|
|
716
|
+
const semanticBackend = new SemanticMemoryBackend(this.config.maxSize, this.config.embeddingModel);
|
|
717
|
+
await semanticBackend.initialize();
|
|
718
|
+
primary = semanticBackend;
|
|
719
|
+
this.log(`✅ Primary backend: semantic (model: ${this.config.embeddingModel})`);
|
|
720
|
+
}
|
|
721
|
+
catch (err) {
|
|
722
|
+
this.log(`⚠️ Semantic backend unavailable: ${err.message}`);
|
|
723
|
+
this.log('⚠️ Falling back to exact-match memory backend');
|
|
724
|
+
primary = new MemoryBackend(this.config.maxSize, this.config.cleanupInterval, this.config.debug);
|
|
725
|
+
this.log('✅ Primary backend: memory (fallback mode)');
|
|
726
|
+
}
|
|
727
|
+
// Add memory fallback for semantic
|
|
728
|
+
if (this.config.enableFallback && !(primary instanceof MemoryBackend)) {
|
|
729
|
+
const memoryBackend = new MemoryBackend(this.config.maxSize, this.config.cleanupInterval, this.config.debug);
|
|
730
|
+
fallbacks.push(memoryBackend);
|
|
731
|
+
this.log('✅ Fallback backend: memory');
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else if (this.config.storage === 'chroma') {
|
|
735
|
+
const chromaBackend = new ChromaBackend(this.config.chromaUrl, this.config.ollamaUrl, this.config.ollamaModel, this.config.collection);
|
|
736
|
+
await chromaBackend.initialize();
|
|
737
|
+
primary = chromaBackend;
|
|
738
|
+
this.log('✅ Primary backend: chroma');
|
|
739
|
+
// Add memory fallback for chroma
|
|
740
|
+
if (this.config.enableFallback) {
|
|
741
|
+
const memoryBackend = new MemoryBackend(this.config.maxSize, this.config.cleanupInterval, this.config.debug);
|
|
742
|
+
fallbacks.push(memoryBackend);
|
|
743
|
+
this.log('✅ Fallback backend: memory');
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
primary = new MemoryBackend(this.config.maxSize, this.config.cleanupInterval, this.config.debug);
|
|
748
|
+
this.log('✅ Primary backend: memory');
|
|
749
|
+
}
|
|
750
|
+
// Wrap primary backend with ResilientBackend
|
|
751
|
+
this.backend = new ResilientBackend(primary, fallbacks, {
|
|
752
|
+
enableFallback: this.config.enableFallback,
|
|
753
|
+
maxRetries: this.config.maxRetries,
|
|
754
|
+
retryDelay: this.config.retryDelay,
|
|
755
|
+
});
|
|
756
|
+
// Forward backend events to Lemma instance
|
|
757
|
+
this.backend.on('backend-error', (data) => {
|
|
758
|
+
this.log(`❌ Backend error: ${data.operation} (attempt ${data.attempt}/${data.maxRetries})`);
|
|
759
|
+
this.emit('backend-error', data);
|
|
760
|
+
});
|
|
761
|
+
this.backend.on('backend-degraded', (data) => {
|
|
762
|
+
this.log(`⚠️ Backend degraded: ${data.from} → ${data.to}`);
|
|
763
|
+
this.emit('backend-degraded', data);
|
|
764
|
+
});
|
|
765
|
+
this.backend.on('backend-recovered', (data) => {
|
|
766
|
+
this.log(`✅ Backend recovered: ${data.backend}`);
|
|
767
|
+
this.emit('backend-recovered', data);
|
|
768
|
+
});
|
|
769
|
+
this.ready = true;
|
|
770
|
+
this.log('✅ Lemma ready with resilient backend');
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Wrap any async function with semantic caching.
|
|
774
|
+
* The first argument of the function is used as the cache key.
|
|
775
|
+
*
|
|
776
|
+
* @example
|
|
777
|
+
* const cachedSuggest = lemma.wrap(async (ingredients: string) => {
|
|
778
|
+
* return await openai.chat.completions.create({ ... });
|
|
779
|
+
* });
|
|
780
|
+
*
|
|
781
|
+
* await cachedSuggest('pollo y arroz'); // calls OpenAI
|
|
782
|
+
* await cachedSuggest('arroz con pollo'); // returns from cache ⚡
|
|
783
|
+
*/
|
|
784
|
+
wrap(fn) {
|
|
785
|
+
return async (...args) => {
|
|
786
|
+
const input = args[0];
|
|
787
|
+
return this.run(input, () => fn(...args));
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Manually check cache and run function if miss.
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* const result = await lemma.run(
|
|
795
|
+
* userMessage,
|
|
796
|
+
* () => openai.chat.completions.create({ ... })
|
|
797
|
+
* );
|
|
798
|
+
* if (result.fromCache) console.log('⚡ Saved an API call!');
|
|
799
|
+
*/
|
|
800
|
+
async run(input, fn) {
|
|
801
|
+
this.assertReady();
|
|
802
|
+
const start = Date.now();
|
|
803
|
+
// Check cache
|
|
804
|
+
const cached = await this.backend.findSimilar(input, this.config.threshold);
|
|
805
|
+
if (cached) {
|
|
806
|
+
const latencyMs = Date.now() - start;
|
|
807
|
+
this.metrics.hits++;
|
|
808
|
+
this.metrics.totalLatency += latencyMs;
|
|
809
|
+
this.log(`⚡ Cache hit (similarity: ${cached.similarity.toFixed(3)}, ${latencyMs}ms)`);
|
|
810
|
+
this.emit('hit', { input, similarity: cached.similarity, latencyMs });
|
|
811
|
+
return { data: cached.data, fromCache: true, similarity: cached.similarity, latencyMs };
|
|
812
|
+
}
|
|
813
|
+
// Cache miss — run the function
|
|
814
|
+
this.metrics.misses++;
|
|
815
|
+
const data = await fn();
|
|
816
|
+
const latencyMs = Date.now() - start;
|
|
817
|
+
this.metrics.totalLatency += latencyMs;
|
|
818
|
+
// Store result async (don't block the response)
|
|
819
|
+
const key = generateKey(input);
|
|
820
|
+
this.backend.set(key, input, data, this.config.ttl).catch((err) => {
|
|
821
|
+
this.log(`⚠️ Failed to store in cache: ${err.message}`);
|
|
822
|
+
});
|
|
823
|
+
this.log(`🔄 Cache miss (${latencyMs}ms) — stored for future use`);
|
|
824
|
+
this.emit('miss', { input, latencyMs });
|
|
825
|
+
return { data, fromCache: false, latencyMs };
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Manually get a value from cache.
|
|
829
|
+
* Returns null if not found.
|
|
830
|
+
*/
|
|
831
|
+
async get(input) {
|
|
832
|
+
this.assertReady();
|
|
833
|
+
const start = Date.now();
|
|
834
|
+
const cached = await this.backend.findSimilar(input, this.config.threshold);
|
|
835
|
+
if (!cached)
|
|
836
|
+
return null;
|
|
837
|
+
return {
|
|
838
|
+
data: cached.data,
|
|
839
|
+
fromCache: true,
|
|
840
|
+
similarity: cached.similarity,
|
|
841
|
+
latencyMs: Date.now() - start,
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Manually store a value in cache.
|
|
846
|
+
*/
|
|
847
|
+
async set(input, data) {
|
|
848
|
+
this.assertReady();
|
|
849
|
+
const key = generateKey(input);
|
|
850
|
+
await this.backend.set(key, input, data, this.config.ttl);
|
|
851
|
+
this.log(`📦 Stored: "${input.slice(0, 50)}..."`);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Get performance metrics.
|
|
855
|
+
*/
|
|
856
|
+
getMetrics() {
|
|
857
|
+
const total = this.metrics.hits + this.metrics.misses;
|
|
858
|
+
const health = this.backend.getHealth();
|
|
859
|
+
// Determine backend health status
|
|
860
|
+
let backendHealth = 'healthy';
|
|
861
|
+
if (health.state === 'OPEN') {
|
|
862
|
+
backendHealth = 'down';
|
|
863
|
+
}
|
|
864
|
+
else if (health.state === 'HALF_OPEN' || health.currentBackend !== this.getStorageType()) {
|
|
865
|
+
backendHealth = 'degraded';
|
|
866
|
+
}
|
|
867
|
+
return {
|
|
868
|
+
hits: this.metrics.hits,
|
|
869
|
+
misses: this.metrics.misses,
|
|
870
|
+
total,
|
|
871
|
+
hitRate: total > 0 ? this.metrics.hits / total : 0,
|
|
872
|
+
avgLatencyMs: total > 0 ? Math.round(this.metrics.totalLatency / total) : 0,
|
|
873
|
+
cacheSize: this.backend.size(),
|
|
874
|
+
backendHealth,
|
|
875
|
+
failureCount: health.totalFailures,
|
|
876
|
+
evictedCount: 0, // Will be implemented per backend
|
|
877
|
+
lastCleanupAt: 0, // Will be implemented per backend
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Get backend health information.
|
|
882
|
+
*/
|
|
883
|
+
getBackendHealth() {
|
|
884
|
+
return this.backend.getHealth();
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Get the configured storage type.
|
|
888
|
+
*/
|
|
889
|
+
getStorageType() {
|
|
890
|
+
return this.config.storage;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Clear all cached entries and reset metrics.
|
|
894
|
+
*/
|
|
895
|
+
clear() {
|
|
896
|
+
this.backend.clear();
|
|
897
|
+
this.metrics = { hits: 0, misses: 0, totalLatency: 0 };
|
|
898
|
+
this.log('🗑️ Cache cleared');
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Update similarity threshold at runtime.
|
|
902
|
+
*/
|
|
903
|
+
setThreshold(threshold) {
|
|
904
|
+
if (threshold < 0 || threshold > 1)
|
|
905
|
+
throw new Error('Threshold must be 0–1');
|
|
906
|
+
this.config.threshold = threshold;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Stop cleanup timer and perform graceful shutdown.
|
|
910
|
+
* Call this before your application exits to prevent memory leaks.
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* const lemma = await Lemma.create();
|
|
914
|
+
* // ... use lemma ...
|
|
915
|
+
* lemma.stop(); // cleanup before exit
|
|
916
|
+
*/
|
|
917
|
+
stop() {
|
|
918
|
+
this.backend.stop();
|
|
919
|
+
this.log('🛑 Lemma stopped');
|
|
920
|
+
}
|
|
921
|
+
assertReady() {
|
|
922
|
+
if (!this.ready)
|
|
923
|
+
throw new Error('Lemma not initialized. Use Lemma.create()');
|
|
924
|
+
}
|
|
925
|
+
log(msg) {
|
|
926
|
+
if (this.config.debug)
|
|
927
|
+
console.log(`[Lemma] ${msg}`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
931
|
+
function normalizeText(text) {
|
|
932
|
+
return text.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
933
|
+
}
|
|
934
|
+
function generateKey(input) {
|
|
935
|
+
return crypto.createHash('sha256').update(normalizeText(input)).digest('hex').slice(0, 16);
|
|
936
|
+
}
|
|
937
|
+
// ─── Default export ───────────────────────────────────────────────────────────
|
|
938
|
+
export default Lemma;
|
|
939
|
+
//# sourceMappingURL=index.js.map
|