@nxuss/lemma 0.1.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.
Files changed (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +311 -0
  3. package/dist/cloud/KeyManager.d.ts +29 -0
  4. package/dist/cloud/KeyManager.d.ts.map +1 -0
  5. package/dist/cloud/KeyManager.js +142 -0
  6. package/dist/cloud/KeyManager.js.map +1 -0
  7. package/dist/cloud/TenantCache.d.ts +29 -0
  8. package/dist/cloud/TenantCache.d.ts.map +1 -0
  9. package/dist/cloud/TenantCache.js +162 -0
  10. package/dist/cloud/TenantCache.js.map +1 -0
  11. package/dist/cloud/index.d.ts +4 -0
  12. package/dist/cloud/index.d.ts.map +1 -0
  13. package/dist/cloud/index.js +23 -0
  14. package/dist/cloud/index.js.map +1 -0
  15. package/dist/cloud/server.d.ts +17 -0
  16. package/dist/cloud/server.d.ts.map +1 -0
  17. package/dist/cloud/server.js +189 -0
  18. package/dist/cloud/server.js.map +1 -0
  19. package/dist/cloud/types.d.ts +35 -0
  20. package/dist/cloud/types.d.ts.map +1 -0
  21. package/dist/cloud/types.js +14 -0
  22. package/dist/cloud/types.js.map +1 -0
  23. package/dist/config/index.d.ts +44 -0
  24. package/dist/config/index.d.ts.map +1 -0
  25. package/dist/config/index.js +161 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/consensus/ConsensusEngine.d.ts +119 -0
  28. package/dist/consensus/ConsensusEngine.d.ts.map +1 -0
  29. package/dist/consensus/ConsensusEngine.js +314 -0
  30. package/dist/consensus/ConsensusEngine.js.map +1 -0
  31. package/dist/consensus/ModelPool.d.ts +103 -0
  32. package/dist/consensus/ModelPool.d.ts.map +1 -0
  33. package/dist/consensus/ModelPool.js +276 -0
  34. package/dist/consensus/ModelPool.js.map +1 -0
  35. package/dist/consensus/index.d.ts +8 -0
  36. package/dist/consensus/index.d.ts.map +1 -0
  37. package/dist/consensus/index.js +13 -0
  38. package/dist/consensus/index.js.map +1 -0
  39. package/dist/core/AgentRegistry.d.ts +89 -0
  40. package/dist/core/AgentRegistry.d.ts.map +1 -0
  41. package/dist/core/AgentRegistry.js +264 -0
  42. package/dist/core/AgentRegistry.js.map +1 -0
  43. package/dist/core/DashboardBroadcaster.d.ts +67 -0
  44. package/dist/core/DashboardBroadcaster.d.ts.map +1 -0
  45. package/dist/core/DashboardBroadcaster.js +264 -0
  46. package/dist/core/DashboardBroadcaster.js.map +1 -0
  47. package/dist/core/OrchestrationEngine.d.ts +84 -0
  48. package/dist/core/OrchestrationEngine.d.ts.map +1 -0
  49. package/dist/core/OrchestrationEngine.js +455 -0
  50. package/dist/core/OrchestrationEngine.js.map +1 -0
  51. package/dist/core/SubconsciousEngine.d.ts +53 -0
  52. package/dist/core/SubconsciousEngine.d.ts.map +1 -0
  53. package/dist/core/SubconsciousEngine.js +273 -0
  54. package/dist/core/SubconsciousEngine.js.map +1 -0
  55. package/dist/core/WebSocketServer.d.ts +89 -0
  56. package/dist/core/WebSocketServer.d.ts.map +1 -0
  57. package/dist/core/WebSocketServer.js +416 -0
  58. package/dist/core/WebSocketServer.js.map +1 -0
  59. package/dist/core/index.d.ts +15 -0
  60. package/dist/core/index.d.ts.map +1 -0
  61. package/dist/core/index.js +24 -0
  62. package/dist/core/index.js.map +1 -0
  63. package/dist/core/router.d.ts +105 -0
  64. package/dist/core/router.d.ts.map +1 -0
  65. package/dist/core/router.js +420 -0
  66. package/dist/core/router.js.map +1 -0
  67. package/dist/embed/index.d.ts +153 -0
  68. package/dist/embed/index.d.ts.map +1 -0
  69. package/dist/embed/index.js +408 -0
  70. package/dist/embed/index.js.map +1 -0
  71. package/dist/embed.d.ts +11 -0
  72. package/dist/embed.d.ts.map +1 -0
  73. package/dist/embed.js +19 -0
  74. package/dist/embed.js.map +1 -0
  75. package/dist/index.d.ts +83 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +176 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/protocol/flows.d.ts +126 -0
  80. package/dist/protocol/flows.d.ts.map +1 -0
  81. package/dist/protocol/flows.js +347 -0
  82. package/dist/protocol/flows.js.map +1 -0
  83. package/dist/protocol/iap.d.ts +54 -0
  84. package/dist/protocol/iap.d.ts.map +1 -0
  85. package/dist/protocol/iap.js +108 -0
  86. package/dist/protocol/iap.js.map +1 -0
  87. package/dist/protocol/index.d.ts +23 -0
  88. package/dist/protocol/index.d.ts.map +1 -0
  89. package/dist/protocol/index.js +45 -0
  90. package/dist/protocol/index.js.map +1 -0
  91. package/dist/protocol/types.d.ts +332 -0
  92. package/dist/protocol/types.d.ts.map +1 -0
  93. package/dist/protocol/types.js +57 -0
  94. package/dist/protocol/types.js.map +1 -0
  95. package/dist/protocol/utils.d.ts +130 -0
  96. package/dist/protocol/utils.d.ts.map +1 -0
  97. package/dist/protocol/utils.js +292 -0
  98. package/dist/protocol/utils.js.map +1 -0
  99. package/dist/protocol/validators.d.ts +54 -0
  100. package/dist/protocol/validators.d.ts.map +1 -0
  101. package/dist/protocol/validators.js +344 -0
  102. package/dist/protocol/validators.js.map +1 -0
  103. package/dist/security/AuthManager.d.ts +73 -0
  104. package/dist/security/AuthManager.d.ts.map +1 -0
  105. package/dist/security/AuthManager.js +102 -0
  106. package/dist/security/AuthManager.js.map +1 -0
  107. package/dist/security/MessageSanitizer.d.ts +51 -0
  108. package/dist/security/MessageSanitizer.d.ts.map +1 -0
  109. package/dist/security/MessageSanitizer.js +166 -0
  110. package/dist/security/MessageSanitizer.js.map +1 -0
  111. package/dist/security/RateLimiter.d.ts +46 -0
  112. package/dist/security/RateLimiter.d.ts.map +1 -0
  113. package/dist/security/RateLimiter.js +133 -0
  114. package/dist/security/RateLimiter.js.map +1 -0
  115. package/dist/security/SecurityMiddleware.d.ts +88 -0
  116. package/dist/security/SecurityMiddleware.d.ts.map +1 -0
  117. package/dist/security/SecurityMiddleware.js +146 -0
  118. package/dist/security/SecurityMiddleware.js.map +1 -0
  119. package/dist/security/index.d.ts +35 -0
  120. package/dist/security/index.d.ts.map +1 -0
  121. package/dist/security/index.js +42 -0
  122. package/dist/security/index.js.map +1 -0
  123. package/dist/speculative/PredictionEngine.d.ts +99 -0
  124. package/dist/speculative/PredictionEngine.d.ts.map +1 -0
  125. package/dist/speculative/PredictionEngine.js +291 -0
  126. package/dist/speculative/PredictionEngine.js.map +1 -0
  127. package/dist/speculative/SpeculativeCache.d.ts +117 -0
  128. package/dist/speculative/SpeculativeCache.d.ts.map +1 -0
  129. package/dist/speculative/SpeculativeCache.js +292 -0
  130. package/dist/speculative/SpeculativeCache.js.map +1 -0
  131. package/dist/speculative/SpeculativeEngine.d.ts +114 -0
  132. package/dist/speculative/SpeculativeEngine.d.ts.map +1 -0
  133. package/dist/speculative/SpeculativeEngine.js +244 -0
  134. package/dist/speculative/SpeculativeEngine.js.map +1 -0
  135. package/dist/speculative/WorkerPool.d.ts +109 -0
  136. package/dist/speculative/WorkerPool.d.ts.map +1 -0
  137. package/dist/speculative/WorkerPool.js +327 -0
  138. package/dist/speculative/WorkerPool.js.map +1 -0
  139. package/dist/speculative/index.d.ts +10 -0
  140. package/dist/speculative/index.d.ts.map +1 -0
  141. package/dist/speculative/index.js +17 -0
  142. package/dist/speculative/index.js.map +1 -0
  143. package/dist/subconscious/EmbeddingService.d.ts +73 -0
  144. package/dist/subconscious/EmbeddingService.d.ts.map +1 -0
  145. package/dist/subconscious/EmbeddingService.js +196 -0
  146. package/dist/subconscious/EmbeddingService.js.map +1 -0
  147. package/dist/subconscious/SemanticCache.d.ts +82 -0
  148. package/dist/subconscious/SemanticCache.d.ts.map +1 -0
  149. package/dist/subconscious/SemanticCache.js +164 -0
  150. package/dist/subconscious/SemanticCache.js.map +1 -0
  151. package/dist/subconscious/SubconsciousEngine.d.ts +121 -0
  152. package/dist/subconscious/SubconsciousEngine.d.ts.map +1 -0
  153. package/dist/subconscious/SubconsciousEngine.js +241 -0
  154. package/dist/subconscious/SubconsciousEngine.js.map +1 -0
  155. package/dist/subconscious/VectorStore.d.ts +54 -0
  156. package/dist/subconscious/VectorStore.d.ts.map +1 -0
  157. package/dist/subconscious/VectorStore.js +168 -0
  158. package/dist/subconscious/VectorStore.js.map +1 -0
  159. package/dist/subconscious/cache.d.ts +34 -0
  160. package/dist/subconscious/cache.d.ts.map +1 -0
  161. package/dist/subconscious/cache.js +156 -0
  162. package/dist/subconscious/cache.js.map +1 -0
  163. package/dist/subconscious/embeddings.d.ts +25 -0
  164. package/dist/subconscious/embeddings.d.ts.map +1 -0
  165. package/dist/subconscious/embeddings.js +65 -0
  166. package/dist/subconscious/embeddings.js.map +1 -0
  167. package/dist/subconscious/index.d.ts +12 -0
  168. package/dist/subconscious/index.d.ts.map +1 -0
  169. package/dist/subconscious/index.js +19 -0
  170. package/dist/subconscious/index.js.map +1 -0
  171. package/dist/types/index.d.ts +286 -0
  172. package/dist/types/index.d.ts.map +1 -0
  173. package/dist/types/index.js +43 -0
  174. package/dist/types/index.js.map +1 -0
  175. package/dist/utils/logger.d.ts +63 -0
  176. package/dist/utils/logger.d.ts.map +1 -0
  177. package/dist/utils/logger.js +126 -0
  178. package/dist/utils/logger.js.map +1 -0
  179. package/package.json +99 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nxus Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,311 @@
1
+ # Lemma
2
+
3
+ **Stop paying for the same LLM call twice.**
4
+
5
+ Lemma is a semantic cache for AI apps. It recognizes when two prompts mean the same thing and returns the cached response — no LLM call, no cost, no wait.
6
+
7
+ ```typescript
8
+ import { Lemma } from 'lemma/embed';
9
+
10
+ const lemma = await Lemma.create();
11
+ const ask = lemma.wrap(yourLLMFunction);
12
+
13
+ await ask('chicken and rice recipes'); // → calls your LLM, ~2s
14
+ await ask('rice with chicken ideas'); // → ⚡ cache hit, 0ms, $0
15
+ ```
16
+
17
+ ---
18
+
19
+ ## The problem
20
+
21
+ Every AI app has the same leak: users ask the same questions in different ways, and you pay full price every time.
22
+
23
+ ```
24
+ 1,000 users ask about "chicken recipes" → 1,000 LLM calls → $60/day
25
+ ```
26
+
27
+ Lemma understands that *"chicken and rice"*, *"rice with chicken"*, and *"what can I make with chicken and rice?"* are the same question. One LLM call. 999 cache hits.
28
+
29
+ ```
30
+ 1,000 users ask about "chicken recipes" → 1 LLM call + 999 cache hits → $4/day
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ npm install lemma
39
+ ```
40
+
41
+ No config. No server. No Docker. Works in any Node.js project.
42
+
43
+ ---
44
+
45
+ ## Quick start
46
+
47
+ ```typescript
48
+ import { Lemma } from 'lemma/embed';
49
+
50
+ const lemma = await Lemma.create();
51
+
52
+ const getAnswer = lemma.wrap(async (question: string) => {
53
+ return await llm.chat.completions.create({
54
+ model: 'gpt-4o-mini',
55
+ messages: [{ role: 'user', content: question }],
56
+ });
57
+ });
58
+
59
+ const r1 = await getAnswer('what is the capital of France?');
60
+ console.log(r1.fromCache); // false — first call
61
+
62
+ const r2 = await getAnswer('capital city of France?');
63
+ console.log(r2.fromCache); // true — ⚡ 0ms, $0
64
+ ```
65
+
66
+ ### Drop into an existing Express app
67
+
68
+ ```typescript
69
+ // Before — every request hits your LLM
70
+ app.post('/api/chat', async (req, res) => {
71
+ const response = await llm.chat.completions.create({ ... });
72
+ res.json(response);
73
+ });
74
+
75
+ // After — 3 lines changed, 70% fewer LLM calls
76
+ import { Lemma } from 'lemma/embed';
77
+ const lemma = await Lemma.create();
78
+ const cachedChat = lemma.wrap((msg: string) => llm.chat.completions.create({ ... }));
79
+
80
+ app.post('/api/chat', async (req, res) => {
81
+ const result = await cachedChat(req.body.message);
82
+ res.json(result.data);
83
+ });
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Storage modes
89
+
90
+ ### `memory` — Zero dependencies (default)
91
+
92
+ ```typescript
93
+ const lemma = await Lemma.create();
94
+ ```
95
+
96
+ - In-process, no external services
97
+ - Exact string matching (normalized)
98
+ - Resets on restart
99
+ - Best for: development, low-traffic apps, getting started
100
+
101
+ ### `chroma` — Persistent semantic cache
102
+
103
+ ```typescript
104
+ const lemma = await Lemma.create({
105
+ storage: 'chroma',
106
+ threshold: 0.90,
107
+ });
108
+ ```
109
+
110
+ - Persists across restarts
111
+ - True semantic matching via vector embeddings — catches paraphrases, typos, language variations
112
+ - Requires ChromaDB + Ollama running locally
113
+ - Best for: production apps where users rephrase the same questions
114
+
115
+ > **Note:** The "chicken and rice" → "rice with chicken" magic only works in `chroma` mode.
116
+ > Memory mode does exact normalized matching only.
117
+
118
+ ### `cloud` — Managed, zero infra
119
+
120
+ ```typescript
121
+ const lemma = await Lemma.create({
122
+ storage: 'cloud',
123
+ apiKey: 'lk_live_your_key_here',
124
+ });
125
+ ```
126
+
127
+ - No ChromaDB, no Ollama, no servers
128
+ - Persistent semantic cache hosted for you
129
+ - Works in serverless (Vercel, AWS Lambda, Cloudflare Workers)
130
+ - Get your key at [lemma.dev](https://lemma.dev)
131
+
132
+ ---
133
+
134
+ ## API
135
+
136
+ ### `Lemma.create(config?)`
137
+
138
+ ```typescript
139
+ const lemma = await Lemma.create({
140
+ storage: 'memory' | 'chroma' | 'cloud', // default: 'memory'
141
+ threshold: 0.92, // similarity cutoff (0–1)
142
+ apiKey: 'lk_live_...', // required for cloud mode
143
+ debug: false, // log hits/misses
144
+ ttl: 0, // cache TTL in ms (0 = forever)
145
+ maxSize: 10000, // max entries (memory mode)
146
+ });
147
+ ```
148
+
149
+ ### `lemma.wrap(fn)`
150
+
151
+ Wraps any async function. Uses the first argument as the cache key.
152
+
153
+ ```typescript
154
+ const cached = lemma.wrap(async (input: string, ...rest) => {
155
+ return await someExpensiveCall(input, ...rest);
156
+ });
157
+
158
+ const result = await cached('my query');
159
+ // result.data — the actual response
160
+ // result.fromCache — true if served from cache
161
+ // result.similarity — how similar to the cached entry (0–1)
162
+ // result.latencyMs — total time including cache lookup
163
+ ```
164
+
165
+ ### `lemma.run(input, fn)`
166
+
167
+ Manual control — check cache first, run function on miss.
168
+
169
+ ```typescript
170
+ const result = await lemma.run(
171
+ userMessage,
172
+ () => llm.chat.completions.create({ ... })
173
+ );
174
+
175
+ if (result.fromCache) {
176
+ console.log(`Saved an API call! Similarity: ${result.similarity}`);
177
+ }
178
+ ```
179
+
180
+ ### `lemma.get(input)` / `lemma.set(input, data)`
181
+
182
+ Direct cache access.
183
+
184
+ ```typescript
185
+ const cached = await lemma.get('what is machine learning?');
186
+ if (!cached) {
187
+ const data = await callYourLLM(...);
188
+ await lemma.set('what is machine learning?', data);
189
+ }
190
+ ```
191
+
192
+ ### `lemma.getMetrics()`
193
+
194
+ ```typescript
195
+ const metrics = lemma.getMetrics();
196
+ // {
197
+ // hits: 847,
198
+ // misses: 153,
199
+ // total: 1000,
200
+ // hitRate: 0.847,
201
+ // avgLatencyMs: 12,
202
+ // cacheSize: 153
203
+ // }
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Who this is for
209
+
210
+ Lemma pays off fast if your app has any of these:
211
+
212
+ - **Repetitive user queries** — support bots, FAQ assistants, recipe/travel/fitness apps. Users ask the same things in different words constantly.
213
+ - **Multiple services calling the same LLM** — if 3 microservices independently ask similar questions, you're paying 3x. Lemma centralizes the cache.
214
+ - **LangChain or CrewAI pipelines** — agents in a pipeline often re-derive the same context. Lemma short-circuits that.
215
+ - **High-volume, cost-sensitive apps** — if your LLM bill is already noticeable, the ROI is immediate.
216
+
217
+ ### Real hit rates by app type
218
+
219
+ | App type | Avg hit rate | Savings on 50k req/mo |
220
+ |---|---|---|
221
+ | Customer support chatbot | 85% | ~$340/mo |
222
+ | Recipe / food suggestions | 78% | ~$180/mo |
223
+ | Location / places search | 82% | ~$290/mo |
224
+ | Code assistant | 65% | ~$120/mo |
225
+
226
+ *Based on gpt-4o-mini pricing at $0.002/1k tokens, avg 800 tokens/request.*
227
+
228
+ ---
229
+
230
+ ## For teams building multi-agent systems
231
+
232
+ If you're past the single-app use case and building systems where multiple AI agents collaborate, Lemma ships a full orchestration layer on top of the cache.
233
+
234
+ ```typescript
235
+ import { SubconsciousHub } from 'lemma';
236
+
237
+ const hub = new SubconsciousHub({ server: { port: 8080 } });
238
+ await hub.start();
239
+ // Agents connect via WebSocket and share a semantic cache
240
+ ```
241
+
242
+ What this adds:
243
+
244
+ - **WebSocket server** — persistent agent connections with heartbeat
245
+ - **Agent registry** — dynamic capability discovery and routing
246
+ - **Shared semantic cache** — all agents benefit from each other's work
247
+ - **Consensus engine** — multi-model voting for high-stakes decisions
248
+ - **Speculative execution** — pre-compute likely next requests
249
+ - **Security middleware** — API key auth, rate limiting, message sanitization
250
+
251
+ This layer is designed for teams already running LangChain, CrewAI, or custom agent frameworks who need coordination and shared memory across agents. See [docs/](./docs/) for the full architecture guide.
252
+
253
+ ---
254
+
255
+ ## Self-hosting the cloud server
256
+
257
+ ```bash
258
+ # 1. Build
259
+ npm run build
260
+
261
+ # 2. Deploy to any VPS
262
+ ./scripts/deploy-cloud.sh YOUR_SERVER_IP
263
+
264
+ # 3. Generate an API key
265
+ curl -X POST http://YOUR_SERVER_IP:3100/admin/keys \
266
+ -H 'X-Admin-Secret: YOUR_ADMIN_SECRET' \
267
+ -H 'Content-Type: application/json' \
268
+ -d '{"tenantId": "user1", "name": "My Project", "plan": "pro"}'
269
+
270
+ # 4. Use it
271
+ const lemma = await Lemma.create({
272
+ storage: 'cloud',
273
+ apiKey: 'lk_live_...',
274
+ cloudUrl: 'http://YOUR_SERVER_IP:3100',
275
+ });
276
+ ```
277
+
278
+ Requirements: Docker, ChromaDB, Ollama with `nomic-embed-text`.
279
+
280
+ ---
281
+
282
+ ## Pricing
283
+
284
+ | | Free | Pro | Scale |
285
+ |---|---|---|---|
286
+ | Requests/day | 1,000 | 100,000 | Unlimited |
287
+ | Semantic matching | ✅ | ✅ | ✅ |
288
+ | Persistent cache | ✅ | ✅ | ✅ |
289
+ | Savings dashboard | ✅ | ✅ | ✅ |
290
+ | Price | $0 | $29/mo | $99/mo |
291
+
292
+ [Get your API key →](https://lemma.dev)
293
+
294
+ ---
295
+
296
+ ## Contributing
297
+
298
+ ```bash
299
+ git clone https://github.com/Nxusbets/lemma.git
300
+ cd lemma
301
+ npm install
302
+ npm run build # compile TypeScript
303
+ npm test # run tests
304
+ npm run dev # start the orchestration server
305
+ ```
306
+
307
+ ---
308
+
309
+ ## License
310
+
311
+ MIT © [Nxus Studio](https://github.com/Nxusbets)
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lemma Cloud — API Key Manager
3
+ *
4
+ * Handles key generation, validation, usage tracking, and plan enforcement.
5
+ * Uses a JSON file as storage (swap for Redis/Postgres in production).
6
+ */
7
+ import { ApiKey } from './types';
8
+ export declare class KeyManager {
9
+ private keys;
10
+ private storePath;
11
+ private saveTimer;
12
+ constructor(storePath?: string);
13
+ generate(tenantId: string, name: string, plan?: ApiKey['plan']): ApiKey;
14
+ validate(keyId: string): {
15
+ valid: boolean;
16
+ key?: ApiKey;
17
+ reason?: string;
18
+ };
19
+ recordRequest(keyId: string): void;
20
+ upgradePlan(keyId: string, plan: ApiKey['plan']): boolean;
21
+ revoke(keyId: string): boolean;
22
+ getByTenant(tenantId: string): ApiKey[];
23
+ get(keyId: string): ApiKey | undefined;
24
+ private maybeResetDaily;
25
+ private scheduleSave;
26
+ private save;
27
+ private load;
28
+ }
29
+ //# sourceMappingURL=KeyManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyManager.d.ts","sourceRoot":"","sources":["../../src/cloud/KeyManager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,MAAM,EAAe,MAAM,SAAS,CAAC;AAE9C,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA+B;gBAEpC,SAAS,SAAqB;IAO1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,CAAU,GAAG,MAAM;IAkB/E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IA4B1E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAUlC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO;IAQzD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ9B,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;IAIvC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMtC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,IAAI;CAYb"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * Lemma Cloud — API Key Manager
4
+ *
5
+ * Handles key generation, validation, usage tracking, and plan enforcement.
6
+ * Uses a JSON file as storage (swap for Redis/Postgres in production).
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KeyManager = void 0;
13
+ const crypto_1 = __importDefault(require("crypto"));
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const types_1 = require("./types");
17
+ class KeyManager {
18
+ constructor(storePath = './data/keys.json') {
19
+ this.keys = new Map();
20
+ this.saveTimer = null;
21
+ this.storePath = storePath;
22
+ this.load();
23
+ }
24
+ // ─── Key Generation ─────────────────────────────────────────────────────────
25
+ generate(tenantId, name, plan = 'free') {
26
+ const id = `lk_${plan === 'free' ? 'free' : 'live'}_${crypto_1.default.randomBytes(16).toString('hex')}`;
27
+ const key = {
28
+ id,
29
+ tenantId,
30
+ name,
31
+ plan,
32
+ createdAt: Date.now(),
33
+ active: true,
34
+ usage: { today: 0, total: 0, lastReset: Date.now() },
35
+ };
36
+ this.keys.set(id, key);
37
+ this.scheduleSave();
38
+ return key;
39
+ }
40
+ // ─── Validation ─────────────────────────────────────────────────────────────
41
+ validate(keyId) {
42
+ const key = this.keys.get(keyId);
43
+ if (!key)
44
+ return { valid: false, reason: 'Invalid API key' };
45
+ if (!key.active)
46
+ return { valid: false, reason: 'API key is suspended' };
47
+ if (key.expiresAt && Date.now() > key.expiresAt) {
48
+ return { valid: false, reason: 'API key has expired' };
49
+ }
50
+ // Reset daily counter if needed
51
+ this.maybeResetDaily(key);
52
+ // Check plan limits
53
+ const limits = types_1.PLAN_LIMITS[key.plan];
54
+ if (key.usage.today >= limits.dailyRequests) {
55
+ return {
56
+ valid: false,
57
+ reason: key.plan === 'free'
58
+ ? 'Free tier limit reached (1,000 req/day). Upgrade at https://lemma.dev'
59
+ : 'Daily request limit reached. Contact support to increase limits.',
60
+ };
61
+ }
62
+ return { valid: true, key };
63
+ }
64
+ // ─── Usage Tracking ─────────────────────────────────────────────────────────
65
+ recordRequest(keyId) {
66
+ const key = this.keys.get(keyId);
67
+ if (!key)
68
+ return;
69
+ key.usage.today++;
70
+ key.usage.total++;
71
+ this.scheduleSave();
72
+ }
73
+ // ─── Plan Management ────────────────────────────────────────────────────────
74
+ upgradePlan(keyId, plan) {
75
+ const key = this.keys.get(keyId);
76
+ if (!key)
77
+ return false;
78
+ key.plan = plan;
79
+ this.scheduleSave();
80
+ return true;
81
+ }
82
+ revoke(keyId) {
83
+ const key = this.keys.get(keyId);
84
+ if (!key)
85
+ return false;
86
+ key.active = false;
87
+ this.scheduleSave();
88
+ return true;
89
+ }
90
+ getByTenant(tenantId) {
91
+ return Array.from(this.keys.values()).filter(k => k.tenantId === tenantId);
92
+ }
93
+ get(keyId) {
94
+ return this.keys.get(keyId);
95
+ }
96
+ // ─── Persistence ────────────────────────────────────────────────────────────
97
+ maybeResetDaily(key) {
98
+ const now = Date.now();
99
+ const midnight = new Date();
100
+ midnight.setUTCHours(0, 0, 0, 0);
101
+ if (key.usage.lastReset < midnight.getTime()) {
102
+ key.usage.today = 0;
103
+ key.usage.lastReset = now;
104
+ }
105
+ }
106
+ scheduleSave() {
107
+ if (this.saveTimer)
108
+ return;
109
+ this.saveTimer = setTimeout(() => {
110
+ this.save();
111
+ this.saveTimer = null;
112
+ }, 2000); // debounce writes
113
+ }
114
+ save() {
115
+ try {
116
+ const dir = path_1.default.dirname(this.storePath);
117
+ if (!fs_1.default.existsSync(dir))
118
+ fs_1.default.mkdirSync(dir, { recursive: true });
119
+ const data = JSON.stringify(Array.from(this.keys.entries()), null, 2);
120
+ fs_1.default.writeFileSync(this.storePath, data, 'utf-8');
121
+ }
122
+ catch (err) {
123
+ console.error('[KeyManager] Failed to save keys:', err);
124
+ }
125
+ }
126
+ load() {
127
+ try {
128
+ if (!fs_1.default.existsSync(this.storePath))
129
+ return;
130
+ const data = JSON.parse(fs_1.default.readFileSync(this.storePath, 'utf-8'));
131
+ for (const [id, key] of data) {
132
+ this.keys.set(id, key);
133
+ }
134
+ console.log(`[KeyManager] Loaded ${this.keys.size} keys`);
135
+ }
136
+ catch (err) {
137
+ console.error('[KeyManager] Failed to load keys:', err);
138
+ }
139
+ }
140
+ }
141
+ exports.KeyManager = KeyManager;
142
+ //# sourceMappingURL=KeyManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyManager.js","sourceRoot":"","sources":["../../src/cloud/KeyManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lemma Cloud — Tenant Cache Manager
3
+ *
4
+ * Each tenant gets an isolated ChromaDB collection + usage stats.
5
+ * Handles semantic search via Ollama embeddings.
6
+ */
7
+ import { TenantStats } from './types';
8
+ export declare class TenantCache {
9
+ private tenantId;
10
+ private chromaUrl;
11
+ private ollamaUrl;
12
+ private model;
13
+ private collectionName;
14
+ private chromaClient;
15
+ private chromaCollection;
16
+ private embeddingCache;
17
+ private stats;
18
+ constructor(tenantId: string, chromaUrl: string, ollamaUrl: string, model: string);
19
+ initialize(): Promise<void>;
20
+ query(input: string, threshold: number): Promise<{
21
+ hit: boolean;
22
+ data?: any;
23
+ similarity?: number;
24
+ }>;
25
+ store(key: string, input: string, data: any, ttl: number): Promise<void>;
26
+ getStats(): Promise<TenantStats>;
27
+ private embed;
28
+ }
29
+ //# sourceMappingURL=TenantCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantCache.d.ts","sourceRoot":"","sources":["../../src/cloud/TenantCache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,WAAW,EAAuD,MAAM,SAAS,CAAC;AAU3F,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,cAAc,CAA+B;IAErD,OAAO,CAAC,KAAK,CAA0B;gBAE3B,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAS3E,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,KAAK,CACT,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,GAAG,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA0CvD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxE,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;YAyBxB,KAAK;CAiBpB"}