ai-sdk-memory 0.0.1

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 ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,12 @@
1
+ import { streamText, StreamTextResult, generateText, GenerateTextResult, generateObject, GenerateObjectResult, streamObject, StreamObjectResult } from 'ai';
2
+ import { SemanticCacheConfig } from './schema.mjs';
3
+ import 'zod';
4
+
5
+ declare function createSemanticCache(config: SemanticCacheConfig): {
6
+ streamText: <TOOLS extends Record<string, any> = {}>(options: Parameters<typeof streamText<TOOLS>>[0]) => Promise<StreamTextResult<TOOLS, any>>;
7
+ generateText: <TOOLS extends Record<string, any> = {}, OUTPUT = undefined>(options: Parameters<typeof generateText<TOOLS, OUTPUT>>[0]) => Promise<GenerateTextResult<TOOLS, OUTPUT>>;
8
+ generateObject: <T = any>(options: Parameters<typeof generateObject>[0]) => Promise<GenerateObjectResult<T>>;
9
+ streamObject: <T = any>(options: Parameters<typeof streamObject>[0]) => Promise<StreamObjectResult<T, T, any>>;
10
+ };
11
+
12
+ export { createSemanticCache };
@@ -0,0 +1,12 @@
1
+ import { streamText, StreamTextResult, generateText, GenerateTextResult, generateObject, GenerateObjectResult, streamObject, StreamObjectResult } from 'ai';
2
+ import { SemanticCacheConfig } from './schema.js';
3
+ import 'zod';
4
+
5
+ declare function createSemanticCache(config: SemanticCacheConfig): {
6
+ streamText: <TOOLS extends Record<string, any> = {}>(options: Parameters<typeof streamText<TOOLS>>[0]) => Promise<StreamTextResult<TOOLS, any>>;
7
+ generateText: <TOOLS extends Record<string, any> = {}, OUTPUT = undefined>(options: Parameters<typeof generateText<TOOLS, OUTPUT>>[0]) => Promise<GenerateTextResult<TOOLS, OUTPUT>>;
8
+ generateObject: <T = any>(options: Parameters<typeof generateObject>[0]) => Promise<GenerateObjectResult<T>>;
9
+ streamObject: <T = any>(options: Parameters<typeof streamObject>[0]) => Promise<StreamObjectResult<T, T, any>>;
10
+ };
11
+
12
+ export { createSemanticCache };
package/dist/index.js ADDED
@@ -0,0 +1,334 @@
1
+ 'use strict';
2
+
3
+ require('@ai-sdk/provider');
4
+ var ai = require('ai');
5
+ var vector = require('@upstash/vector');
6
+ var redis = require('@upstash/redis');
7
+ var crypto = require('crypto');
8
+ var zod = require('zod');
9
+ require('dotenv/config');
10
+
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
30
+
31
+ // index.ts
32
+ var semanticCacheConfigSchema = zod.z.object({
33
+ model: zod.z.union([
34
+ zod.z.string(),
35
+ zod.z.custom((val) => {
36
+ return val && typeof val === "object";
37
+ })
38
+ ]),
39
+ vector: zod.z.object({
40
+ url: zod.z.url(),
41
+ token: zod.z.string().min(1)
42
+ }).optional().default({
43
+ url: process.env.VECTOR_REST_URL ?? "",
44
+ token: process.env.VECTOR_REST_TOKEN ?? ""
45
+ }),
46
+ redis: zod.z.object({
47
+ url: zod.z.url(),
48
+ token: zod.z.string().min(1)
49
+ }).optional().default({
50
+ url: process.env.REDIS_REST_URL ?? "",
51
+ token: process.env.REDIS_REST_TOKEN ?? ""
52
+ }),
53
+ threshold: zod.z.number().min(0).max(1).optional().default(0.92),
54
+ ttl: zod.z.number().positive().optional().default(60 * 60 * 24 * 14),
55
+ debug: zod.z.boolean().optional().default(false),
56
+ cacheMode: zod.z.enum(["default", "refresh"]).optional().default("default"),
57
+ simulateStream: zod.z.object({
58
+ enabled: zod.z.boolean().optional().default(true),
59
+ initialDelayInMs: zod.z.number().min(0).optional().default(0),
60
+ chunkDelayInMs: zod.z.number().min(0).optional().default(10)
61
+ }).optional().default({
62
+ enabled: true,
63
+ initialDelayInMs: 0,
64
+ chunkDelayInMs: 10
65
+ }),
66
+ useFullMessages: zod.z.boolean().optional().default(false)
67
+ });
68
+ function validateEnvConfig(config) {
69
+ const errors = [];
70
+ if (!config.vector.url) {
71
+ errors.push(
72
+ "Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable."
73
+ );
74
+ }
75
+ if (!config.vector.token) {
76
+ errors.push(
77
+ "Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable."
78
+ );
79
+ }
80
+ if (!config.redis.url) {
81
+ errors.push(
82
+ "Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable."
83
+ );
84
+ }
85
+ if (!config.redis.token) {
86
+ errors.push(
87
+ "Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable."
88
+ );
89
+ }
90
+ if (errors.length > 0) {
91
+ throw new Error(
92
+ `Semantic Cache Configuration Error:
93
+ ${errors.map((e) => ` - ${e}`).join("\n")}`
94
+ );
95
+ }
96
+ }
97
+
98
+ // index.ts
99
+ var norm = (s) => s.trim().toLowerCase().replace(/\s+/g, " ");
100
+ var sha = (s) => crypto__namespace.createHash("sha256").update(s).digest("hex");
101
+ function canonicalizeMessages(msgs) {
102
+ return msgs.map((m) => ({
103
+ role: m.role,
104
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
105
+ }));
106
+ }
107
+ function buildScope(options) {
108
+ return {
109
+ llmModel: options.model?.modelId ?? String(options.model ?? ""),
110
+ systemHash: sha(options.system ?? ""),
111
+ params: sha(
112
+ JSON.stringify({
113
+ temperature: options.temperature,
114
+ topP: options.topP
115
+ })
116
+ ),
117
+ toolsHash: sha(JSON.stringify(options.tools ?? {}))
118
+ };
119
+ }
120
+ function createSemanticCache(config) {
121
+ const parsed = semanticCacheConfigSchema.parse(config);
122
+ validateEnvConfig(parsed);
123
+ const {
124
+ vector: vector$1,
125
+ redis: redisConfig,
126
+ threshold,
127
+ ttl,
128
+ simulateStream,
129
+ debug,
130
+ cacheMode,
131
+ useFullMessages
132
+ } = parsed;
133
+ const model = parsed.model;
134
+ const index = new vector.Index({
135
+ url: vector$1.url,
136
+ token: vector$1.token
137
+ });
138
+ const redis$1 = new redis.Redis({
139
+ url: redisConfig.url,
140
+ token: redisConfig.token
141
+ });
142
+ function getCacheKey(options) {
143
+ if (options.messages) {
144
+ const messages = Array.isArray(options.messages) ? options.messages : [];
145
+ if (!useFullMessages && messages.length > 0) {
146
+ const lastMessage = messages[messages.length - 1];
147
+ return JSON.stringify({
148
+ role: lastMessage.role,
149
+ content: typeof lastMessage.content === "string" ? lastMessage.content : JSON.stringify(lastMessage.content)
150
+ });
151
+ }
152
+ return JSON.stringify(canonicalizeMessages(options.messages));
153
+ }
154
+ if (options.prompt) {
155
+ return String(options.prompt);
156
+ }
157
+ return "";
158
+ }
159
+ async function checkSemanticCache(cacheInput, scope) {
160
+ const promptNorm = norm(cacheInput);
161
+ const { embedding } = await ai.embed({
162
+ model,
163
+ value: promptNorm
164
+ });
165
+ const result = await index.query({
166
+ vector: embedding,
167
+ topK: 3,
168
+ includeMetadata: true
169
+ });
170
+ const hit = result.find((m) => {
171
+ if (m.score < threshold) return false;
172
+ const metadata = m.metadata;
173
+ if (!metadata) return false;
174
+ return metadata.llmModel === scope.llmModel && metadata.systemHash === scope.systemHash && metadata.params === scope.params && metadata.toolsHash === scope.toolsHash;
175
+ });
176
+ if (hit) {
177
+ const cached = await redis$1.get(hit.id.toString());
178
+ if (cached) {
179
+ if (debug) console.log("\u2705 cache hit", hit.score.toFixed(3));
180
+ return { cached, embedding, promptNorm };
181
+ }
182
+ }
183
+ if (debug) console.log("\u274C miss -> generating\u2026");
184
+ return { cached: null, embedding, promptNorm };
185
+ }
186
+ async function storeInCache(id, data, embedding, promptNorm, scope) {
187
+ const lockKey = "lock:" + id;
188
+ const ok = await redis$1.set(lockKey, "1", { nx: true, ex: 15 });
189
+ if (!ok) {
190
+ if (debug)
191
+ console.log("\u26A0\uFE0F Another process is writing to cache, skipping");
192
+ return;
193
+ }
194
+ try {
195
+ await redis$1.set(id, data, { ex: ttl });
196
+ await index.upsert([
197
+ {
198
+ id,
199
+ vector: embedding,
200
+ metadata: {
201
+ prompt: promptNorm,
202
+ ...scope
203
+ }
204
+ }
205
+ ]);
206
+ } finally {
207
+ await redis$1.del(lockKey);
208
+ }
209
+ }
210
+ const semanticCacheMiddleware = {
211
+ wrapStream: async ({ doStream, params }) => {
212
+ const cacheInput = getCacheKey(params);
213
+ const scope = buildScope(params);
214
+ const promptScope = Object.values(scope).join("|");
215
+ const { cached, embedding, promptNorm } = await checkSemanticCache(
216
+ cacheInput,
217
+ scope
218
+ );
219
+ if (cached && cacheMode !== "refresh") {
220
+ if (debug) console.log("\u2705 Returning cached stream");
221
+ let chunks = [];
222
+ if (cached.streamParts) {
223
+ chunks = cached.streamParts.map((p) => {
224
+ if (p.type === "response-metadata" && p.timestamp) {
225
+ return { ...p, timestamp: new Date(p.timestamp) };
226
+ }
227
+ return p;
228
+ });
229
+ } else if (cached.text) {
230
+ chunks = [
231
+ { type: "text-start", id: cached.id },
232
+ { type: "text-delta", delta: cached.text, id: cached.id },
233
+ { type: "finish", finishReason: "stop", usage: cached.usage }
234
+ ];
235
+ }
236
+ return {
237
+ stream: ai.simulateReadableStream({
238
+ initialDelayInMs: simulateStream.enabled ? simulateStream.initialDelayInMs : 0,
239
+ chunkDelayInMs: simulateStream.enabled ? simulateStream.chunkDelayInMs : 0,
240
+ chunks
241
+ })
242
+ };
243
+ }
244
+ const { stream, ...rest } = await doStream();
245
+ const fullResponse = [];
246
+ const transformStream = new TransformStream({
247
+ transform(chunk, controller) {
248
+ fullResponse.push(chunk);
249
+ controller.enqueue(chunk);
250
+ },
251
+ async flush() {
252
+ const id = "llm:" + sha(promptScope + "|" + promptNorm);
253
+ await storeInCache(
254
+ id,
255
+ { streamParts: fullResponse },
256
+ embedding,
257
+ promptNorm,
258
+ scope
259
+ );
260
+ }
261
+ });
262
+ return {
263
+ stream: stream.pipeThrough(transformStream),
264
+ ...rest
265
+ };
266
+ },
267
+ wrapGenerate: async ({ doGenerate, params }) => {
268
+ const cacheInput = getCacheKey(params);
269
+ const scope = buildScope(params);
270
+ const promptScope = Object.values(scope).join("|");
271
+ const { cached, embedding, promptNorm } = await checkSemanticCache(
272
+ cacheInput,
273
+ scope
274
+ );
275
+ if (cached && cacheMode !== "refresh") {
276
+ if (debug) console.log("\u2705 Returning cached generation");
277
+ if (cached?.response?.timestamp) {
278
+ cached.response.timestamp = new Date(cached.response.timestamp);
279
+ }
280
+ return cached;
281
+ }
282
+ const result = await doGenerate();
283
+ const id = "llm:" + sha(promptScope + "|" + promptNorm);
284
+ await storeInCache(id, result, embedding, promptNorm, scope);
285
+ return result;
286
+ }
287
+ };
288
+ return {
289
+ streamText: async (options) => {
290
+ const wrappedModel = ai.wrapLanguageModel({
291
+ model: typeof options.model === "string" ? ai.gateway(options.model) : options.model,
292
+ middleware: semanticCacheMiddleware
293
+ });
294
+ return ai.streamText({
295
+ ...options,
296
+ model: wrappedModel
297
+ });
298
+ },
299
+ generateText: async (options) => {
300
+ const wrappedModel = ai.wrapLanguageModel({
301
+ model: typeof options.model === "string" ? ai.gateway(options.model) : options.model,
302
+ middleware: semanticCacheMiddleware
303
+ });
304
+ return ai.generateText({
305
+ ...options,
306
+ model: wrappedModel
307
+ });
308
+ },
309
+ generateObject: async (options) => {
310
+ const wrappedModel = ai.wrapLanguageModel({
311
+ model: typeof options.model === "string" ? ai.gateway(options.model) : options.model,
312
+ middleware: semanticCacheMiddleware
313
+ });
314
+ return await ai.generateObject({
315
+ ...options,
316
+ model: wrappedModel
317
+ });
318
+ },
319
+ streamObject: async (options) => {
320
+ const wrappedModel = ai.wrapLanguageModel({
321
+ model: typeof options.model === "string" ? ai.gateway(options.model) : options.model,
322
+ middleware: semanticCacheMiddleware
323
+ });
324
+ return ai.streamObject({
325
+ ...options,
326
+ model: wrappedModel
327
+ });
328
+ }
329
+ };
330
+ }
331
+
332
+ exports.createSemanticCache = createSemanticCache;
333
+ //# sourceMappingURL=index.js.map
334
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../schema.ts","../index.ts"],"names":["z","crypto","vector","Index","redis","Redis","embed","simulateReadableStream","wrapLanguageModel","gateway","streamText","generateText","generateObject","streamObject"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,yBAAA,GAA4BA,MAAE,MAAA,CAAO;AAAA,EAChD,KAAA,EAAOA,MAAE,KAAA,CAAM;AAAA,IACbA,MAAE,MAAA,EAAO;AAAA,IACTA,KAAA,CAAE,MAAA,CAAO,CAAC,GAAA,KAAQ;AAChB,MAAA,OAAO,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA;AAAA,IAC/B,CAAC;AAAA,GACF,CAAA;AAAA,EACD,MAAA,EAAQA,MACL,MAAA,CAAO;AAAA,IACN,GAAA,EAAKA,MAAE,GAAA,EAAI;AAAA,IACX,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GACxB,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,EAAA;AAAA,IACpC,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,iBAAA,IAAqB;AAAA,GACzC,CAAA;AAAA,EACH,KAAA,EAAOA,MACJ,MAAA,CAAO;AAAA,IACN,GAAA,EAAKA,MAAE,GAAA,EAAI;AAAA,IACX,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GACxB,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,cAAA,IAAkB,EAAA;AAAA,IACnC,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,gBAAA,IAAoB;AAAA,GACxC,CAAA;AAAA,EACH,SAAA,EAAWA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,QAAQ,IAAI,CAAA;AAAA,EAC3D,GAAA,EAAKA,KAAA,CACF,MAAA,EAAO,CACP,QAAA,EAAS,CACT,QAAA,EAAS,CACT,OAAA,CAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA;AAAA,EAC5B,OAAOA,KAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,KAAK,CAAA;AAAA,EAC3C,SAAA,EAAWA,KAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAW,SAAS,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA;AAAA,EACtE,cAAA,EAAgBA,MACb,MAAA,CAAO;AAAA,IACN,SAASA,KAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,IAAI,CAAA;AAAA,IAC5C,gBAAA,EAAkBA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxD,cAAA,EAAgBA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE;AAAA,GACxD,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,OAAA,EAAS,IAAA;AAAA,IACT,gBAAA,EAAkB,CAAA;AAAA,IAClB,cAAA,EAAgB;AAAA,GACjB,CAAA;AAAA,EACH,iBAAiBA,KAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,KAAK;AACvD,CAAC,CAAA;AAOM,SAAS,kBAAkB,MAAA,EAAmC;AACnE,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK;AACtB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO;AACxB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK;AACrB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO;AACvB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA;AAAA,EAAwC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,IAAA,EAAO,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAClF;AAAA,EACF;AACF;;;AC3DA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAc,CAAA,CAAE,IAAA,GAAO,WAAA,EAAY,CAAE,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA;AACtE,IAAM,GAAA,GAAM,CAAC,CAAA,KAAqBC,iBAAA,CAAA,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,CAAC,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAE7E,SAAS,qBAAqB,IAAA,EAAa;AACzC,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACtB,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAA,EACE,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAA,CAAE,OAAO;AAAA,GACxE,CAAE,CAAA;AACJ;AAEA,SAAS,WAAW,OAAA,EAAc;AAChC,EAAA,OAAO;AAAA,IACL,UAAU,OAAA,CAAQ,KAAA,EAAO,WAAW,MAAA,CAAO,OAAA,CAAQ,SAAS,EAAE,CAAA;AAAA,IAC9D,UAAA,EAAY,GAAA,CAAI,OAAA,CAAQ,MAAA,IAAU,EAAE,CAAA;AAAA,IACpC,MAAA,EAAQ,GAAA;AAAA,MACN,KAAK,SAAA,CAAU;AAAA,QACb,aAAa,OAAA,CAAQ,WAAA;AAAA,QACrB,MAAM,OAAA,CAAQ;AAAA,OACf;AAAA,KACH;AAAA,IACA,SAAA,EAAW,IAAI,IAAA,CAAK,SAAA,CAAU,QAAQ,KAAA,IAAS,EAAE,CAAC;AAAA,GACpD;AACF;AAEO,SAAS,oBAAoB,MAAA,EAA6B;AAC/D,EAAA,MAAM,MAAA,GAAS,yBAAA,CAA0B,KAAA,CAAM,MAAM,CAAA;AAErD,EAAA,iBAAA,CAAkB,MAAM,CAAA;AAExB,EAAA,MAAM;AAAA,YACJC,QAAA;AAAA,IACA,KAAA,EAAO,WAAA;AAAA,IACP,SAAA;AAAA,IACA,GAAA;AAAA,IACA,cAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF,GAAI,MAAA;AACJ,EAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AAErB,EAAA,MAAM,KAAA,GAAQ,IAAIC,YAAA,CAAM;AAAA,IACtB,KAAKD,QAAA,CAAO,GAAA;AAAA,IACZ,OAAOA,QAAA,CAAO;AAAA,GACf,CAAA;AAED,EAAA,MAAME,OAAA,GAAQ,IAAIC,WAAA,CAAM;AAAA,IACtB,KAAK,WAAA,CAAY,GAAA;AAAA,IACjB,OAAO,WAAA,CAAY;AAAA,GACpB,CAAA;AAED,EAAA,SAAS,YAAY,OAAA,EAAsB;AACzC,IAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,GAAI,OAAA,CAAQ,WAAW,EAAC;AAGvE,MAAA,IAAI,CAAC,eAAA,IAAmB,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAC3C,QAAA,MAAM,WAAA,GAAc,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA;AAChD,QAAA,OAAO,KAAK,SAAA,CAAU;AAAA,UACpB,MAAM,WAAA,CAAY,IAAA;AAAA,UAClB,OAAA,EACE,OAAO,WAAA,CAAY,OAAA,KAAY,QAAA,GAC3B,YAAY,OAAA,GACZ,IAAA,CAAK,SAAA,CAAU,WAAA,CAAY,OAAO;AAAA,SACzC,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,IAAA,CAAK,SAAA,CAAU,oBAAA,CAAqB,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAAA,IAC9D;AACA,IAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,MAAA,OAAO,MAAA,CAAO,QAAQ,MAAM,CAAA;AAAA,IAC9B;AACA,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,eAAe,kBAAA,CAAmB,YAAoB,KAAA,EAAY;AAChE,IAAA,MAAM,UAAA,GAAa,KAAK,UAAU,CAAA;AAElC,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAMC,QAAA,CAAM;AAAA,MAChC,KAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACR,CAAA;AAED,IAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,KAAA,CAAM;AAAA,MAC/B,MAAA,EAAQ,SAAA;AAAA,MACR,IAAA,EAAM,CAAA;AAAA,MACN,eAAA,EAAiB;AAAA,KAClB,CAAA;AAED,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM;AAC7B,MAAA,IAAI,CAAA,CAAE,KAAA,GAAQ,SAAA,EAAW,OAAO,KAAA;AAEhC,MAAA,MAAM,WAAW,CAAA,CAAE,QAAA;AACnB,MAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AAEtB,MAAA,OACE,QAAA,CAAS,QAAA,KAAa,KAAA,CAAM,QAAA,IAC5B,SAAS,UAAA,KAAe,KAAA,CAAM,UAAA,IAC9B,QAAA,CAAS,MAAA,KAAW,KAAA,CAAM,MAAA,IAC1B,QAAA,CAAS,cAAc,KAAA,CAAM,SAAA;AAAA,IAEjC,CAAC,CAAA;AAED,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,MAAM,SAAS,MAAMF,OAAA,CAAM,IAKxB,GAAA,CAAI,EAAA,CAAG,UAAU,CAAA;AAEpB,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,IAAI,KAAA,UAAe,GAAA,CAAI,kBAAA,EAAe,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA;AAC1D,QAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,EAAW;AAAA,MACzC;AAAA,IACF;AAEA,IAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,iCAAuB,CAAA;AAC9C,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,UAAA,EAAW;AAAA,EAC/C;AAEA,EAAA,eAAe,YAAA,CACb,EAAA,EACA,IAAA,EACA,SAAA,EACA,YACA,KAAA,EACA;AACA,IAAA,MAAM,UAAU,OAAA,GAAU,EAAA;AAC1B,IAAA,MAAM,EAAA,GAAK,MAAMA,OAAA,CAAM,GAAA,CAAI,OAAA,EAAS,GAAA,EAAK,EAAE,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,EAAA,EAAI,CAAA;AAE7D,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,IAAI,KAAA;AACF,QAAA,OAAA,CAAQ,IAAI,4DAAkD,CAAA;AAChE,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAMA,QAAM,GAAA,CAAI,EAAA,EAAI,MAAM,EAAE,EAAA,EAAI,KAAK,CAAA;AACrC,MAAA,MAAM,MAAM,MAAA,CAAO;AAAA,QACjB;AAAA,UACE,EAAA;AAAA,UACA,MAAA,EAAQ,SAAA;AAAA,UACR,QAAA,EAAU;AAAA,YACR,MAAA,EAAQ,UAAA;AAAA,YACR,GAAG;AAAA;AACL;AACF,OACD,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,MAAMA,OAAA,CAAM,IAAI,OAAO,CAAA;AAAA,IACzB;AAAA,EACF;AAEA,EAAA,MAAM,uBAAA,GAAqD;AAAA,IACzD,UAAA,EAAY,OAAO,EAAE,QAAA,EAAU,QAAO,KAAM;AAC1C,MAAA,MAAM,UAAA,GAAa,YAAY,MAAM,CAAA;AACrC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,CAAA;AAC/B,MAAA,MAAM,cAAc,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEjD,MAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,KAAe,MAAM,kBAAA;AAAA,QAC9C,UAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,MAAA,IAAU,cAAc,SAAA,EAAW;AACrC,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,gCAA2B,CAAA;AAElD,QAAA,IAAI,SAAsC,EAAC;AAE3C,QAAA,IAAI,OAAO,WAAA,EAAa;AACtB,UAAA,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAW;AAC1C,YAAA,IAAI,CAAA,CAAE,IAAA,KAAS,mBAAA,IAAuB,CAAA,CAAE,SAAA,EAAW;AACjD,cAAA,OAAO,EAAE,GAAG,CAAA,EAAG,SAAA,EAAW,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,CAAA,EAAE;AAAA,YAClD;AACA,YAAA,OAAO,CAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA,MAAA,IAAW,OAAO,IAAA,EAAM;AACtB,UAAA,MAAA,GAAS;AAAA,YACP,EAAE,IAAA,EAAM,YAAA,EAAc,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,YACpC,EAAE,MAAM,YAAA,EAAc,KAAA,EAAO,OAAO,IAAA,EAAM,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,YACxD,EAAE,IAAA,EAAM,QAAA,EAAU,cAAc,MAAA,EAAQ,KAAA,EAAO,OAAO,KAAA;AAAM,WAC9D;AAAA,QACF;AAEA,QAAA,OAAO;AAAA,UACL,QAAQG,yBAAA,CAAuB;AAAA,YAC7B,gBAAA,EAAkB,cAAA,CAAe,OAAA,GAC7B,cAAA,CAAe,gBAAA,GACf,CAAA;AAAA,YACJ,cAAA,EAAgB,cAAA,CAAe,OAAA,GAC3B,cAAA,CAAe,cAAA,GACf,CAAA;AAAA,YACJ;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,IAAA,EAAK,GAAI,MAAM,QAAA,EAAS;AAE3C,MAAA,MAAM,eAA4C,EAAC;AAEnD,MAAA,MAAM,eAAA,GAAkB,IAAI,eAAA,CAG1B;AAAA,QACA,SAAA,CAAU,OAAO,UAAA,EAAY;AAC3B,UAAA,YAAA,CAAa,KAAK,KAAK,CAAA;AACvB,UAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA,QAC1B,CAAA;AAAA,QACA,MAAM,KAAA,GAAQ;AACZ,UAAA,MAAM,EAAA,GAAK,MAAA,GAAS,GAAA,CAAI,WAAA,GAAc,MAAM,UAAU,CAAA;AACtD,UAAA,MAAM,YAAA;AAAA,YACJ,EAAA;AAAA,YACA,EAAE,aAAa,YAAA,EAAa;AAAA,YAC5B,SAAA;AAAA,YACA,UAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AAAA,OACD,CAAA;AAED,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,MAAA,CAAO,WAAA,CAAY,eAAe,CAAA;AAAA,QAC1C,GAAG;AAAA,OACL;AAAA,IACF,CAAA;AAAA,IAEA,YAAA,EAAc,OAAO,EAAE,UAAA,EAAY,QAAO,KAAM;AAC9C,MAAA,MAAM,UAAA,GAAa,YAAY,MAAM,CAAA;AACrC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,CAAA;AAC/B,MAAA,MAAM,cAAc,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEjD,MAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,KAAe,MAAM,kBAAA;AAAA,QAC9C,UAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,MAAA,IAAU,cAAc,SAAA,EAAW;AACrC,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,oCAA+B,CAAA;AAEtD,QAAA,IAAI,MAAA,EAAQ,UAAU,SAAA,EAAW;AAC/B,UAAA,MAAA,CAAO,SAAS,SAAA,GAAY,IAAI,IAAA,CAAK,MAAA,CAAO,SAAS,SAAS,CAAA;AAAA,QAChE;AAGA,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,UAAA,EAAW;AAEhC,MAAA,MAAM,EAAA,GAAK,MAAA,GAAS,GAAA,CAAI,WAAA,GAAc,MAAM,UAAU,CAAA;AACtD,MAAA,MAAM,YAAA,CAAa,EAAA,EAAI,MAAA,EAAQ,SAAA,EAAW,YAAY,KAAK,CAAA;AAE3D,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,OACV,OAAA,KAC0C;AAC1C,MAAA,MAAM,eAAeC,oBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrBC,UAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAOC,aAAA,CAAW;AAAA,QAChB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,YAAA,EAAc,OAIZ,OAAA,KAC+C;AAC/C,MAAA,MAAM,eAAeF,oBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrBC,UAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAOE,eAAA,CAAa;AAAA,QAClB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,cAAA,EAAgB,OACd,OAAA,KACqC;AACrC,MAAA,MAAM,eAAeH,oBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrBC,UAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAQ,MAAMG,iBAAA,CAAe;AAAA,QAC3B,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,YAAA,EAAc,OACZ,OAAA,KAC2C;AAC3C,MAAA,MAAM,eAAeJ,oBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrBC,UAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAOI,eAAA,CAAa;AAAA,QAClB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH;AAAA,GACF;AACF","file":"index.js","sourcesContent":["import { z } from \"zod\";\nimport \"dotenv/config\";\n\nexport const semanticCacheConfigSchema = z.object({\n model: z.union([\n z.string(),\n z.custom((val) => {\n return val && typeof val === \"object\";\n }),\n ]),\n vector: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.VECTOR_REST_URL ?? \"\",\n token: process.env.VECTOR_REST_TOKEN ?? \"\",\n }),\n redis: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.REDIS_REST_URL ?? \"\",\n token: process.env.REDIS_REST_TOKEN ?? \"\",\n }),\n threshold: z.number().min(0).max(1).optional().default(0.92),\n ttl: z\n .number()\n .positive()\n .optional()\n .default(60 * 60 * 24 * 14),\n debug: z.boolean().optional().default(false),\n cacheMode: z.enum([\"default\", \"refresh\"]).optional().default(\"default\"),\n simulateStream: z\n .object({\n enabled: z.boolean().optional().default(true),\n initialDelayInMs: z.number().min(0).optional().default(0),\n chunkDelayInMs: z.number().min(0).optional().default(10),\n })\n .optional()\n .default({\n enabled: true,\n initialDelayInMs: 0,\n chunkDelayInMs: 10,\n }),\n useFullMessages: z.boolean().optional().default(false),\n});\n\nexport type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;\nexport type SemanticCacheConfigParsed = z.output<\n typeof semanticCacheConfigSchema\n>;\n\nexport function validateEnvConfig(config: SemanticCacheConfigParsed) {\n const errors: string[] = [];\n\n if (!config.vector.url) {\n errors.push(\n \"Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable.\",\n );\n }\n\n if (!config.vector.token) {\n errors.push(\n \"Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable.\",\n );\n }\n\n if (!config.redis.url) {\n errors.push(\n \"Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable.\",\n );\n }\n\n if (!config.redis.token) {\n errors.push(\n \"Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable.\",\n );\n }\n\n if (errors.length > 0) {\n throw new Error(\n `Semantic Cache Configuration Error:\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`,\n );\n }\n}\n","import {\n type LanguageModelV2Middleware,\n type LanguageModelV2StreamPart,\n type LanguageModelV2Content,\n type LanguageModelV2CallOptions,\n} from \"@ai-sdk/provider\";\nimport {\n embed,\n streamText,\n generateText,\n generateObject,\n streamObject,\n simulateReadableStream,\n wrapLanguageModel,\n type EmbeddingModel,\n type StreamTextResult,\n type GenerateTextResult,\n type GenerateObjectResult,\n type StreamObjectResult,\n gateway,\n} from \"ai\";\n\nimport { Index } from \"@upstash/vector\";\nimport { Redis } from \"@upstash/redis\";\nimport * as crypto from \"node:crypto\";\nimport {\n semanticCacheConfigSchema,\n type SemanticCacheConfig,\n validateEnvConfig,\n} from \"./schema\";\n\nconst norm = (s: string) => s.trim().toLowerCase().replace(/\\s+/g, \" \");\nconst sha = (s: string) => crypto.createHash(\"sha256\").update(s).digest(\"hex\");\n\nfunction canonicalizeMessages(msgs: any[]) {\n return msgs.map((m) => ({\n role: m.role,\n content:\n typeof m.content === \"string\" ? m.content : JSON.stringify(m.content),\n }));\n}\n\nfunction buildScope(options: any) {\n return {\n llmModel: options.model?.modelId ?? String(options.model ?? \"\"),\n systemHash: sha(options.system ?? \"\"),\n params: sha(\n JSON.stringify({\n temperature: options.temperature,\n topP: options.topP,\n }),\n ),\n toolsHash: sha(JSON.stringify(options.tools ?? {})),\n };\n}\n\nexport function createSemanticCache(config: SemanticCacheConfig) {\n const parsed = semanticCacheConfigSchema.parse(config);\n\n validateEnvConfig(parsed);\n\n const {\n vector,\n redis: redisConfig,\n threshold,\n ttl,\n simulateStream,\n debug,\n cacheMode,\n useFullMessages,\n } = parsed;\n const model = parsed.model as EmbeddingModel<string>;\n\n const index = new Index({\n url: vector.url as string,\n token: vector.token as string,\n });\n\n const redis = new Redis({\n url: redisConfig.url as string,\n token: redisConfig.token as string,\n });\n\n function getCacheKey(options: any): string {\n if (options.messages) {\n const messages = Array.isArray(options.messages) ? options.messages : [];\n\n // By default, use only the last message to avoid token limit issues\n if (!useFullMessages && messages.length > 0) {\n const lastMessage = messages[messages.length - 1];\n return JSON.stringify({\n role: lastMessage.role,\n content:\n typeof lastMessage.content === \"string\"\n ? lastMessage.content\n : JSON.stringify(lastMessage.content),\n });\n }\n\n return JSON.stringify(canonicalizeMessages(options.messages));\n }\n if (options.prompt) {\n return String(options.prompt);\n }\n return \"\";\n }\n\n async function checkSemanticCache(cacheInput: string, scope: any) {\n const promptNorm = norm(cacheInput);\n\n const { embedding } = await embed({\n model,\n value: promptNorm,\n });\n\n const result = await index.query({\n vector: embedding,\n topK: 3,\n includeMetadata: true,\n });\n\n const hit = result.find((m) => {\n if (m.score < threshold) return false;\n\n const metadata = m.metadata as any;\n if (!metadata) return false;\n\n return (\n metadata.llmModel === scope.llmModel &&\n metadata.systemHash === scope.systemHash &&\n metadata.params === scope.params &&\n metadata.toolsHash === scope.toolsHash\n );\n });\n\n if (hit) {\n const cached = await redis.get<{\n streamParts?: LanguageModelV2StreamPart[];\n text?: string;\n content?: LanguageModelV2Content;\n [key: string]: any;\n }>(hit.id.toString());\n\n if (cached) {\n if (debug) console.log(\"✅ cache hit\", hit.score.toFixed(3));\n return { cached, embedding, promptNorm };\n }\n }\n\n if (debug) console.log(\"❌ miss -> generating…\");\n return { cached: null, embedding, promptNorm };\n }\n\n async function storeInCache(\n id: string,\n data: any,\n embedding: number[],\n promptNorm: string,\n scope: any,\n ) {\n const lockKey = \"lock:\" + id;\n const ok = await redis.set(lockKey, \"1\", { nx: true, ex: 15 });\n\n if (!ok) {\n if (debug)\n console.log(\"⚠️ Another process is writing to cache, skipping\");\n return;\n }\n\n try {\n await redis.set(id, data, { ex: ttl });\n await index.upsert([\n {\n id,\n vector: embedding,\n metadata: {\n prompt: promptNorm,\n ...scope,\n },\n },\n ]);\n } finally {\n await redis.del(lockKey);\n }\n }\n\n const semanticCacheMiddleware: LanguageModelV2Middleware = {\n wrapStream: async ({ doStream, params }) => {\n const cacheInput = getCacheKey(params);\n const scope = buildScope(params);\n const promptScope = Object.values(scope).join(\"|\");\n\n const { cached, embedding, promptNorm } = await checkSemanticCache(\n cacheInput,\n scope,\n );\n\n if (cached && cacheMode !== \"refresh\") {\n if (debug) console.log(\"✅ Returning cached stream\");\n\n let chunks: LanguageModelV2StreamPart[] = [];\n\n if (cached.streamParts) {\n chunks = cached.streamParts.map((p: any) => {\n if (p.type === \"response-metadata\" && p.timestamp) {\n return { ...p, timestamp: new Date(p.timestamp) };\n }\n return p;\n });\n } else if (cached.text) {\n chunks = [\n { type: \"text-start\", id: cached.id },\n { type: \"text-delta\", delta: cached.text, id: cached.id },\n { type: \"finish\", finishReason: \"stop\", usage: cached.usage },\n ];\n }\n\n return {\n stream: simulateReadableStream({\n initialDelayInMs: simulateStream.enabled\n ? simulateStream.initialDelayInMs\n : 0,\n chunkDelayInMs: simulateStream.enabled\n ? simulateStream.chunkDelayInMs\n : 0,\n chunks,\n }),\n };\n }\n\n const { stream, ...rest } = await doStream();\n\n const fullResponse: LanguageModelV2StreamPart[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV2StreamPart,\n LanguageModelV2StreamPart\n >({\n transform(chunk, controller) {\n fullResponse.push(chunk);\n controller.enqueue(chunk);\n },\n async flush() {\n const id = \"llm:\" + sha(promptScope + \"|\" + promptNorm);\n await storeInCache(\n id,\n { streamParts: fullResponse },\n embedding,\n promptNorm,\n scope,\n );\n },\n });\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n };\n },\n\n wrapGenerate: async ({ doGenerate, params }) => {\n const cacheInput = getCacheKey(params);\n const scope = buildScope(params);\n const promptScope = Object.values(scope).join(\"|\");\n\n const { cached, embedding, promptNorm } = await checkSemanticCache(\n cacheInput,\n scope,\n );\n\n if (cached && cacheMode !== \"refresh\") {\n if (debug) console.log(\"✅ Returning cached generation\");\n\n if (cached?.response?.timestamp) {\n cached.response.timestamp = new Date(cached.response.timestamp);\n }\n\n type GenReturn<T> = T extends () => PromiseLike<infer R> ? R : never;\n return cached as unknown as GenReturn<typeof doGenerate>;\n }\n\n const result = await doGenerate();\n\n const id = \"llm:\" + sha(promptScope + \"|\" + promptNorm);\n await storeInCache(id, result, embedding, promptNorm, scope);\n\n return result;\n },\n };\n\n return {\n streamText: async <TOOLS extends Record<string, any> = {}>(\n options: Parameters<typeof streamText<TOOLS>>[0],\n ): Promise<StreamTextResult<TOOLS, any>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return streamText({\n ...options,\n model: wrappedModel,\n });\n },\n\n generateText: async <\n TOOLS extends Record<string, any> = {},\n OUTPUT = undefined,\n >(\n options: Parameters<typeof generateText<TOOLS, OUTPUT>>[0],\n ): Promise<GenerateTextResult<TOOLS, OUTPUT>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return generateText({\n ...options,\n model: wrappedModel,\n });\n },\n\n generateObject: async <T = any>(\n options: Parameters<typeof generateObject>[0],\n ): Promise<GenerateObjectResult<T>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return (await generateObject({\n ...options,\n model: wrappedModel,\n })) as GenerateObjectResult<T>;\n },\n\n streamObject: async <T = any>(\n options: Parameters<typeof streamObject>[0],\n ): Promise<StreamObjectResult<T, T, any>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return streamObject({\n ...options,\n model: wrappedModel,\n }) as unknown as StreamObjectResult<T, T, any>;\n },\n };\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,312 @@
1
+ import '@ai-sdk/provider';
2
+ import { wrapLanguageModel, gateway, streamObject, generateObject, generateText, streamText, simulateReadableStream, embed } from 'ai';
3
+ import { Index } from '@upstash/vector';
4
+ import { Redis } from '@upstash/redis';
5
+ import * as crypto from 'crypto';
6
+ import { z } from 'zod';
7
+ import 'dotenv/config';
8
+
9
+ // index.ts
10
+ var semanticCacheConfigSchema = z.object({
11
+ model: z.union([
12
+ z.string(),
13
+ z.custom((val) => {
14
+ return val && typeof val === "object";
15
+ })
16
+ ]),
17
+ vector: z.object({
18
+ url: z.url(),
19
+ token: z.string().min(1)
20
+ }).optional().default({
21
+ url: process.env.VECTOR_REST_URL ?? "",
22
+ token: process.env.VECTOR_REST_TOKEN ?? ""
23
+ }),
24
+ redis: z.object({
25
+ url: z.url(),
26
+ token: z.string().min(1)
27
+ }).optional().default({
28
+ url: process.env.REDIS_REST_URL ?? "",
29
+ token: process.env.REDIS_REST_TOKEN ?? ""
30
+ }),
31
+ threshold: z.number().min(0).max(1).optional().default(0.92),
32
+ ttl: z.number().positive().optional().default(60 * 60 * 24 * 14),
33
+ debug: z.boolean().optional().default(false),
34
+ cacheMode: z.enum(["default", "refresh"]).optional().default("default"),
35
+ simulateStream: z.object({
36
+ enabled: z.boolean().optional().default(true),
37
+ initialDelayInMs: z.number().min(0).optional().default(0),
38
+ chunkDelayInMs: z.number().min(0).optional().default(10)
39
+ }).optional().default({
40
+ enabled: true,
41
+ initialDelayInMs: 0,
42
+ chunkDelayInMs: 10
43
+ }),
44
+ useFullMessages: z.boolean().optional().default(false)
45
+ });
46
+ function validateEnvConfig(config) {
47
+ const errors = [];
48
+ if (!config.vector.url) {
49
+ errors.push(
50
+ "Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable."
51
+ );
52
+ }
53
+ if (!config.vector.token) {
54
+ errors.push(
55
+ "Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable."
56
+ );
57
+ }
58
+ if (!config.redis.url) {
59
+ errors.push(
60
+ "Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable."
61
+ );
62
+ }
63
+ if (!config.redis.token) {
64
+ errors.push(
65
+ "Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable."
66
+ );
67
+ }
68
+ if (errors.length > 0) {
69
+ throw new Error(
70
+ `Semantic Cache Configuration Error:
71
+ ${errors.map((e) => ` - ${e}`).join("\n")}`
72
+ );
73
+ }
74
+ }
75
+
76
+ // index.ts
77
+ var norm = (s) => s.trim().toLowerCase().replace(/\s+/g, " ");
78
+ var sha = (s) => crypto.createHash("sha256").update(s).digest("hex");
79
+ function canonicalizeMessages(msgs) {
80
+ return msgs.map((m) => ({
81
+ role: m.role,
82
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
83
+ }));
84
+ }
85
+ function buildScope(options) {
86
+ return {
87
+ llmModel: options.model?.modelId ?? String(options.model ?? ""),
88
+ systemHash: sha(options.system ?? ""),
89
+ params: sha(
90
+ JSON.stringify({
91
+ temperature: options.temperature,
92
+ topP: options.topP
93
+ })
94
+ ),
95
+ toolsHash: sha(JSON.stringify(options.tools ?? {}))
96
+ };
97
+ }
98
+ function createSemanticCache(config) {
99
+ const parsed = semanticCacheConfigSchema.parse(config);
100
+ validateEnvConfig(parsed);
101
+ const {
102
+ vector,
103
+ redis: redisConfig,
104
+ threshold,
105
+ ttl,
106
+ simulateStream,
107
+ debug,
108
+ cacheMode,
109
+ useFullMessages
110
+ } = parsed;
111
+ const model = parsed.model;
112
+ const index = new Index({
113
+ url: vector.url,
114
+ token: vector.token
115
+ });
116
+ const redis = new Redis({
117
+ url: redisConfig.url,
118
+ token: redisConfig.token
119
+ });
120
+ function getCacheKey(options) {
121
+ if (options.messages) {
122
+ const messages = Array.isArray(options.messages) ? options.messages : [];
123
+ if (!useFullMessages && messages.length > 0) {
124
+ const lastMessage = messages[messages.length - 1];
125
+ return JSON.stringify({
126
+ role: lastMessage.role,
127
+ content: typeof lastMessage.content === "string" ? lastMessage.content : JSON.stringify(lastMessage.content)
128
+ });
129
+ }
130
+ return JSON.stringify(canonicalizeMessages(options.messages));
131
+ }
132
+ if (options.prompt) {
133
+ return String(options.prompt);
134
+ }
135
+ return "";
136
+ }
137
+ async function checkSemanticCache(cacheInput, scope) {
138
+ const promptNorm = norm(cacheInput);
139
+ const { embedding } = await embed({
140
+ model,
141
+ value: promptNorm
142
+ });
143
+ const result = await index.query({
144
+ vector: embedding,
145
+ topK: 3,
146
+ includeMetadata: true
147
+ });
148
+ const hit = result.find((m) => {
149
+ if (m.score < threshold) return false;
150
+ const metadata = m.metadata;
151
+ if (!metadata) return false;
152
+ return metadata.llmModel === scope.llmModel && metadata.systemHash === scope.systemHash && metadata.params === scope.params && metadata.toolsHash === scope.toolsHash;
153
+ });
154
+ if (hit) {
155
+ const cached = await redis.get(hit.id.toString());
156
+ if (cached) {
157
+ if (debug) console.log("\u2705 cache hit", hit.score.toFixed(3));
158
+ return { cached, embedding, promptNorm };
159
+ }
160
+ }
161
+ if (debug) console.log("\u274C miss -> generating\u2026");
162
+ return { cached: null, embedding, promptNorm };
163
+ }
164
+ async function storeInCache(id, data, embedding, promptNorm, scope) {
165
+ const lockKey = "lock:" + id;
166
+ const ok = await redis.set(lockKey, "1", { nx: true, ex: 15 });
167
+ if (!ok) {
168
+ if (debug)
169
+ console.log("\u26A0\uFE0F Another process is writing to cache, skipping");
170
+ return;
171
+ }
172
+ try {
173
+ await redis.set(id, data, { ex: ttl });
174
+ await index.upsert([
175
+ {
176
+ id,
177
+ vector: embedding,
178
+ metadata: {
179
+ prompt: promptNorm,
180
+ ...scope
181
+ }
182
+ }
183
+ ]);
184
+ } finally {
185
+ await redis.del(lockKey);
186
+ }
187
+ }
188
+ const semanticCacheMiddleware = {
189
+ wrapStream: async ({ doStream, params }) => {
190
+ const cacheInput = getCacheKey(params);
191
+ const scope = buildScope(params);
192
+ const promptScope = Object.values(scope).join("|");
193
+ const { cached, embedding, promptNorm } = await checkSemanticCache(
194
+ cacheInput,
195
+ scope
196
+ );
197
+ if (cached && cacheMode !== "refresh") {
198
+ if (debug) console.log("\u2705 Returning cached stream");
199
+ let chunks = [];
200
+ if (cached.streamParts) {
201
+ chunks = cached.streamParts.map((p) => {
202
+ if (p.type === "response-metadata" && p.timestamp) {
203
+ return { ...p, timestamp: new Date(p.timestamp) };
204
+ }
205
+ return p;
206
+ });
207
+ } else if (cached.text) {
208
+ chunks = [
209
+ { type: "text-start", id: cached.id },
210
+ { type: "text-delta", delta: cached.text, id: cached.id },
211
+ { type: "finish", finishReason: "stop", usage: cached.usage }
212
+ ];
213
+ }
214
+ return {
215
+ stream: simulateReadableStream({
216
+ initialDelayInMs: simulateStream.enabled ? simulateStream.initialDelayInMs : 0,
217
+ chunkDelayInMs: simulateStream.enabled ? simulateStream.chunkDelayInMs : 0,
218
+ chunks
219
+ })
220
+ };
221
+ }
222
+ const { stream, ...rest } = await doStream();
223
+ const fullResponse = [];
224
+ const transformStream = new TransformStream({
225
+ transform(chunk, controller) {
226
+ fullResponse.push(chunk);
227
+ controller.enqueue(chunk);
228
+ },
229
+ async flush() {
230
+ const id = "llm:" + sha(promptScope + "|" + promptNorm);
231
+ await storeInCache(
232
+ id,
233
+ { streamParts: fullResponse },
234
+ embedding,
235
+ promptNorm,
236
+ scope
237
+ );
238
+ }
239
+ });
240
+ return {
241
+ stream: stream.pipeThrough(transformStream),
242
+ ...rest
243
+ };
244
+ },
245
+ wrapGenerate: async ({ doGenerate, params }) => {
246
+ const cacheInput = getCacheKey(params);
247
+ const scope = buildScope(params);
248
+ const promptScope = Object.values(scope).join("|");
249
+ const { cached, embedding, promptNorm } = await checkSemanticCache(
250
+ cacheInput,
251
+ scope
252
+ );
253
+ if (cached && cacheMode !== "refresh") {
254
+ if (debug) console.log("\u2705 Returning cached generation");
255
+ if (cached?.response?.timestamp) {
256
+ cached.response.timestamp = new Date(cached.response.timestamp);
257
+ }
258
+ return cached;
259
+ }
260
+ const result = await doGenerate();
261
+ const id = "llm:" + sha(promptScope + "|" + promptNorm);
262
+ await storeInCache(id, result, embedding, promptNorm, scope);
263
+ return result;
264
+ }
265
+ };
266
+ return {
267
+ streamText: async (options) => {
268
+ const wrappedModel = wrapLanguageModel({
269
+ model: typeof options.model === "string" ? gateway(options.model) : options.model,
270
+ middleware: semanticCacheMiddleware
271
+ });
272
+ return streamText({
273
+ ...options,
274
+ model: wrappedModel
275
+ });
276
+ },
277
+ generateText: async (options) => {
278
+ const wrappedModel = wrapLanguageModel({
279
+ model: typeof options.model === "string" ? gateway(options.model) : options.model,
280
+ middleware: semanticCacheMiddleware
281
+ });
282
+ return generateText({
283
+ ...options,
284
+ model: wrappedModel
285
+ });
286
+ },
287
+ generateObject: async (options) => {
288
+ const wrappedModel = wrapLanguageModel({
289
+ model: typeof options.model === "string" ? gateway(options.model) : options.model,
290
+ middleware: semanticCacheMiddleware
291
+ });
292
+ return await generateObject({
293
+ ...options,
294
+ model: wrappedModel
295
+ });
296
+ },
297
+ streamObject: async (options) => {
298
+ const wrappedModel = wrapLanguageModel({
299
+ model: typeof options.model === "string" ? gateway(options.model) : options.model,
300
+ middleware: semanticCacheMiddleware
301
+ });
302
+ return streamObject({
303
+ ...options,
304
+ model: wrappedModel
305
+ });
306
+ }
307
+ };
308
+ }
309
+
310
+ export { createSemanticCache };
311
+ //# sourceMappingURL=index.mjs.map
312
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../schema.ts","../index.ts"],"names":[],"mappings":";;;;;;;;;AAGO,IAAM,yBAAA,GAA4B,EAAE,MAAA,CAAO;AAAA,EAChD,KAAA,EAAO,EAAE,KAAA,CAAM;AAAA,IACb,EAAE,MAAA,EAAO;AAAA,IACT,CAAA,CAAE,MAAA,CAAO,CAAC,GAAA,KAAQ;AAChB,MAAA,OAAO,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA;AAAA,IAC/B,CAAC;AAAA,GACF,CAAA;AAAA,EACD,MAAA,EAAQ,EACL,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,EAAE,GAAA,EAAI;AAAA,IACX,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GACxB,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,EAAA;AAAA,IACpC,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,iBAAA,IAAqB;AAAA,GACzC,CAAA;AAAA,EACH,KAAA,EAAO,EACJ,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,EAAE,GAAA,EAAI;AAAA,IACX,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GACxB,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,cAAA,IAAkB,EAAA;AAAA,IACnC,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,gBAAA,IAAoB;AAAA,GACxC,CAAA;AAAA,EACH,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,QAAQ,IAAI,CAAA;AAAA,EAC3D,GAAA,EAAK,CAAA,CACF,MAAA,EAAO,CACP,QAAA,EAAS,CACT,QAAA,EAAS,CACT,OAAA,CAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA;AAAA,EAC5B,OAAO,CAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,KAAK,CAAA;AAAA,EAC3C,SAAA,EAAW,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAW,SAAS,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA;AAAA,EACtE,cAAA,EAAgB,EACb,MAAA,CAAO;AAAA,IACN,SAAS,CAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,IAAI,CAAA;AAAA,IAC5C,gBAAA,EAAkB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxD,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE;AAAA,GACxD,CAAA,CACA,QAAA,EAAS,CACT,OAAA,CAAQ;AAAA,IACP,OAAA,EAAS,IAAA;AAAA,IACT,gBAAA,EAAkB,CAAA;AAAA,IAClB,cAAA,EAAgB;AAAA,GACjB,CAAA;AAAA,EACH,iBAAiB,CAAA,CAAE,OAAA,GAAU,QAAA,EAAS,CAAE,QAAQ,KAAK;AACvD,CAAC,CAAA;AAOM,SAAS,kBAAkB,MAAA,EAAmC;AACnE,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK;AACtB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO;AACxB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK;AACrB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO;AACvB,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA;AAAA,EAAwC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,IAAA,EAAO,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAClF;AAAA,EACF;AACF;;;AC3DA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAc,CAAA,CAAE,IAAA,GAAO,WAAA,EAAY,CAAE,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA;AACtE,IAAM,GAAA,GAAM,CAAC,CAAA,KAAqB,MAAA,CAAA,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,CAAC,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAE7E,SAAS,qBAAqB,IAAA,EAAa;AACzC,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACtB,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAA,EACE,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,CAAA,CAAE,OAAO;AAAA,GACxE,CAAE,CAAA;AACJ;AAEA,SAAS,WAAW,OAAA,EAAc;AAChC,EAAA,OAAO;AAAA,IACL,UAAU,OAAA,CAAQ,KAAA,EAAO,WAAW,MAAA,CAAO,OAAA,CAAQ,SAAS,EAAE,CAAA;AAAA,IAC9D,UAAA,EAAY,GAAA,CAAI,OAAA,CAAQ,MAAA,IAAU,EAAE,CAAA;AAAA,IACpC,MAAA,EAAQ,GAAA;AAAA,MACN,KAAK,SAAA,CAAU;AAAA,QACb,aAAa,OAAA,CAAQ,WAAA;AAAA,QACrB,MAAM,OAAA,CAAQ;AAAA,OACf;AAAA,KACH;AAAA,IACA,SAAA,EAAW,IAAI,IAAA,CAAK,SAAA,CAAU,QAAQ,KAAA,IAAS,EAAE,CAAC;AAAA,GACpD;AACF;AAEO,SAAS,oBAAoB,MAAA,EAA6B;AAC/D,EAAA,MAAM,MAAA,GAAS,yBAAA,CAA0B,KAAA,CAAM,MAAM,CAAA;AAErD,EAAA,iBAAA,CAAkB,MAAM,CAAA;AAExB,EAAA,MAAM;AAAA,IACJ,MAAA;AAAA,IACA,KAAA,EAAO,WAAA;AAAA,IACP,SAAA;AAAA,IACA,GAAA;AAAA,IACA,cAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF,GAAI,MAAA;AACJ,EAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AAErB,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM;AAAA,IACtB,KAAK,MAAA,CAAO,GAAA;AAAA,IACZ,OAAO,MAAA,CAAO;AAAA,GACf,CAAA;AAED,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM;AAAA,IACtB,KAAK,WAAA,CAAY,GAAA;AAAA,IACjB,OAAO,WAAA,CAAY;AAAA,GACpB,CAAA;AAED,EAAA,SAAS,YAAY,OAAA,EAAsB;AACzC,IAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,GAAI,OAAA,CAAQ,WAAW,EAAC;AAGvE,MAAA,IAAI,CAAC,eAAA,IAAmB,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAC3C,QAAA,MAAM,WAAA,GAAc,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA;AAChD,QAAA,OAAO,KAAK,SAAA,CAAU;AAAA,UACpB,MAAM,WAAA,CAAY,IAAA;AAAA,UAClB,OAAA,EACE,OAAO,WAAA,CAAY,OAAA,KAAY,QAAA,GAC3B,YAAY,OAAA,GACZ,IAAA,CAAK,SAAA,CAAU,WAAA,CAAY,OAAO;AAAA,SACzC,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,IAAA,CAAK,SAAA,CAAU,oBAAA,CAAqB,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAAA,IAC9D;AACA,IAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,MAAA,OAAO,MAAA,CAAO,QAAQ,MAAM,CAAA;AAAA,IAC9B;AACA,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,eAAe,kBAAA,CAAmB,YAAoB,KAAA,EAAY;AAChE,IAAA,MAAM,UAAA,GAAa,KAAK,UAAU,CAAA;AAElC,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,KAAA,CAAM;AAAA,MAChC,KAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACR,CAAA;AAED,IAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,KAAA,CAAM;AAAA,MAC/B,MAAA,EAAQ,SAAA;AAAA,MACR,IAAA,EAAM,CAAA;AAAA,MACN,eAAA,EAAiB;AAAA,KAClB,CAAA;AAED,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM;AAC7B,MAAA,IAAI,CAAA,CAAE,KAAA,GAAQ,SAAA,EAAW,OAAO,KAAA;AAEhC,MAAA,MAAM,WAAW,CAAA,CAAE,QAAA;AACnB,MAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AAEtB,MAAA,OACE,QAAA,CAAS,QAAA,KAAa,KAAA,CAAM,QAAA,IAC5B,SAAS,UAAA,KAAe,KAAA,CAAM,UAAA,IAC9B,QAAA,CAAS,MAAA,KAAW,KAAA,CAAM,MAAA,IAC1B,QAAA,CAAS,cAAc,KAAA,CAAM,SAAA;AAAA,IAEjC,CAAC,CAAA;AAED,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,MAAM,SAAS,MAAM,KAAA,CAAM,IAKxB,GAAA,CAAI,EAAA,CAAG,UAAU,CAAA;AAEpB,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,IAAI,KAAA,UAAe,GAAA,CAAI,kBAAA,EAAe,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA;AAC1D,QAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,EAAW;AAAA,MACzC;AAAA,IACF;AAEA,IAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,iCAAuB,CAAA;AAC9C,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,UAAA,EAAW;AAAA,EAC/C;AAEA,EAAA,eAAe,YAAA,CACb,EAAA,EACA,IAAA,EACA,SAAA,EACA,YACA,KAAA,EACA;AACA,IAAA,MAAM,UAAU,OAAA,GAAU,EAAA;AAC1B,IAAA,MAAM,EAAA,GAAK,MAAM,KAAA,CAAM,GAAA,CAAI,OAAA,EAAS,GAAA,EAAK,EAAE,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,EAAA,EAAI,CAAA;AAE7D,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,IAAI,KAAA;AACF,QAAA,OAAA,CAAQ,IAAI,4DAAkD,CAAA;AAChE,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,GAAA,CAAI,EAAA,EAAI,MAAM,EAAE,EAAA,EAAI,KAAK,CAAA;AACrC,MAAA,MAAM,MAAM,MAAA,CAAO;AAAA,QACjB;AAAA,UACE,EAAA;AAAA,UACA,MAAA,EAAQ,SAAA;AAAA,UACR,QAAA,EAAU;AAAA,YACR,MAAA,EAAQ,UAAA;AAAA,YACR,GAAG;AAAA;AACL;AACF,OACD,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,MAAM,KAAA,CAAM,IAAI,OAAO,CAAA;AAAA,IACzB;AAAA,EACF;AAEA,EAAA,MAAM,uBAAA,GAAqD;AAAA,IACzD,UAAA,EAAY,OAAO,EAAE,QAAA,EAAU,QAAO,KAAM;AAC1C,MAAA,MAAM,UAAA,GAAa,YAAY,MAAM,CAAA;AACrC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,CAAA;AAC/B,MAAA,MAAM,cAAc,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEjD,MAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,KAAe,MAAM,kBAAA;AAAA,QAC9C,UAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,MAAA,IAAU,cAAc,SAAA,EAAW;AACrC,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,gCAA2B,CAAA;AAElD,QAAA,IAAI,SAAsC,EAAC;AAE3C,QAAA,IAAI,OAAO,WAAA,EAAa;AACtB,UAAA,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAW;AAC1C,YAAA,IAAI,CAAA,CAAE,IAAA,KAAS,mBAAA,IAAuB,CAAA,CAAE,SAAA,EAAW;AACjD,cAAA,OAAO,EAAE,GAAG,CAAA,EAAG,SAAA,EAAW,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,CAAA,EAAE;AAAA,YAClD;AACA,YAAA,OAAO,CAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA,MAAA,IAAW,OAAO,IAAA,EAAM;AACtB,UAAA,MAAA,GAAS;AAAA,YACP,EAAE,IAAA,EAAM,YAAA,EAAc,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,YACpC,EAAE,MAAM,YAAA,EAAc,KAAA,EAAO,OAAO,IAAA,EAAM,EAAA,EAAI,OAAO,EAAA,EAAG;AAAA,YACxD,EAAE,IAAA,EAAM,QAAA,EAAU,cAAc,MAAA,EAAQ,KAAA,EAAO,OAAO,KAAA;AAAM,WAC9D;AAAA,QACF;AAEA,QAAA,OAAO;AAAA,UACL,QAAQ,sBAAA,CAAuB;AAAA,YAC7B,gBAAA,EAAkB,cAAA,CAAe,OAAA,GAC7B,cAAA,CAAe,gBAAA,GACf,CAAA;AAAA,YACJ,cAAA,EAAgB,cAAA,CAAe,OAAA,GAC3B,cAAA,CAAe,cAAA,GACf,CAAA;AAAA,YACJ;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,IAAA,EAAK,GAAI,MAAM,QAAA,EAAS;AAE3C,MAAA,MAAM,eAA4C,EAAC;AAEnD,MAAA,MAAM,eAAA,GAAkB,IAAI,eAAA,CAG1B;AAAA,QACA,SAAA,CAAU,OAAO,UAAA,EAAY;AAC3B,UAAA,YAAA,CAAa,KAAK,KAAK,CAAA;AACvB,UAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA,QAC1B,CAAA;AAAA,QACA,MAAM,KAAA,GAAQ;AACZ,UAAA,MAAM,EAAA,GAAK,MAAA,GAAS,GAAA,CAAI,WAAA,GAAc,MAAM,UAAU,CAAA;AACtD,UAAA,MAAM,YAAA;AAAA,YACJ,EAAA;AAAA,YACA,EAAE,aAAa,YAAA,EAAa;AAAA,YAC5B,SAAA;AAAA,YACA,UAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AAAA,OACD,CAAA;AAED,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,MAAA,CAAO,WAAA,CAAY,eAAe,CAAA;AAAA,QAC1C,GAAG;AAAA,OACL;AAAA,IACF,CAAA;AAAA,IAEA,YAAA,EAAc,OAAO,EAAE,UAAA,EAAY,QAAO,KAAM;AAC9C,MAAA,MAAM,UAAA,GAAa,YAAY,MAAM,CAAA;AACrC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,CAAA;AAC/B,MAAA,MAAM,cAAc,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEjD,MAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAA,KAAe,MAAM,kBAAA;AAAA,QAC9C,UAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,MAAA,IAAU,cAAc,SAAA,EAAW;AACrC,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,oCAA+B,CAAA;AAEtD,QAAA,IAAI,MAAA,EAAQ,UAAU,SAAA,EAAW;AAC/B,UAAA,MAAA,CAAO,SAAS,SAAA,GAAY,IAAI,IAAA,CAAK,MAAA,CAAO,SAAS,SAAS,CAAA;AAAA,QAChE;AAGA,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,UAAA,EAAW;AAEhC,MAAA,MAAM,EAAA,GAAK,MAAA,GAAS,GAAA,CAAI,WAAA,GAAc,MAAM,UAAU,CAAA;AACtD,MAAA,MAAM,YAAA,CAAa,EAAA,EAAI,MAAA,EAAQ,SAAA,EAAW,YAAY,KAAK,CAAA;AAE3D,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,OACV,OAAA,KAC0C;AAC1C,MAAA,MAAM,eAAe,iBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrB,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAO,UAAA,CAAW;AAAA,QAChB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,YAAA,EAAc,OAIZ,OAAA,KAC+C;AAC/C,MAAA,MAAM,eAAe,iBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrB,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAO,YAAA,CAAa;AAAA,QAClB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,cAAA,EAAgB,OACd,OAAA,KACqC;AACrC,MAAA,MAAM,eAAe,iBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrB,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAQ,MAAM,cAAA,CAAe;AAAA,QAC3B,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,YAAA,EAAc,OACZ,OAAA,KAC2C;AAC3C,MAAA,MAAM,eAAe,iBAAA,CAAkB;AAAA,QACrC,KAAA,EACE,OAAO,OAAA,CAAQ,KAAA,KAAU,WACrB,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA,GACrB,OAAA,CAAQ,KAAA;AAAA,QACd,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,OAAO,YAAA,CAAa;AAAA,QAClB,GAAG,OAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH;AAAA,GACF;AACF","file":"index.mjs","sourcesContent":["import { z } from \"zod\";\nimport \"dotenv/config\";\n\nexport const semanticCacheConfigSchema = z.object({\n model: z.union([\n z.string(),\n z.custom((val) => {\n return val && typeof val === \"object\";\n }),\n ]),\n vector: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.VECTOR_REST_URL ?? \"\",\n token: process.env.VECTOR_REST_TOKEN ?? \"\",\n }),\n redis: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.REDIS_REST_URL ?? \"\",\n token: process.env.REDIS_REST_TOKEN ?? \"\",\n }),\n threshold: z.number().min(0).max(1).optional().default(0.92),\n ttl: z\n .number()\n .positive()\n .optional()\n .default(60 * 60 * 24 * 14),\n debug: z.boolean().optional().default(false),\n cacheMode: z.enum([\"default\", \"refresh\"]).optional().default(\"default\"),\n simulateStream: z\n .object({\n enabled: z.boolean().optional().default(true),\n initialDelayInMs: z.number().min(0).optional().default(0),\n chunkDelayInMs: z.number().min(0).optional().default(10),\n })\n .optional()\n .default({\n enabled: true,\n initialDelayInMs: 0,\n chunkDelayInMs: 10,\n }),\n useFullMessages: z.boolean().optional().default(false),\n});\n\nexport type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;\nexport type SemanticCacheConfigParsed = z.output<\n typeof semanticCacheConfigSchema\n>;\n\nexport function validateEnvConfig(config: SemanticCacheConfigParsed) {\n const errors: string[] = [];\n\n if (!config.vector.url) {\n errors.push(\n \"Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable.\",\n );\n }\n\n if (!config.vector.token) {\n errors.push(\n \"Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable.\",\n );\n }\n\n if (!config.redis.url) {\n errors.push(\n \"Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable.\",\n );\n }\n\n if (!config.redis.token) {\n errors.push(\n \"Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable.\",\n );\n }\n\n if (errors.length > 0) {\n throw new Error(\n `Semantic Cache Configuration Error:\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`,\n );\n }\n}\n","import {\n type LanguageModelV2Middleware,\n type LanguageModelV2StreamPart,\n type LanguageModelV2Content,\n type LanguageModelV2CallOptions,\n} from \"@ai-sdk/provider\";\nimport {\n embed,\n streamText,\n generateText,\n generateObject,\n streamObject,\n simulateReadableStream,\n wrapLanguageModel,\n type EmbeddingModel,\n type StreamTextResult,\n type GenerateTextResult,\n type GenerateObjectResult,\n type StreamObjectResult,\n gateway,\n} from \"ai\";\n\nimport { Index } from \"@upstash/vector\";\nimport { Redis } from \"@upstash/redis\";\nimport * as crypto from \"node:crypto\";\nimport {\n semanticCacheConfigSchema,\n type SemanticCacheConfig,\n validateEnvConfig,\n} from \"./schema\";\n\nconst norm = (s: string) => s.trim().toLowerCase().replace(/\\s+/g, \" \");\nconst sha = (s: string) => crypto.createHash(\"sha256\").update(s).digest(\"hex\");\n\nfunction canonicalizeMessages(msgs: any[]) {\n return msgs.map((m) => ({\n role: m.role,\n content:\n typeof m.content === \"string\" ? m.content : JSON.stringify(m.content),\n }));\n}\n\nfunction buildScope(options: any) {\n return {\n llmModel: options.model?.modelId ?? String(options.model ?? \"\"),\n systemHash: sha(options.system ?? \"\"),\n params: sha(\n JSON.stringify({\n temperature: options.temperature,\n topP: options.topP,\n }),\n ),\n toolsHash: sha(JSON.stringify(options.tools ?? {})),\n };\n}\n\nexport function createSemanticCache(config: SemanticCacheConfig) {\n const parsed = semanticCacheConfigSchema.parse(config);\n\n validateEnvConfig(parsed);\n\n const {\n vector,\n redis: redisConfig,\n threshold,\n ttl,\n simulateStream,\n debug,\n cacheMode,\n useFullMessages,\n } = parsed;\n const model = parsed.model as EmbeddingModel<string>;\n\n const index = new Index({\n url: vector.url as string,\n token: vector.token as string,\n });\n\n const redis = new Redis({\n url: redisConfig.url as string,\n token: redisConfig.token as string,\n });\n\n function getCacheKey(options: any): string {\n if (options.messages) {\n const messages = Array.isArray(options.messages) ? options.messages : [];\n\n // By default, use only the last message to avoid token limit issues\n if (!useFullMessages && messages.length > 0) {\n const lastMessage = messages[messages.length - 1];\n return JSON.stringify({\n role: lastMessage.role,\n content:\n typeof lastMessage.content === \"string\"\n ? lastMessage.content\n : JSON.stringify(lastMessage.content),\n });\n }\n\n return JSON.stringify(canonicalizeMessages(options.messages));\n }\n if (options.prompt) {\n return String(options.prompt);\n }\n return \"\";\n }\n\n async function checkSemanticCache(cacheInput: string, scope: any) {\n const promptNorm = norm(cacheInput);\n\n const { embedding } = await embed({\n model,\n value: promptNorm,\n });\n\n const result = await index.query({\n vector: embedding,\n topK: 3,\n includeMetadata: true,\n });\n\n const hit = result.find((m) => {\n if (m.score < threshold) return false;\n\n const metadata = m.metadata as any;\n if (!metadata) return false;\n\n return (\n metadata.llmModel === scope.llmModel &&\n metadata.systemHash === scope.systemHash &&\n metadata.params === scope.params &&\n metadata.toolsHash === scope.toolsHash\n );\n });\n\n if (hit) {\n const cached = await redis.get<{\n streamParts?: LanguageModelV2StreamPart[];\n text?: string;\n content?: LanguageModelV2Content;\n [key: string]: any;\n }>(hit.id.toString());\n\n if (cached) {\n if (debug) console.log(\"✅ cache hit\", hit.score.toFixed(3));\n return { cached, embedding, promptNorm };\n }\n }\n\n if (debug) console.log(\"❌ miss -> generating…\");\n return { cached: null, embedding, promptNorm };\n }\n\n async function storeInCache(\n id: string,\n data: any,\n embedding: number[],\n promptNorm: string,\n scope: any,\n ) {\n const lockKey = \"lock:\" + id;\n const ok = await redis.set(lockKey, \"1\", { nx: true, ex: 15 });\n\n if (!ok) {\n if (debug)\n console.log(\"⚠️ Another process is writing to cache, skipping\");\n return;\n }\n\n try {\n await redis.set(id, data, { ex: ttl });\n await index.upsert([\n {\n id,\n vector: embedding,\n metadata: {\n prompt: promptNorm,\n ...scope,\n },\n },\n ]);\n } finally {\n await redis.del(lockKey);\n }\n }\n\n const semanticCacheMiddleware: LanguageModelV2Middleware = {\n wrapStream: async ({ doStream, params }) => {\n const cacheInput = getCacheKey(params);\n const scope = buildScope(params);\n const promptScope = Object.values(scope).join(\"|\");\n\n const { cached, embedding, promptNorm } = await checkSemanticCache(\n cacheInput,\n scope,\n );\n\n if (cached && cacheMode !== \"refresh\") {\n if (debug) console.log(\"✅ Returning cached stream\");\n\n let chunks: LanguageModelV2StreamPart[] = [];\n\n if (cached.streamParts) {\n chunks = cached.streamParts.map((p: any) => {\n if (p.type === \"response-metadata\" && p.timestamp) {\n return { ...p, timestamp: new Date(p.timestamp) };\n }\n return p;\n });\n } else if (cached.text) {\n chunks = [\n { type: \"text-start\", id: cached.id },\n { type: \"text-delta\", delta: cached.text, id: cached.id },\n { type: \"finish\", finishReason: \"stop\", usage: cached.usage },\n ];\n }\n\n return {\n stream: simulateReadableStream({\n initialDelayInMs: simulateStream.enabled\n ? simulateStream.initialDelayInMs\n : 0,\n chunkDelayInMs: simulateStream.enabled\n ? simulateStream.chunkDelayInMs\n : 0,\n chunks,\n }),\n };\n }\n\n const { stream, ...rest } = await doStream();\n\n const fullResponse: LanguageModelV2StreamPart[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV2StreamPart,\n LanguageModelV2StreamPart\n >({\n transform(chunk, controller) {\n fullResponse.push(chunk);\n controller.enqueue(chunk);\n },\n async flush() {\n const id = \"llm:\" + sha(promptScope + \"|\" + promptNorm);\n await storeInCache(\n id,\n { streamParts: fullResponse },\n embedding,\n promptNorm,\n scope,\n );\n },\n });\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n };\n },\n\n wrapGenerate: async ({ doGenerate, params }) => {\n const cacheInput = getCacheKey(params);\n const scope = buildScope(params);\n const promptScope = Object.values(scope).join(\"|\");\n\n const { cached, embedding, promptNorm } = await checkSemanticCache(\n cacheInput,\n scope,\n );\n\n if (cached && cacheMode !== \"refresh\") {\n if (debug) console.log(\"✅ Returning cached generation\");\n\n if (cached?.response?.timestamp) {\n cached.response.timestamp = new Date(cached.response.timestamp);\n }\n\n type GenReturn<T> = T extends () => PromiseLike<infer R> ? R : never;\n return cached as unknown as GenReturn<typeof doGenerate>;\n }\n\n const result = await doGenerate();\n\n const id = \"llm:\" + sha(promptScope + \"|\" + promptNorm);\n await storeInCache(id, result, embedding, promptNorm, scope);\n\n return result;\n },\n };\n\n return {\n streamText: async <TOOLS extends Record<string, any> = {}>(\n options: Parameters<typeof streamText<TOOLS>>[0],\n ): Promise<StreamTextResult<TOOLS, any>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return streamText({\n ...options,\n model: wrappedModel,\n });\n },\n\n generateText: async <\n TOOLS extends Record<string, any> = {},\n OUTPUT = undefined,\n >(\n options: Parameters<typeof generateText<TOOLS, OUTPUT>>[0],\n ): Promise<GenerateTextResult<TOOLS, OUTPUT>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return generateText({\n ...options,\n model: wrappedModel,\n });\n },\n\n generateObject: async <T = any>(\n options: Parameters<typeof generateObject>[0],\n ): Promise<GenerateObjectResult<T>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return (await generateObject({\n ...options,\n model: wrappedModel,\n })) as GenerateObjectResult<T>;\n },\n\n streamObject: async <T = any>(\n options: Parameters<typeof streamObject>[0],\n ): Promise<StreamObjectResult<T, T, any>> => {\n const wrappedModel = wrapLanguageModel({\n model:\n typeof options.model === \"string\"\n ? gateway(options.model)\n : options.model,\n middleware: semanticCacheMiddleware,\n });\n\n return streamObject({\n ...options,\n model: wrappedModel,\n }) as unknown as StreamObjectResult<T, T, any>;\n },\n };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const semanticCacheConfigSchema: z.ZodObject<{
4
+ model: z.ZodUnion<readonly [z.ZodString, z.ZodCustom<unknown, unknown>]>;
5
+ vector: z.ZodDefault<z.ZodOptional<z.ZodObject<{
6
+ url: z.ZodURL;
7
+ token: z.ZodString;
8
+ }, z.core.$strip>>>;
9
+ redis: z.ZodDefault<z.ZodOptional<z.ZodObject<{
10
+ url: z.ZodURL;
11
+ token: z.ZodString;
12
+ }, z.core.$strip>>>;
13
+ threshold: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
14
+ ttl: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
15
+ debug: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
16
+ cacheMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
17
+ default: "default";
18
+ refresh: "refresh";
19
+ }>>>;
20
+ simulateStream: z.ZodDefault<z.ZodOptional<z.ZodObject<{
21
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
22
+ initialDelayInMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
23
+ chunkDelayInMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
24
+ }, z.core.$strip>>>;
25
+ useFullMessages: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
26
+ }, z.core.$strip>;
27
+ type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;
28
+ type SemanticCacheConfigParsed = z.output<typeof semanticCacheConfigSchema>;
29
+ declare function validateEnvConfig(config: SemanticCacheConfigParsed): void;
30
+
31
+ export { type SemanticCacheConfig, type SemanticCacheConfigParsed, semanticCacheConfigSchema, validateEnvConfig };
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const semanticCacheConfigSchema: z.ZodObject<{
4
+ model: z.ZodUnion<readonly [z.ZodString, z.ZodCustom<unknown, unknown>]>;
5
+ vector: z.ZodDefault<z.ZodOptional<z.ZodObject<{
6
+ url: z.ZodURL;
7
+ token: z.ZodString;
8
+ }, z.core.$strip>>>;
9
+ redis: z.ZodDefault<z.ZodOptional<z.ZodObject<{
10
+ url: z.ZodURL;
11
+ token: z.ZodString;
12
+ }, z.core.$strip>>>;
13
+ threshold: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
14
+ ttl: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
15
+ debug: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
16
+ cacheMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
17
+ default: "default";
18
+ refresh: "refresh";
19
+ }>>>;
20
+ simulateStream: z.ZodDefault<z.ZodOptional<z.ZodObject<{
21
+ enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
22
+ initialDelayInMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
23
+ chunkDelayInMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
24
+ }, z.core.$strip>>>;
25
+ useFullMessages: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
26
+ }, z.core.$strip>;
27
+ type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;
28
+ type SemanticCacheConfigParsed = z.output<typeof semanticCacheConfigSchema>;
29
+ declare function validateEnvConfig(config: SemanticCacheConfigParsed): void;
30
+
31
+ export { type SemanticCacheConfig, type SemanticCacheConfigParsed, semanticCacheConfigSchema, validateEnvConfig };
package/dist/schema.js ADDED
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ require('dotenv/config');
5
+
6
+ // schema.ts
7
+ var semanticCacheConfigSchema = zod.z.object({
8
+ model: zod.z.union([
9
+ zod.z.string(),
10
+ zod.z.custom((val) => {
11
+ return val && typeof val === "object";
12
+ })
13
+ ]),
14
+ vector: zod.z.object({
15
+ url: zod.z.url(),
16
+ token: zod.z.string().min(1)
17
+ }).optional().default({
18
+ url: process.env.VECTOR_REST_URL ?? "",
19
+ token: process.env.VECTOR_REST_TOKEN ?? ""
20
+ }),
21
+ redis: zod.z.object({
22
+ url: zod.z.url(),
23
+ token: zod.z.string().min(1)
24
+ }).optional().default({
25
+ url: process.env.REDIS_REST_URL ?? "",
26
+ token: process.env.REDIS_REST_TOKEN ?? ""
27
+ }),
28
+ threshold: zod.z.number().min(0).max(1).optional().default(0.92),
29
+ ttl: zod.z.number().positive().optional().default(60 * 60 * 24 * 14),
30
+ debug: zod.z.boolean().optional().default(false),
31
+ cacheMode: zod.z.enum(["default", "refresh"]).optional().default("default"),
32
+ simulateStream: zod.z.object({
33
+ enabled: zod.z.boolean().optional().default(true),
34
+ initialDelayInMs: zod.z.number().min(0).optional().default(0),
35
+ chunkDelayInMs: zod.z.number().min(0).optional().default(10)
36
+ }).optional().default({
37
+ enabled: true,
38
+ initialDelayInMs: 0,
39
+ chunkDelayInMs: 10
40
+ }),
41
+ useFullMessages: zod.z.boolean().optional().default(false)
42
+ });
43
+ function validateEnvConfig(config) {
44
+ const errors = [];
45
+ if (!config.vector.url) {
46
+ errors.push(
47
+ "Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable."
48
+ );
49
+ }
50
+ if (!config.vector.token) {
51
+ errors.push(
52
+ "Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable."
53
+ );
54
+ }
55
+ if (!config.redis.url) {
56
+ errors.push(
57
+ "Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable."
58
+ );
59
+ }
60
+ if (!config.redis.token) {
61
+ errors.push(
62
+ "Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable."
63
+ );
64
+ }
65
+ if (errors.length > 0) {
66
+ throw new Error(
67
+ `Semantic Cache Configuration Error:
68
+ ${errors.map((e) => ` - ${e}`).join("\n")}`
69
+ );
70
+ }
71
+ }
72
+
73
+ exports.semanticCacheConfigSchema = semanticCacheConfigSchema;
74
+ exports.validateEnvConfig = validateEnvConfig;
75
+ //# sourceMappingURL=schema.js.map
76
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../schema.ts"],"names":["z"],"mappings":"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","file":"schema.js","sourcesContent":["import { z } from \"zod\";\nimport \"dotenv/config\";\n\nexport const semanticCacheConfigSchema = z.object({\n model: z.union([\n z.string(),\n z.custom((val) => {\n return val && typeof val === \"object\";\n }),\n ]),\n vector: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.VECTOR_REST_URL ?? \"\",\n token: process.env.VECTOR_REST_TOKEN ?? \"\",\n }),\n redis: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.REDIS_REST_URL ?? \"\",\n token: process.env.REDIS_REST_TOKEN ?? \"\",\n }),\n threshold: z.number().min(0).max(1).optional().default(0.92),\n ttl: z\n .number()\n .positive()\n .optional()\n .default(60 * 60 * 24 * 14),\n debug: z.boolean().optional().default(false),\n cacheMode: z.enum([\"default\", \"refresh\"]).optional().default(\"default\"),\n simulateStream: z\n .object({\n enabled: z.boolean().optional().default(true),\n initialDelayInMs: z.number().min(0).optional().default(0),\n chunkDelayInMs: z.number().min(0).optional().default(10),\n })\n .optional()\n .default({\n enabled: true,\n initialDelayInMs: 0,\n chunkDelayInMs: 10,\n }),\n useFullMessages: z.boolean().optional().default(false),\n});\n\nexport type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;\nexport type SemanticCacheConfigParsed = z.output<\n typeof semanticCacheConfigSchema\n>;\n\nexport function validateEnvConfig(config: SemanticCacheConfigParsed) {\n const errors: string[] = [];\n\n if (!config.vector.url) {\n errors.push(\n \"Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable.\",\n );\n }\n\n if (!config.vector.token) {\n errors.push(\n \"Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable.\",\n );\n }\n\n if (!config.redis.url) {\n errors.push(\n \"Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable.\",\n );\n }\n\n if (!config.redis.token) {\n errors.push(\n \"Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable.\",\n );\n }\n\n if (errors.length > 0) {\n throw new Error(\n `Semantic Cache Configuration Error:\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`,\n );\n }\n}\n"]}
@@ -0,0 +1,73 @@
1
+ import { z } from 'zod';
2
+ import 'dotenv/config';
3
+
4
+ // schema.ts
5
+ var semanticCacheConfigSchema = z.object({
6
+ model: z.union([
7
+ z.string(),
8
+ z.custom((val) => {
9
+ return val && typeof val === "object";
10
+ })
11
+ ]),
12
+ vector: z.object({
13
+ url: z.url(),
14
+ token: z.string().min(1)
15
+ }).optional().default({
16
+ url: process.env.VECTOR_REST_URL ?? "",
17
+ token: process.env.VECTOR_REST_TOKEN ?? ""
18
+ }),
19
+ redis: z.object({
20
+ url: z.url(),
21
+ token: z.string().min(1)
22
+ }).optional().default({
23
+ url: process.env.REDIS_REST_URL ?? "",
24
+ token: process.env.REDIS_REST_TOKEN ?? ""
25
+ }),
26
+ threshold: z.number().min(0).max(1).optional().default(0.92),
27
+ ttl: z.number().positive().optional().default(60 * 60 * 24 * 14),
28
+ debug: z.boolean().optional().default(false),
29
+ cacheMode: z.enum(["default", "refresh"]).optional().default("default"),
30
+ simulateStream: z.object({
31
+ enabled: z.boolean().optional().default(true),
32
+ initialDelayInMs: z.number().min(0).optional().default(0),
33
+ chunkDelayInMs: z.number().min(0).optional().default(10)
34
+ }).optional().default({
35
+ enabled: true,
36
+ initialDelayInMs: 0,
37
+ chunkDelayInMs: 10
38
+ }),
39
+ useFullMessages: z.boolean().optional().default(false)
40
+ });
41
+ function validateEnvConfig(config) {
42
+ const errors = [];
43
+ if (!config.vector.url) {
44
+ errors.push(
45
+ "Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable."
46
+ );
47
+ }
48
+ if (!config.vector.token) {
49
+ errors.push(
50
+ "Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable."
51
+ );
52
+ }
53
+ if (!config.redis.url) {
54
+ errors.push(
55
+ "Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable."
56
+ );
57
+ }
58
+ if (!config.redis.token) {
59
+ errors.push(
60
+ "Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable."
61
+ );
62
+ }
63
+ if (errors.length > 0) {
64
+ throw new Error(
65
+ `Semantic Cache Configuration Error:
66
+ ${errors.map((e) => ` - ${e}`).join("\n")}`
67
+ );
68
+ }
69
+ }
70
+
71
+ export { semanticCacheConfigSchema, validateEnvConfig };
72
+ //# sourceMappingURL=schema.mjs.map
73
+ //# sourceMappingURL=schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../schema.ts"],"names":[],"mappings":"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","file":"schema.mjs","sourcesContent":["import { z } from \"zod\";\nimport \"dotenv/config\";\n\nexport const semanticCacheConfigSchema = z.object({\n model: z.union([\n z.string(),\n z.custom((val) => {\n return val && typeof val === \"object\";\n }),\n ]),\n vector: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.VECTOR_REST_URL ?? \"\",\n token: process.env.VECTOR_REST_TOKEN ?? \"\",\n }),\n redis: z\n .object({\n url: z.url(),\n token: z.string().min(1),\n })\n .optional()\n .default({\n url: process.env.REDIS_REST_URL ?? \"\",\n token: process.env.REDIS_REST_TOKEN ?? \"\",\n }),\n threshold: z.number().min(0).max(1).optional().default(0.92),\n ttl: z\n .number()\n .positive()\n .optional()\n .default(60 * 60 * 24 * 14),\n debug: z.boolean().optional().default(false),\n cacheMode: z.enum([\"default\", \"refresh\"]).optional().default(\"default\"),\n simulateStream: z\n .object({\n enabled: z.boolean().optional().default(true),\n initialDelayInMs: z.number().min(0).optional().default(0),\n chunkDelayInMs: z.number().min(0).optional().default(10),\n })\n .optional()\n .default({\n enabled: true,\n initialDelayInMs: 0,\n chunkDelayInMs: 10,\n }),\n useFullMessages: z.boolean().optional().default(false),\n});\n\nexport type SemanticCacheConfig = z.input<typeof semanticCacheConfigSchema>;\nexport type SemanticCacheConfigParsed = z.output<\n typeof semanticCacheConfigSchema\n>;\n\nexport function validateEnvConfig(config: SemanticCacheConfigParsed) {\n const errors: string[] = [];\n\n if (!config.vector.url) {\n errors.push(\n \"Vector URL is required. Provide 'vector.url' or set VECTOR_REST_URL environment variable.\",\n );\n }\n\n if (!config.vector.token) {\n errors.push(\n \"Vector token is required. Provide 'vector.token' or set VECTOR_REST_TOKEN environment variable.\",\n );\n }\n\n if (!config.redis.url) {\n errors.push(\n \"Redis URL is required. Provide 'redis.url' or set REDIS_REST_URL environment variable.\",\n );\n }\n\n if (!config.redis.token) {\n errors.push(\n \"Redis token is required. Provide 'redis.token' or set REDIS_REST_TOKEN environment variable.\",\n );\n }\n\n if (errors.length > 0) {\n throw new Error(\n `Semantic Cache Configuration Error:\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`,\n );\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "ai-sdk-memory",
3
+ "version": "0.0.1",
4
+ "description": "Semantic caching layer for AI SDK. Automatically generates embeddings for prompts, stores them in Upstash Vector and Redis, and reuses semantically similar responses to reduce token costs.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "prepublishOnly": "bun run build",
23
+ "test": "echo \"Error: no test specified\" && exit 1"
24
+ },
25
+ "keywords": [
26
+ "ai",
27
+ "ai-sdk",
28
+ "vercel",
29
+ "cache",
30
+ "semantic",
31
+ "semantic-cache",
32
+ "vector",
33
+ "upstash",
34
+ "redis",
35
+ "embedding",
36
+ "llm"
37
+ ],
38
+ "author": "",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/yourusername/ai-sdk-memory.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/yourusername/ai-sdk-memory/issues"
46
+ },
47
+ "homepage": "https://github.com/yourusername/ai-sdk-memory#readme",
48
+ "devDependencies": {
49
+ "@types/bun": "latest",
50
+ "@types/node": "^24.7.0",
51
+ "tsup": "^8.5.0",
52
+ "typescript": "^5.0.0",
53
+ "vitest": "^3.2.4"
54
+ },
55
+ "peerDependencies": {
56
+ "ai": "^5.0.0"
57
+ },
58
+ "dependencies": {
59
+ "@ai-sdk/provider": "^2.0.0",
60
+ "@upstash/redis": "^1.35.5",
61
+ "@upstash/vector": "^1.2.2",
62
+ "dotenv": "^17.2.3",
63
+ "zod": "4.1.12"
64
+ }
65
+ }