@nano-llm-cache/core 1.0.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/LICENSE +21 -0
- package/README.md +445 -0
- package/dist/index.d.mts +152 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.js +452 -0
- package/dist/index.mjs +423 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nano-LLM-Cache Contributors
|
|
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,445 @@
|
|
|
1
|
+
# 🚀 Nano-LLM-Cache
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/nano-llm-cache)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> **A Semantic Cache for LLM API Calls** - Save money and improve response times by caching based on *meaning*, not exact matches.
|
|
7
|
+
|
|
8
|
+
## 🎯 What is Nano-LLM-Cache?
|
|
9
|
+
|
|
10
|
+
Nano-LLM-Cache is a TypeScript library that intercepts LLM API calls and returns cached responses based on **semantic similarity** rather than exact string matching. It uses local embeddings (running entirely in the browser/client-side) to understand the *meaning* of prompts.
|
|
11
|
+
|
|
12
|
+
### The Problem
|
|
13
|
+
|
|
14
|
+
Traditional caches look for exact key matches:
|
|
15
|
+
- ❌ "What is the weather in London?" → Cache MISS
|
|
16
|
+
- ❌ "Tell me the London weather" → Cache MISS (different string!)
|
|
17
|
+
|
|
18
|
+
### The Solution
|
|
19
|
+
|
|
20
|
+
Nano-LLM-Cache uses **vector embeddings** to understand meaning:
|
|
21
|
+
- ✅ "What is the weather in London?" → Cache HIT
|
|
22
|
+
- ✅ "Tell me the London weather" → Cache HIT (same meaning!)
|
|
23
|
+
|
|
24
|
+
## ✨ Features
|
|
25
|
+
|
|
26
|
+
- 🧠 **Semantic Understanding**: Matches prompts by meaning, not exact text
|
|
27
|
+
- 🔒 **Privacy-First**: Embeddings run locally - your data never leaves the device
|
|
28
|
+
- ⚡ **Fast & Lightweight**: Uses quantized models (~20MB, cached forever)
|
|
29
|
+
- 💾 **Persistent Storage**: IndexedDB for cross-session caching
|
|
30
|
+
- ⏰ **TTL Support**: Configurable time-to-live for cache entries
|
|
31
|
+
- 🔌 **Drop-in Replacement**: Works as an OpenAI SDK wrapper
|
|
32
|
+
- 📊 **Cache Analytics**: Built-in statistics and monitoring
|
|
33
|
+
- 🎨 **TypeScript**: Full type safety and IntelliSense support
|
|
34
|
+
|
|
35
|
+
## 📦 Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @nano-llm-cache/core
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { NanoCache } from '@nano-llm-cache/core';
|
|
47
|
+
|
|
48
|
+
// Create cache instance
|
|
49
|
+
const cache = new NanoCache({
|
|
50
|
+
similarityThreshold: 0.95, // 95% similarity required for cache hit
|
|
51
|
+
maxAge: 60 * 60 * 1000, // 1 hour TTL
|
|
52
|
+
debug: true // Enable logging
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Save a response
|
|
56
|
+
await cache.save(
|
|
57
|
+
'What is the weather in London?',
|
|
58
|
+
'The weather in London is cloudy with a chance of rain, 15°C.'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Query with similar prompt
|
|
62
|
+
const result = await cache.query('Tell me the London weather');
|
|
63
|
+
|
|
64
|
+
if (result.hit) {
|
|
65
|
+
console.log('Cache HIT!', result.response);
|
|
66
|
+
console.log('Similarity:', result.similarity); // 0.98
|
|
67
|
+
} else {
|
|
68
|
+
console.log('Cache MISS - call your LLM API');
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### OpenAI Wrapper (Drop-in Replacement)
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import OpenAI from 'openai';
|
|
76
|
+
import { NanoCache } from '@nano-llm-cache/core';
|
|
77
|
+
|
|
78
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
79
|
+
const cache = new NanoCache({ similarityThreshold: 0.95 });
|
|
80
|
+
|
|
81
|
+
// Wrap the OpenAI function
|
|
82
|
+
const cachedCreate = cache.createChatWrapper(
|
|
83
|
+
openai.chat.completions.create.bind(openai.chat.completions)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Use it exactly like the original!
|
|
87
|
+
const response = await cachedCreate({
|
|
88
|
+
model: 'gpt-4',
|
|
89
|
+
messages: [
|
|
90
|
+
{ role: 'user', content: 'How do I center a div?' }
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
console.log(response.choices[0].message.content);
|
|
95
|
+
|
|
96
|
+
// Second call with similar question - returns cached response instantly!
|
|
97
|
+
const response2 = await cachedCreate({
|
|
98
|
+
model: 'gpt-4',
|
|
99
|
+
messages: [
|
|
100
|
+
{ role: 'user', content: 'Best way to align a div to the middle?' }
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 📚 API Reference
|
|
106
|
+
|
|
107
|
+
### `NanoCache`
|
|
108
|
+
|
|
109
|
+
#### Constructor
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
new NanoCache(config?: NanoCacheConfig)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Configuration Options:**
|
|
116
|
+
|
|
117
|
+
| Option | Type | Default | Description |
|
|
118
|
+
|--------|------|---------|-------------|
|
|
119
|
+
| `similarityThreshold` | `number` | `0.95` | Minimum similarity (0-1) for cache hit |
|
|
120
|
+
| `maxAge` | `number` | `undefined` | Max age in ms before entries expire |
|
|
121
|
+
| `modelName` | `string` | `'Xenova/all-MiniLM-L6-v2'` | Embedding model to use |
|
|
122
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
123
|
+
| `storagePrefix` | `string` | `'nano-llm-cache'` | IndexedDB key prefix |
|
|
124
|
+
|
|
125
|
+
#### Methods
|
|
126
|
+
|
|
127
|
+
##### `query(prompt: string): Promise<CacheQueryResult>`
|
|
128
|
+
|
|
129
|
+
Search the cache for a semantically similar prompt.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const result = await cache.query('What is TypeScript?');
|
|
133
|
+
|
|
134
|
+
if (result.hit) {
|
|
135
|
+
console.log(result.response); // Cached response
|
|
136
|
+
console.log(result.similarity); // 0.97
|
|
137
|
+
console.log(result.entry); // Full cache entry
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
##### `save(prompt: string, response: string, metadata?: object): Promise<void>`
|
|
142
|
+
|
|
143
|
+
Save a prompt-response pair to the cache.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
await cache.save(
|
|
147
|
+
'What is TypeScript?',
|
|
148
|
+
'TypeScript is a typed superset of JavaScript.',
|
|
149
|
+
{ model: 'gpt-4', timestamp: Date.now() }
|
|
150
|
+
);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
##### `clear(): Promise<void>`
|
|
154
|
+
|
|
155
|
+
Clear all cache entries.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
await cache.clear();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
##### `getStats(): Promise<CacheStats>`
|
|
162
|
+
|
|
163
|
+
Get cache statistics.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const stats = await cache.getStats();
|
|
167
|
+
console.log(stats.totalEntries); // 42
|
|
168
|
+
console.log(stats.oldestEntry); // 1707123456789
|
|
169
|
+
console.log(stats.newestEntry); // 1707987654321
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
##### `preloadModel(): Promise<void>`
|
|
173
|
+
|
|
174
|
+
Preload the embedding model (recommended for better UX).
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
await cache.preloadModel();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
##### `unloadModel(): Promise<void>`
|
|
181
|
+
|
|
182
|
+
Unload the model to free memory.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
await cache.unloadModel();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
##### `createChatWrapper<T>(originalFn: T): T`
|
|
189
|
+
|
|
190
|
+
Create an OpenAI-compatible wrapper function.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const cachedCreate = cache.createChatWrapper(
|
|
194
|
+
openai.chat.completions.create.bind(openai.chat.completions)
|
|
195
|
+
);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## 🎨 Examples
|
|
199
|
+
|
|
200
|
+
### Example 1: Weather Queries
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const cache = new NanoCache({ similarityThreshold: 0.95 });
|
|
204
|
+
|
|
205
|
+
// Save weather data
|
|
206
|
+
await cache.save(
|
|
207
|
+
'What is the weather in London?',
|
|
208
|
+
'Cloudy, 15°C, chance of rain'
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// These all return the cached response:
|
|
212
|
+
await cache.query('Tell me the London weather'); // ✅ HIT
|
|
213
|
+
await cache.query('How is the weather in London?'); // ✅ HIT
|
|
214
|
+
await cache.query('London weather today'); // ✅ HIT
|
|
215
|
+
await cache.query('What is the weather in Paris?'); // ❌ MISS
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Example 2: Programming Questions
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
await cache.save(
|
|
222
|
+
'How do I center a div?',
|
|
223
|
+
'Use flexbox: display: flex; justify-content: center; align-items: center;'
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Similar questions hit the cache:
|
|
227
|
+
await cache.query('Best way to align a div to the middle?'); // ✅ HIT
|
|
228
|
+
await cache.query('Center a div CSS'); // ✅ HIT
|
|
229
|
+
await cache.query('How to make a div centered?'); // ✅ HIT
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Example 3: With TTL (Time To Live)
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const cache = new NanoCache({
|
|
236
|
+
maxAge: 60 * 60 * 1000 // 1 hour
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Weather data expires after 1 hour
|
|
240
|
+
await cache.save(
|
|
241
|
+
'Current temperature in NYC',
|
|
242
|
+
'72°F, sunny'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// After 1 hour, this will be a cache miss
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 🧪 Testing
|
|
249
|
+
|
|
250
|
+
Run the test suite:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npm test
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Run tests with UI:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
npm run test:ui
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Generate coverage report:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
npm run test:coverage
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## 🏗️ Building
|
|
269
|
+
|
|
270
|
+
Build the library:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
npm run build
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Development mode (watch):
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm run dev
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## 📊 How It Works
|
|
283
|
+
|
|
284
|
+
### 1. Vector Embeddings
|
|
285
|
+
|
|
286
|
+
When you save a prompt, Nano-LLM-Cache converts it into a **384-dimensional vector**:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
"What is the weather in London?" → [0.12, -0.44, 0.88, ...]
|
|
290
|
+
"Tell me the London weather" → [0.13, -0.43, 0.89, ...]
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
These vectors are **close together in space** because they have similar meanings.
|
|
294
|
+
|
|
295
|
+
### 2. Cosine Similarity
|
|
296
|
+
|
|
297
|
+
When querying, we calculate the **cosine similarity** between vectors:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
similarity = dotProduct(vecA, vecB) / (magnitude(vecA) * magnitude(vecB))
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
A similarity of `0.95` means the prompts are 95% semantically similar.
|
|
304
|
+
|
|
305
|
+
### 3. Local Processing
|
|
306
|
+
|
|
307
|
+
Everything runs **locally** using WebAssembly:
|
|
308
|
+
- ✅ No API calls for embeddings
|
|
309
|
+
- ✅ No data sent to external servers
|
|
310
|
+
- ✅ Works offline after initial model download
|
|
311
|
+
- ✅ Model cached in browser (~20MB, downloads once)
|
|
312
|
+
|
|
313
|
+
## 💡 Use Cases
|
|
314
|
+
|
|
315
|
+
### 1. **Cost Reduction**
|
|
316
|
+
|
|
317
|
+
LLM APIs charge per token. For a million users asking similar questions:
|
|
318
|
+
- Without cache: $50,000+ in API costs
|
|
319
|
+
- With Nano-LLM-Cache: $500 (99% cache hit rate)
|
|
320
|
+
|
|
321
|
+
### 2. **Faster Response Times**
|
|
322
|
+
|
|
323
|
+
- API call: 2-5 seconds
|
|
324
|
+
- Cache hit: <100ms
|
|
325
|
+
|
|
326
|
+
### 3. **Offline Capability**
|
|
327
|
+
|
|
328
|
+
Once the model is cached, your app works offline for cached queries.
|
|
329
|
+
|
|
330
|
+
### 4. **Privacy**
|
|
331
|
+
|
|
332
|
+
User prompts are embedded locally - no data leaves the device until the actual LLM call.
|
|
333
|
+
|
|
334
|
+
## ⚙️ Configuration Tips
|
|
335
|
+
|
|
336
|
+
### Similarity Threshold
|
|
337
|
+
|
|
338
|
+
- `0.99`: Very strict - only nearly identical prompts match
|
|
339
|
+
- `0.95`: Recommended - catches paraphrases and similar questions
|
|
340
|
+
- `0.90`: Looser - may match somewhat related topics
|
|
341
|
+
- `0.85`: Very loose - use with caution
|
|
342
|
+
|
|
343
|
+
### Model Selection
|
|
344
|
+
|
|
345
|
+
Default: `Xenova/all-MiniLM-L6-v2` (384 dimensions, ~20MB)
|
|
346
|
+
|
|
347
|
+
Other options:
|
|
348
|
+
- `Xenova/all-MiniLM-L12-v2`: Larger, more accurate (~45MB)
|
|
349
|
+
- `Xenova/paraphrase-multilingual-MiniLM-L12-v2`: Multilingual support
|
|
350
|
+
|
|
351
|
+
### TTL Strategy
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
// Real-time data (weather, stock prices)
|
|
355
|
+
maxAge: 60 * 60 * 1000 // 1 hour
|
|
356
|
+
|
|
357
|
+
// Static knowledge (programming questions)
|
|
358
|
+
maxAge: undefined // Never expire
|
|
359
|
+
|
|
360
|
+
// Daily updates (news summaries)
|
|
361
|
+
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## 🔧 Advanced Usage
|
|
365
|
+
|
|
366
|
+
### Custom Storage
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { NanoCache } from '@nano-llm-cache/core';
|
|
370
|
+
|
|
371
|
+
const cache = new NanoCache({
|
|
372
|
+
storagePrefix: 'my-app-cache' // Separate cache per app
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Batch Operations
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
// Save multiple entries
|
|
380
|
+
const entries = [
|
|
381
|
+
{ prompt: 'Q1', response: 'A1' },
|
|
382
|
+
{ prompt: 'Q2', response: 'A2' },
|
|
383
|
+
];
|
|
384
|
+
|
|
385
|
+
for (const { prompt, response } of entries) {
|
|
386
|
+
await cache.save(prompt, response);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Cache Warming
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Preload common queries on app startup
|
|
394
|
+
async function warmCache() {
|
|
395
|
+
await cache.preloadModel();
|
|
396
|
+
|
|
397
|
+
const commonQueries = [
|
|
398
|
+
{ q: 'How do I...', a: '...' },
|
|
399
|
+
{ q: 'What is...', a: '...' },
|
|
400
|
+
];
|
|
401
|
+
|
|
402
|
+
for (const { q, a } of commonQueries) {
|
|
403
|
+
await cache.save(q, a);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## 📈 Performance
|
|
409
|
+
|
|
410
|
+
| Operation | Time |
|
|
411
|
+
|-----------|------|
|
|
412
|
+
| First query (model load) | ~2-3s |
|
|
413
|
+
| Subsequent queries | ~50-100ms |
|
|
414
|
+
| Save operation | ~50-100ms |
|
|
415
|
+
| Cache hit | <10ms |
|
|
416
|
+
|
|
417
|
+
**Memory Usage:**
|
|
418
|
+
- Model: ~20MB (cached in browser)
|
|
419
|
+
- Per entry: ~2-3KB (embedding + metadata)
|
|
420
|
+
- 1000 entries: ~2-3MB
|
|
421
|
+
|
|
422
|
+
## 🤝 Contributing
|
|
423
|
+
|
|
424
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
425
|
+
|
|
426
|
+
## 📄 License
|
|
427
|
+
|
|
428
|
+
MIT © [Your Name]
|
|
429
|
+
|
|
430
|
+
## 🙏 Acknowledgments
|
|
431
|
+
|
|
432
|
+
- [@xenova/transformers](https://github.com/xenova/transformers.js) - WASM-based transformers
|
|
433
|
+
- [idb-keyval](https://github.com/jakearchibald/idb-keyval) - Simple IndexedDB wrapper
|
|
434
|
+
- [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) - Embedding model
|
|
435
|
+
|
|
436
|
+
## 🔗 Links
|
|
437
|
+
|
|
438
|
+
- [GitHub Repository](https://github.com/yourusername/nano-llm-cache)
|
|
439
|
+
- [NPM Package](https://www.npmjs.com/package/nano-llm-cache)
|
|
440
|
+
- [Documentation](https://github.com/yourusername/nano-llm-cache#readme)
|
|
441
|
+
- [Issues](https://github.com/yourusername/nano-llm-cache/issues)
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
**Made with ❤️ by developers who hate paying for duplicate LLM calls**
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for NanoCache
|
|
3
|
+
*/
|
|
4
|
+
interface NanoCacheConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Similarity threshold for cache hits (0-1)
|
|
7
|
+
* @default 0.95
|
|
8
|
+
*/
|
|
9
|
+
similarityThreshold?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Maximum age of cached entries in milliseconds
|
|
12
|
+
* @default undefined (no expiration)
|
|
13
|
+
*/
|
|
14
|
+
maxAge?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Model name for embeddings
|
|
17
|
+
* @default 'Xenova/all-MiniLM-L6-v2'
|
|
18
|
+
*/
|
|
19
|
+
modelName?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Enable debug logging
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Custom storage key prefix
|
|
27
|
+
* @default 'nano-llm-cache'
|
|
28
|
+
*/
|
|
29
|
+
storagePrefix?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Cached entry structure
|
|
33
|
+
*/
|
|
34
|
+
interface CacheEntry {
|
|
35
|
+
prompt: string;
|
|
36
|
+
embedding: number[];
|
|
37
|
+
response: string;
|
|
38
|
+
timestamp: number;
|
|
39
|
+
metadata?: Record<string, any>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Cache query result
|
|
43
|
+
*/
|
|
44
|
+
interface CacheQueryResult {
|
|
45
|
+
hit: boolean;
|
|
46
|
+
response?: string;
|
|
47
|
+
similarity?: number;
|
|
48
|
+
entry?: CacheEntry;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* OpenAI-compatible message structure
|
|
52
|
+
*/
|
|
53
|
+
interface ChatMessage {
|
|
54
|
+
role: 'system' | 'user' | 'assistant';
|
|
55
|
+
content: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* OpenAI-compatible chat completion request
|
|
59
|
+
*/
|
|
60
|
+
interface ChatCompletionRequest {
|
|
61
|
+
model: string;
|
|
62
|
+
messages: ChatMessage[];
|
|
63
|
+
temperature?: number;
|
|
64
|
+
max_tokens?: number;
|
|
65
|
+
[key: string]: any;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* OpenAI-compatible chat completion response
|
|
69
|
+
*/
|
|
70
|
+
interface ChatCompletionResponse {
|
|
71
|
+
id: string;
|
|
72
|
+
object: string;
|
|
73
|
+
created: number;
|
|
74
|
+
model: string;
|
|
75
|
+
choices: Array<{
|
|
76
|
+
index: number;
|
|
77
|
+
message: ChatMessage;
|
|
78
|
+
finish_reason: string;
|
|
79
|
+
}>;
|
|
80
|
+
usage?: {
|
|
81
|
+
prompt_tokens: number;
|
|
82
|
+
completion_tokens: number;
|
|
83
|
+
total_tokens: number;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* NanoCache - Semantic cache for LLM API calls
|
|
89
|
+
*/
|
|
90
|
+
declare class NanoCache {
|
|
91
|
+
private storage;
|
|
92
|
+
private embeddings;
|
|
93
|
+
private config;
|
|
94
|
+
constructor(config?: NanoCacheConfig);
|
|
95
|
+
/**
|
|
96
|
+
* Query the cache for a similar prompt
|
|
97
|
+
*/
|
|
98
|
+
query(prompt: string): Promise<CacheQueryResult>;
|
|
99
|
+
/**
|
|
100
|
+
* Save a prompt-response pair to the cache
|
|
101
|
+
*/
|
|
102
|
+
save(prompt: string, response: string, metadata?: Record<string, any>): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Clear all cached entries
|
|
105
|
+
*/
|
|
106
|
+
clear(): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Get cache statistics
|
|
109
|
+
*/
|
|
110
|
+
getStats(): Promise<{
|
|
111
|
+
totalEntries: number;
|
|
112
|
+
oldestEntry: number | null;
|
|
113
|
+
newestEntry: number | null;
|
|
114
|
+
}>;
|
|
115
|
+
/**
|
|
116
|
+
* Check if embedding model is loaded
|
|
117
|
+
*/
|
|
118
|
+
isModelLoaded(): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Preload the embedding model
|
|
121
|
+
*/
|
|
122
|
+
preloadModel(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Unload the embedding model to free memory
|
|
125
|
+
*/
|
|
126
|
+
unloadModel(): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Simple hash function for prompt
|
|
129
|
+
*/
|
|
130
|
+
private hashPrompt;
|
|
131
|
+
/**
|
|
132
|
+
* Create a wrapper for OpenAI-compatible chat completion
|
|
133
|
+
* This allows drop-in replacement of openai.chat.completions.create
|
|
134
|
+
*/
|
|
135
|
+
createChatWrapper<T extends (req: ChatCompletionRequest) => Promise<ChatCompletionResponse>>(originalFn: T): T;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Calculate cosine similarity between two vectors
|
|
140
|
+
* @param vecA - First vector
|
|
141
|
+
* @param vecB - Second vector
|
|
142
|
+
* @returns Similarity score between 0 and 1
|
|
143
|
+
*/
|
|
144
|
+
declare function calculateSimilarity(vecA: number[], vecB: number[]): number;
|
|
145
|
+
/**
|
|
146
|
+
* Normalize a vector to unit length
|
|
147
|
+
* @param vec - Input vector
|
|
148
|
+
* @returns Normalized vector
|
|
149
|
+
*/
|
|
150
|
+
declare function normalizeVector(vec: number[]): number[];
|
|
151
|
+
|
|
152
|
+
export { type CacheEntry, type CacheQueryResult, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, NanoCache, type NanoCacheConfig, calculateSimilarity, normalizeVector };
|