openai-cache 1.0.25 → 1.0.26
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/dist/openai_cache.js +20 -26
- package/dist/openai_cache.js.map +1 -1
- package/package.json +2 -6
- package/dist/openai_cache copy.d.ts +0 -54
- package/dist/openai_cache copy.js +0 -157
package/dist/openai_cache.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
1
|
// node imports
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import Crypto from "node:crypto";
|
|
3
|
+
import { Buffer } from "node:buffer";
|
|
4
|
+
import { Cacheable } from "cacheable";
|
|
5
|
+
import Chalk from "chalk";
|
|
11
6
|
///////////////////////////////////////////////////////////////////////////////
|
|
12
7
|
///////////////////////////////////////////////////////////////////////////////
|
|
13
8
|
// OpenAICache
|
|
@@ -34,7 +29,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
34
29
|
* });
|
|
35
30
|
* ```
|
|
36
31
|
*/
|
|
37
|
-
class OpenAICache {
|
|
32
|
+
export default class OpenAICache {
|
|
38
33
|
_cache;
|
|
39
34
|
_markResponseEnabled;
|
|
40
35
|
_verboseLevel;
|
|
@@ -49,7 +44,7 @@ class OpenAICache {
|
|
|
49
44
|
* the original response body so it is optional. so the response is { X_FROM_OPENAI_CACHE: true, ...originalResponseBody }
|
|
50
45
|
*/
|
|
51
46
|
constructor(cache, { markResponseEnabled = false, verboseLevel = 0, } = {}) {
|
|
52
|
-
this._cache = cache ?? new
|
|
47
|
+
this._cache = cache ?? new Cacheable();
|
|
53
48
|
this._markResponseEnabled = markResponseEnabled;
|
|
54
49
|
this._verboseLevel = verboseLevel;
|
|
55
50
|
}
|
|
@@ -89,30 +84,30 @@ class OpenAICache {
|
|
|
89
84
|
// If body type unsupported, skip caching
|
|
90
85
|
if (bodyForHash === null) {
|
|
91
86
|
if (this._verboseLevel > 1) {
|
|
92
|
-
console.warn(
|
|
87
|
+
console.warn(Chalk.yellow(`Skipping cache for ${method} ${url} due to unsupported body type`));
|
|
93
88
|
}
|
|
94
89
|
return fetch(input, init);
|
|
95
90
|
}
|
|
96
91
|
// Build cache key and file path
|
|
97
|
-
const cacheKey =
|
|
92
|
+
const cacheKey = Crypto.createHash("sha256")
|
|
98
93
|
.update(`${method}:${url}:${bodyForHash}`)
|
|
99
94
|
.digest("hex");
|
|
100
95
|
// console.log("cacheKey", cacheKey, bodyForHash);
|
|
101
96
|
// Log a warning if cache is disabled via environment variable, but only once to avoid spamming the console
|
|
102
97
|
if (process.env.OPENAI_CACHE === "disabled" && this.disabledCacheWarningLogged === false) {
|
|
103
98
|
if (this._verboseLevel > 0) {
|
|
104
|
-
console.warn(
|
|
99
|
+
console.warn(Chalk.red("OpenAI cache is disabled via OPENAI_CACHE=disabled. All requests will be live and no caching will occur."));
|
|
105
100
|
}
|
|
106
101
|
this.disabledCacheWarningLogged = true;
|
|
107
102
|
}
|
|
108
103
|
const cachedValue = await this._cache.get(cacheKey);
|
|
109
104
|
if (cachedValue !== undefined && process.env.OPENAI_CACHE !== "disabled") {
|
|
110
105
|
const bodyEncoding = cachedValue.bodyEncoding ?? "utf8";
|
|
111
|
-
const cachedBodyBuffer =
|
|
106
|
+
const cachedBodyBuffer = Buffer.from(cachedValue.body, bodyEncoding);
|
|
112
107
|
// For streaming SSE responses, return directly without JSON modification
|
|
113
108
|
if (OpenAICache._isStreamingResponse(cachedValue.headers)) {
|
|
114
109
|
if (this._verboseLevel > 1) {
|
|
115
|
-
console.log(
|
|
110
|
+
console.log(Chalk.green(`Cache hit for streamed ${method} ${url}`));
|
|
116
111
|
}
|
|
117
112
|
return new Response(cachedBodyBuffer, {
|
|
118
113
|
status: cachedValue.status,
|
|
@@ -120,7 +115,7 @@ class OpenAICache {
|
|
|
120
115
|
});
|
|
121
116
|
}
|
|
122
117
|
if (this._verboseLevel > 1) {
|
|
123
|
-
console.log(
|
|
118
|
+
console.log(Chalk.green(`Cache hit for non-streamed ${method} ${url}`));
|
|
124
119
|
}
|
|
125
120
|
// Return cached response
|
|
126
121
|
let newResponse = new Response(cachedBodyBuffer, {
|
|
@@ -136,7 +131,7 @@ class OpenAICache {
|
|
|
136
131
|
// Set the magic property to indicate this response is from cache
|
|
137
132
|
bodyJson.X_FROM_OPENAI_CACHE = true;
|
|
138
133
|
// Rebuild response with modified body
|
|
139
|
-
const modifiedBodyBuffer =
|
|
134
|
+
const modifiedBodyBuffer = Buffer.from(JSON.stringify(bodyJson));
|
|
140
135
|
newResponse = new Response(modifiedBodyBuffer, { status: cachedValue.status, headers: cachedValue.headers, });
|
|
141
136
|
}
|
|
142
137
|
catch (error) {
|
|
@@ -152,7 +147,7 @@ class OpenAICache {
|
|
|
152
147
|
// For streaming SSE responses, pipe through to enable progressive streaming + background caching
|
|
153
148
|
if (OpenAICache._isStreamingResponse(response.headers)) {
|
|
154
149
|
if (this._verboseLevel > 1) {
|
|
155
|
-
console.log(
|
|
150
|
+
console.log(Chalk.yellow(`Cache miss for streamed ${method} ${url} - caching in background as it streams in`));
|
|
156
151
|
}
|
|
157
152
|
if (!response.ok || !response.body) {
|
|
158
153
|
return response;
|
|
@@ -160,11 +155,11 @@ class OpenAICache {
|
|
|
160
155
|
return OpenAICache._createCachingStreamResponse(response, this._cache, cacheKey);
|
|
161
156
|
}
|
|
162
157
|
if (this._verboseLevel > 1) {
|
|
163
|
-
console.log(
|
|
158
|
+
console.log(Chalk.yellow(`Cache miss for non-streamed ${method} ${url} - caching in background`));
|
|
164
159
|
}
|
|
165
160
|
const clonedResponse = response.clone();
|
|
166
161
|
// Materialize response body for caching
|
|
167
|
-
const responseBuffer =
|
|
162
|
+
const responseBuffer = Buffer.from(await clonedResponse.arrayBuffer());
|
|
168
163
|
// Collect headers and normalize them
|
|
169
164
|
const headers = Array.from(clonedResponse.headers.entries());
|
|
170
165
|
const normalizedHeaders = OpenAICache._normalizeHeaders(headers, responseBuffer.length);
|
|
@@ -198,7 +193,7 @@ class OpenAICache {
|
|
|
198
193
|
const { done, value } = await reader.read();
|
|
199
194
|
if (done) {
|
|
200
195
|
controller.close();
|
|
201
|
-
const fullBody =
|
|
196
|
+
const fullBody = Buffer.concat(chunks);
|
|
202
197
|
const normalizedHeaders = OpenAICache._normalizeHeaders(responseHeaders, fullBody.length);
|
|
203
198
|
cache.set(cacheKey, {
|
|
204
199
|
status: responseStatus,
|
|
@@ -253,17 +248,16 @@ class OpenAICache {
|
|
|
253
248
|
if (typeof body === "string")
|
|
254
249
|
return body;
|
|
255
250
|
// Handle Buffer body
|
|
256
|
-
if (
|
|
251
|
+
if (Buffer.isBuffer(body))
|
|
257
252
|
return body.toString("base64");
|
|
258
253
|
// Handle ArrayBuffer body
|
|
259
254
|
if (body instanceof ArrayBuffer)
|
|
260
|
-
return
|
|
255
|
+
return Buffer.from(body).toString("base64");
|
|
261
256
|
// Handle typed arrays (Uint8Array, Int8Array, etc.)
|
|
262
257
|
if (ArrayBuffer.isView(body))
|
|
263
|
-
return
|
|
258
|
+
return Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString("base64");
|
|
264
259
|
// Return null for unsupported body types to skip caching
|
|
265
260
|
return null;
|
|
266
261
|
}
|
|
267
262
|
}
|
|
268
|
-
exports.default = OpenAICache;
|
|
269
263
|
//# sourceMappingURL=openai_cache.js.map
|
package/dist/openai_cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai_cache.js","sourceRoot":"","sources":["../src/openai_cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openai_cache.js","sourceRoot":"","sources":["../src/openai_cache.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openai-cache",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/openai_cache.js",
|
|
6
6
|
"types": "dist/openai_cache.d.ts",
|
|
@@ -27,11 +27,7 @@
|
|
|
27
27
|
"prepublishOnly": "npm run test && npm run build",
|
|
28
28
|
"test": "tsx --test ./test/*_test.ts",
|
|
29
29
|
"test:watch": "tsx --test --watch ./test/*_test.ts",
|
|
30
|
-
"publish:all": "npm run build && npm
|
|
31
|
-
"version:patch": "npm version patch",
|
|
32
|
-
"version:minor": "npm version minor",
|
|
33
|
-
"version:major": "npm version major",
|
|
34
|
-
"version:prerelease": "npm version prerelease --preid=rc"
|
|
30
|
+
"publish:all": "npm run build && npm version patch && npm publish --access public"
|
|
35
31
|
},
|
|
36
32
|
"keywords": [
|
|
37
33
|
"openai",
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Cacheable } from "cacheable";
|
|
2
|
-
type FetchFn = typeof globalThis.fetch;
|
|
3
|
-
type FetchInput = Parameters<FetchFn>[0];
|
|
4
|
-
type FetchInit = Parameters<FetchFn>[1];
|
|
5
|
-
type FetchResponse = Awaited<ReturnType<FetchFn>>;
|
|
6
|
-
/**
|
|
7
|
-
* OpenAICachingCacheable is a wrapper around the Fetch API that adds caching capabilities for OpenAI requests.
|
|
8
|
-
* It uses a Cacheable instance to store and retrieve cached responses based on a hash of the request details.
|
|
9
|
-
*/
|
|
10
|
-
export default class OpenAICache {
|
|
11
|
-
private readonly _cache;
|
|
12
|
-
private readonly _markResponseEnabled;
|
|
13
|
-
static readonly MarkResponseName = "X_FROM_OPENAI_CACHE";
|
|
14
|
-
/**
|
|
15
|
-
* Creates a new instance of OpenAICache.
|
|
16
|
-
*
|
|
17
|
-
* @param cache cacheable instance
|
|
18
|
-
* @param options.markResponseEnabled whether to mark cached responses with an additional property in the JSON body (default: true).
|
|
19
|
-
* This can be useful for downstream logic that needs to differentiate between live and cached responses, but it does modify
|
|
20
|
-
* the original response body so it is optional. so the response is { X_FROM_OPENAI_CACHE: true, ...originalResponseBody }
|
|
21
|
-
*/
|
|
22
|
-
constructor(cache?: Cacheable, { markResponseEnabled }?: {
|
|
23
|
-
markResponseEnabled?: boolean;
|
|
24
|
-
});
|
|
25
|
-
/**
|
|
26
|
-
* Cleans the OpenAI cache by deleting all cached values.
|
|
27
|
-
*/
|
|
28
|
-
cleanCache(): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* return a fetch function that can be passed to OpenAI client for caching support
|
|
31
|
-
*
|
|
32
|
-
* ```js
|
|
33
|
-
* const openai = new OpenAI({
|
|
34
|
-
* fetch: openaiCache.getFetchFn()
|
|
35
|
-
* });
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
getFetchFn(): (input: FetchInput, init?: FetchInit) => Promise<FetchResponse>;
|
|
39
|
-
/**
|
|
40
|
-
* This is the fetch() implementation that adds caching for OpenAI requests.
|
|
41
|
-
*
|
|
42
|
-
* @param input The resource that you wish to fetch.
|
|
43
|
-
* @param init An options object containing any custom settings that you want to apply to the request.
|
|
44
|
-
* @returns A Promise that resolves to the Response to that request.
|
|
45
|
-
*/
|
|
46
|
-
private _fetch;
|
|
47
|
-
/**
|
|
48
|
-
* Remove transfer/content encodings that no longer apply once the body is materialized
|
|
49
|
-
* and optionally set a correct content-length for the cached payload.
|
|
50
|
-
*/
|
|
51
|
-
private static _normalizeHeaders;
|
|
52
|
-
private static _serializeBodyForHash;
|
|
53
|
-
}
|
|
54
|
-
export {};
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
// node imports
|
|
7
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
-
const node_buffer_1 = require("node:buffer");
|
|
9
|
-
const cacheable_1 = require("cacheable");
|
|
10
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
12
|
-
// OpenAICache
|
|
13
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
15
|
-
/**
|
|
16
|
-
* OpenAICachingCacheable is a wrapper around the Fetch API that adds caching capabilities for OpenAI requests.
|
|
17
|
-
* It uses a Cacheable instance to store and retrieve cached responses based on a hash of the request details.
|
|
18
|
-
*/
|
|
19
|
-
class OpenAICache {
|
|
20
|
-
/**
|
|
21
|
-
* Creates a new instance of OpenAICache.
|
|
22
|
-
*
|
|
23
|
-
* @param cache cacheable instance
|
|
24
|
-
* @param options.markResponseEnabled whether to mark cached responses with an additional property in the JSON body (default: true).
|
|
25
|
-
* This can be useful for downstream logic that needs to differentiate between live and cached responses, but it does modify
|
|
26
|
-
* the original response body so it is optional. so the response is { X_FROM_OPENAI_CACHE: true, ...originalResponseBody }
|
|
27
|
-
*/
|
|
28
|
-
constructor(cache, { markResponseEnabled = false } = {}) {
|
|
29
|
-
this._cache = cache !== null && cache !== void 0 ? cache : new cacheable_1.Cacheable();
|
|
30
|
-
this._markResponseEnabled = markResponseEnabled;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Cleans the OpenAI cache by deleting all cached values.
|
|
34
|
-
*/
|
|
35
|
-
async cleanCache() {
|
|
36
|
-
await this._cache.clear();
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* return a fetch function that can be passed to OpenAI client for caching support
|
|
40
|
-
*
|
|
41
|
-
* ```js
|
|
42
|
-
* const openai = new OpenAI({
|
|
43
|
-
* fetch: openaiCache.getFetchFn()
|
|
44
|
-
* });
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
getFetchFn() {
|
|
48
|
-
return this._fetch.bind(this);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* This is the fetch() implementation that adds caching for OpenAI requests.
|
|
52
|
-
*
|
|
53
|
-
* @param input The resource that you wish to fetch.
|
|
54
|
-
* @param init An options object containing any custom settings that you want to apply to the request.
|
|
55
|
-
* @returns A Promise that resolves to the Response to that request.
|
|
56
|
-
*/
|
|
57
|
-
async _fetch(input, init) {
|
|
58
|
-
var _a, _b;
|
|
59
|
-
// Extract the URL from the input (string or Request)
|
|
60
|
-
const url = typeof input === "string" ? input : input instanceof Request ? input.url : input.toString();
|
|
61
|
-
// Normalize HTTP method
|
|
62
|
-
const method = ((init === null || init === void 0 ? void 0 : init.method) || "GET").toUpperCase();
|
|
63
|
-
// Generate body hash payload
|
|
64
|
-
const bodyForHash = OpenAICache._serializeBodyForHash(init === null || init === void 0 ? void 0 : init.body);
|
|
65
|
-
// If body type unsupported, skip caching
|
|
66
|
-
if (bodyForHash === null)
|
|
67
|
-
return fetch(input, init);
|
|
68
|
-
// Build cache key and file path
|
|
69
|
-
const cacheKey = node_crypto_1.default.createHash("sha256")
|
|
70
|
-
.update(`${method}:${url}:${bodyForHash}`)
|
|
71
|
-
.digest("hex");
|
|
72
|
-
const cached = (await this._cache.get(cacheKey));
|
|
73
|
-
if (cached !== undefined && process.env.OPENAI_CACHE !== "disabled") {
|
|
74
|
-
const bodyEncoding = (_a = cached.bodyEncoding) !== null && _a !== void 0 ? _a : "utf8";
|
|
75
|
-
const cachedBodyBuffer = node_buffer_1.Buffer.from(cached.body, bodyEncoding);
|
|
76
|
-
// Return cached response
|
|
77
|
-
let newResponse = new Response(cachedBodyBuffer, {
|
|
78
|
-
status: cached.status,
|
|
79
|
-
headers: cached.headers,
|
|
80
|
-
});
|
|
81
|
-
// honor this._markResponseEnabled option to indicate cache hit
|
|
82
|
-
const contentTypeIsJson = ((_b = newResponse.headers.get("content-type")) === null || _b === void 0 ? void 0 : _b.includes("application/json")) ? true : false;
|
|
83
|
-
if (this._markResponseEnabled && contentTypeIsJson) {
|
|
84
|
-
try {
|
|
85
|
-
// decode JSON from cachedBodyBuffer
|
|
86
|
-
const bodyJson = JSON.parse(cachedBodyBuffer.toString());
|
|
87
|
-
// Set the magic property to indicate this response is from cache
|
|
88
|
-
bodyJson.X_FROM_OPENAI_CACHE = true;
|
|
89
|
-
// Rebuild response with modified body
|
|
90
|
-
const modifiedBodyBuffer = node_buffer_1.Buffer.from(JSON.stringify(bodyJson));
|
|
91
|
-
newResponse = new Response(modifiedBodyBuffer, { status: cached.status, headers: cached.headers, });
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
// If parsing fails, return the original cached response without modification
|
|
95
|
-
console.warn("Failed to parse cached response body as JSON for header modification:", error);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Return cached response (body already buffered)
|
|
99
|
-
return newResponse;
|
|
100
|
-
}
|
|
101
|
-
// Perform network fetch
|
|
102
|
-
const response = await fetch(input, init);
|
|
103
|
-
const clonedResponse = response.clone();
|
|
104
|
-
// Materialize response body for caching
|
|
105
|
-
const responseBuffer = node_buffer_1.Buffer.from(await clonedResponse.arrayBuffer());
|
|
106
|
-
// Collect headers and normalize them
|
|
107
|
-
const headers = Array.from(clonedResponse.headers.entries());
|
|
108
|
-
const normalizedHeaders = OpenAICache._normalizeHeaders(headers, responseBuffer.length);
|
|
109
|
-
if (response.ok) {
|
|
110
|
-
await this._cache.set(cacheKey, {
|
|
111
|
-
status: clonedResponse.status,
|
|
112
|
-
headers: normalizedHeaders,
|
|
113
|
-
body: responseBuffer.toString("base64"),
|
|
114
|
-
bodyEncoding: "base64",
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
// Return live response (body already buffered)
|
|
118
|
-
return new Response(responseBuffer, { status: response.status, headers: normalizedHeaders });
|
|
119
|
-
}
|
|
120
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
121
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
122
|
-
// Private functions
|
|
123
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
124
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
125
|
-
/**
|
|
126
|
-
* Remove transfer/content encodings that no longer apply once the body is materialized
|
|
127
|
-
* and optionally set a correct content-length for the cached payload.
|
|
128
|
-
*/
|
|
129
|
-
static _normalizeHeaders(headers, bodyLength) {
|
|
130
|
-
const drop = new Set([
|
|
131
|
-
"content-encoding", // body is already decoded by fetch()
|
|
132
|
-
"transfer-encoding",
|
|
133
|
-
"content-length", // will be recalculated
|
|
134
|
-
]);
|
|
135
|
-
const filtered = headers.filter(([name]) => drop.has(name.toLowerCase()) === false);
|
|
136
|
-
if (bodyLength !== undefined) {
|
|
137
|
-
filtered.push(["content-length", String(bodyLength)]);
|
|
138
|
-
}
|
|
139
|
-
return filtered;
|
|
140
|
-
}
|
|
141
|
-
// Serialize body into a deterministic string for hashing
|
|
142
|
-
static _serializeBodyForHash(body) {
|
|
143
|
-
if (body === undefined || body === null)
|
|
144
|
-
return "";
|
|
145
|
-
if (typeof body === "string")
|
|
146
|
-
return body;
|
|
147
|
-
if (node_buffer_1.Buffer.isBuffer(body))
|
|
148
|
-
return body.toString("base64");
|
|
149
|
-
if (body instanceof ArrayBuffer)
|
|
150
|
-
return node_buffer_1.Buffer.from(body).toString("base64");
|
|
151
|
-
if (ArrayBuffer.isView(body))
|
|
152
|
-
return node_buffer_1.Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString("base64");
|
|
153
|
-
return null; // unsupported body type
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
OpenAICache.MarkResponseName = "X_FROM_OPENAI_CACHE";
|
|
157
|
-
exports.default = OpenAICache;
|