@ai-sdk-tool/middleware 0.0.1 → 0.0.2
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/{chunk-XJIXXAOA.js → chunk-ET4WAX7V.js} +7 -3
- package/dist/chunk-ET4WAX7V.js.map +1 -0
- package/dist/disk-cache.cjs +6 -2
- package/dist/disk-cache.cjs.map +1 -1
- package/dist/disk-cache.js +1 -1
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-XJIXXAOA.js.map +0 -1
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { dirname, join, resolve } from "path";
|
|
12
12
|
function defaultGenerateKey(modelId, params) {
|
|
13
13
|
const serialized = JSON.stringify(
|
|
14
|
-
{ version: "0.0.
|
|
14
|
+
{ version: "0.0.2", modelId, params },
|
|
15
15
|
(_key, value) => {
|
|
16
16
|
if (typeof value === "function") {
|
|
17
17
|
return "[function]";
|
|
@@ -80,10 +80,14 @@ function createDiskCacheMiddleware(options = {}) {
|
|
|
80
80
|
const forceRefresh = envForceRefresh !== void 0 ? envForceRefresh.toLowerCase() === "true" || envForceRefresh === "1" : (_e = options.forceRefresh) != null ? _e : false;
|
|
81
81
|
const log = debug ? (msg, data) => console.log(`[ai-cache] ${msg}`, data != null ? data : "") : () => void 0;
|
|
82
82
|
if (!enabled) {
|
|
83
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
specificationVersion: "v3",
|
|
85
|
+
transformParams: async ({ params }) => params
|
|
86
|
+
};
|
|
84
87
|
}
|
|
85
88
|
return {
|
|
86
89
|
specificationVersion: "v3",
|
|
90
|
+
transformParams: async ({ params }) => params,
|
|
87
91
|
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
88
92
|
const cacheKey = generateKey(model.modelId, params);
|
|
89
93
|
const cachePath = getCachePath(resolvedCacheDir, cacheKey);
|
|
@@ -220,4 +224,4 @@ export {
|
|
|
220
224
|
clearDiskCache,
|
|
221
225
|
getCacheStats
|
|
222
226
|
};
|
|
223
|
-
//# sourceMappingURL=chunk-
|
|
227
|
+
//# sourceMappingURL=chunk-ET4WAX7V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/disk-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return {\n specificationVersion: \"v3\",\n transformParams: async ({ params }) => params,\n };\n }\n\n return {\n specificationVersion: \"v3\",\n\n transformParams: async ({ params }) => params,\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n"],"mappings":"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","names":[]}
|
package/dist/disk-cache.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var import_promises = require("fs/promises");
|
|
|
30
30
|
var import_node_path = require("path");
|
|
31
31
|
function defaultGenerateKey(modelId, params) {
|
|
32
32
|
const serialized = JSON.stringify(
|
|
33
|
-
{ version: "0.0.
|
|
33
|
+
{ version: "0.0.2", modelId, params },
|
|
34
34
|
(_key, value) => {
|
|
35
35
|
if (typeof value === "function") {
|
|
36
36
|
return "[function]";
|
|
@@ -99,10 +99,14 @@ function createDiskCacheMiddleware(options = {}) {
|
|
|
99
99
|
const forceRefresh = envForceRefresh !== void 0 ? envForceRefresh.toLowerCase() === "true" || envForceRefresh === "1" : (_e = options.forceRefresh) != null ? _e : false;
|
|
100
100
|
const log = debug ? (msg, data) => console.log(`[ai-cache] ${msg}`, data != null ? data : "") : () => void 0;
|
|
101
101
|
if (!enabled) {
|
|
102
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
specificationVersion: "v3",
|
|
104
|
+
transformParams: async ({ params }) => params
|
|
105
|
+
};
|
|
103
106
|
}
|
|
104
107
|
return {
|
|
105
108
|
specificationVersion: "v3",
|
|
109
|
+
transformParams: async ({ params }) => params,
|
|
106
110
|
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
107
111
|
const cacheKey = generateKey(model.modelId, params);
|
|
108
112
|
const cachePath = getCachePath(resolvedCacheDir, cacheKey);
|
package/dist/disk-cache.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/disk-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return { specificationVersion: \"v3\" };\n }\n\n return {\n specificationVersion: \"v3\",\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/disk-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return {\n specificationVersion: \"v3\",\n transformParams: async ({ params }) => params,\n };\n }\n\n return {\n specificationVersion: \"v3\",\n\n transformParams: async ({ params }) => params,\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n"],"mappings":"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","names":[]}
|
package/dist/disk-cache.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -135,7 +135,7 @@ var import_promises = require("fs/promises");
|
|
|
135
135
|
var import_node_path = require("path");
|
|
136
136
|
function defaultGenerateKey(modelId, params) {
|
|
137
137
|
const serialized = JSON.stringify(
|
|
138
|
-
{ version: "0.0.
|
|
138
|
+
{ version: "0.0.2", modelId, params },
|
|
139
139
|
(_key, value) => {
|
|
140
140
|
if (typeof value === "function") {
|
|
141
141
|
return "[function]";
|
|
@@ -204,10 +204,14 @@ function createDiskCacheMiddleware(options = {}) {
|
|
|
204
204
|
const forceRefresh = envForceRefresh !== void 0 ? envForceRefresh.toLowerCase() === "true" || envForceRefresh === "1" : (_e = options.forceRefresh) != null ? _e : false;
|
|
205
205
|
const log = debug ? (msg, data) => console.log(`[ai-cache] ${msg}`, data != null ? data : "") : () => void 0;
|
|
206
206
|
if (!enabled) {
|
|
207
|
-
return {
|
|
207
|
+
return {
|
|
208
|
+
specificationVersion: "v3",
|
|
209
|
+
transformParams: async ({ params }) => params
|
|
210
|
+
};
|
|
208
211
|
}
|
|
209
212
|
return {
|
|
210
213
|
specificationVersion: "v3",
|
|
214
|
+
transformParams: async ({ params }) => params,
|
|
211
215
|
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
212
216
|
const cacheKey = generateKey(model.modelId, params);
|
|
213
217
|
const cachePath = getCachePath(resolvedCacheDir, cacheKey);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/default-system-prompt.ts","../src/disk-cache.ts","../src/reasoning-parser.ts"],"sourcesContent":["// biome-ignore lint/performance/noBarrelFile: Package entrypoint - must re-export for public API\nexport * from \"./default-system-prompt\";\nexport * from \"./disk-cache\";\nexport * from \"./reasoning-parser\";\n","import type {\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3Middleware,\n LanguageModelV3Prompt,\n} from \"@ai-sdk/provider\";\n\ntype SystemPromptPlacement = \"first\" | \"last\";\n\ninterface DefaultSystemPromptMiddlewareOptions {\n systemPrompt: string;\n placement?: SystemPromptPlacement;\n}\n\nfunction extractSystemText(content: unknown): string | undefined {\n if (typeof content === \"string\") {\n return content;\n }\n\n if (!Array.isArray(content)) {\n if (content == null) {\n return;\n }\n return String(content);\n }\n\n const parts = (content as LanguageModelV3Content[]).map((part) => {\n if (part?.type === \"text\" && \"text\" in part) {\n return String(part.text ?? \"\");\n }\n\n return JSON.stringify(part);\n });\n\n const textParts = parts.filter((value) => value.length > 0);\n if (textParts.length === 0) {\n return;\n }\n\n return textParts.join(\"\\n\");\n}\n\nfunction mergeSystemPrompts({\n base,\n addition,\n placement,\n}: {\n base?: string;\n addition: string;\n placement: SystemPromptPlacement;\n}): string {\n if (!base) {\n return addition;\n }\n\n if (addition.length === 0) {\n return base;\n }\n\n return placement === \"first\"\n ? `${addition}\\n\\n${base}`\n : `${base}\\n\\n${addition}`;\n}\n\nfunction ensurePromptArray(\n prompt?: LanguageModelV3Prompt\n): LanguageModelV3Prompt {\n if (!prompt) {\n return [];\n }\n\n return [...prompt];\n}\n\nexport function defaultSystemPromptMiddleware({\n systemPrompt,\n placement = \"first\",\n}: DefaultSystemPromptMiddlewareOptions): LanguageModelV3Middleware {\n return {\n specificationVersion: \"v3\",\n transformParams: ({ params }) => {\n const prompt = ensurePromptArray(params.prompt);\n const systemIndex = prompt.findIndex(\n (message) => message.role === \"system\"\n );\n\n if (systemIndex === -1) {\n const promptWithSystem =\n placement === \"first\"\n ? ([\n {\n role: \"system\" as const,\n content: systemPrompt,\n },\n ...prompt,\n ] as LanguageModelV3Prompt)\n : ([\n ...prompt,\n {\n role: \"system\" as const,\n content: systemPrompt,\n },\n ] as LanguageModelV3Prompt);\n\n const nextParams: LanguageModelV3CallOptions = {\n ...params,\n prompt: promptWithSystem,\n };\n\n return Promise.resolve<LanguageModelV3CallOptions>(nextParams);\n }\n\n const systemMessage = prompt[systemIndex];\n const baseText = extractSystemText(systemMessage.content);\n const mergedContent = mergeSystemPrompts({\n base: baseText,\n addition: systemPrompt,\n placement,\n });\n\n const updatedPrompt = prompt.map((message, index) =>\n index === systemIndex\n ? {\n ...message,\n content: mergedContent,\n }\n : message\n ) as LanguageModelV3Prompt;\n\n const nextParams: LanguageModelV3CallOptions = {\n ...params,\n prompt: updatedPrompt,\n };\n\n return Promise.resolve<LanguageModelV3CallOptions>(nextParams);\n },\n };\n}\n","import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return { specificationVersion: \"v3\" };\n }\n\n return {\n specificationVersion: \"v3\",\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n","/**\n * @license\n * Copyright (c) 2021-present, FriendliAI Inc. All rights reserved.\n */\n\nimport type {\n LanguageModelV3Content,\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\n/**\n * All code below is forked from the following link:\n * https://github.com/vercel/ai/blob/v5/packages/ai/core/middleware/extract-reasoning-middleware.ts\n */\n\n/**\n * Returns the index of the start of the searchedText in the text, or null if it\n * is not found.\n */\nexport function getPotentialStartIndex(\n text: string,\n searchedText: string\n): number | null {\n // Return null immediately if searchedText is empty.\n if (searchedText.length === 0) {\n return null;\n }\n\n // Check if the searchedText exists as a direct substring of text.\n const directIndex = text.indexOf(searchedText);\n if (directIndex !== -1) {\n return directIndex;\n }\n\n // Otherwise, look for the largest suffix of \"text\" that matches\n // a prefix of \"searchedText\". We go from the end of text inward.\n for (let i = text.length - 1; i >= 0; i -= 1) {\n const suffix = text.substring(i);\n if (searchedText.startsWith(suffix)) {\n return i;\n }\n }\n\n return null;\n}\n\n/**\n * Extract an XML-tagged reasoning section from the generated text and exposes it\n * as a `reasoning` property on the result.\n *\n * @param openingTag - The opening XML tag to extract reasoning from.\n * @param closingTag - The closing XML tag to extract reasoning from.\n * @param separator - The separator to use between reasoning and text sections.\n * @param startWithReasoning - Whether to start with reasoning tokens.\n */\nexport function extractReasoningMiddleware({\n openingTag,\n closingTag,\n separator = \"\\n\",\n startWithReasoning = false,\n}: {\n openingTag: string;\n closingTag: string;\n separator?: string;\n startWithReasoning?: boolean;\n}): LanguageModelV3Middleware {\n function processTextPart(\n text: string,\n transformedContent: LanguageModelV3Content[]\n ) {\n const regexp = new RegExp(`${openingTag}(.*?)${closingTag}`, \"gs\");\n const matches = Array.from(text.matchAll(regexp));\n\n if (!matches.length) {\n return;\n }\n\n const reasoningText = matches.map((match) => match[1]).join(separator);\n\n let textWithoutReasoning = text;\n for (let i = matches.length - 1; i >= 0; i -= 1) {\n const match = matches[i];\n\n const beforeMatch = textWithoutReasoning.slice(0, match.index);\n const matchIndex = match.index ?? 0;\n const afterMatch = textWithoutReasoning.slice(\n matchIndex + match[0].length\n );\n\n textWithoutReasoning =\n beforeMatch +\n (beforeMatch.length > 0 && afterMatch.length > 0 ? separator : \"\") +\n afterMatch;\n }\n\n transformedContent.push({\n type: \"reasoning\",\n text: reasoningText,\n });\n\n transformedContent.push({\n type: \"text\",\n text: textWithoutReasoning,\n });\n }\n\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate }) => {\n const { content, ...rest } = await doGenerate();\n\n const transformedContent: LanguageModelV3Content[] = [];\n for (const part of content) {\n if (part.type !== \"text\") {\n transformedContent.push(part);\n continue;\n }\n\n const text = startWithReasoning ? openingTag + part.text : part.text;\n const regexp = new RegExp(`${openingTag}(.*?)${closingTag}`, \"gs\");\n const matches = Array.from(text.matchAll(regexp));\n\n if (!matches.length) {\n transformedContent.push(part);\n continue;\n }\n\n processTextPart(text, transformedContent);\n }\n\n return { content: transformedContent, ...rest };\n },\n\n wrapStream: async ({ doStream }) => {\n const { stream, ...rest } = await doStream();\n\n interface ExtractionState {\n isFirstReasoning: boolean;\n isFirstText: boolean;\n afterSwitch: boolean;\n isReasoning: boolean;\n buffer: string;\n idCounter: number;\n textId: string;\n }\n\n const reasoningExtractions: Record<string, ExtractionState> = {};\n\n function createPublisher(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n return (text: string) => {\n if (text.length === 0) {\n return;\n }\n\n const prefix = getPrefix(activeExtraction);\n enqueueReasoningStart(activeExtraction, controller);\n enqueueDelta(activeExtraction, controller, prefix, text);\n updateExtractionState(activeExtraction);\n };\n }\n\n function getPrefix(activeExtraction: ExtractionState): string {\n return activeExtraction.afterSwitch &&\n (activeExtraction.isReasoning\n ? !activeExtraction.isFirstReasoning\n : !activeExtraction.isFirstText)\n ? separator\n : \"\";\n }\n\n function enqueueReasoningStart(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n if (\n (activeExtraction.afterSwitch && activeExtraction.isReasoning) ||\n activeExtraction.isFirstReasoning\n ) {\n controller.enqueue({\n type: \"reasoning-start\",\n id: `reasoning-${activeExtraction.idCounter}`,\n });\n }\n }\n\n function enqueueDelta(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,\n prefix: string,\n text: string\n ) {\n controller.enqueue(\n activeExtraction.isReasoning\n ? {\n type: \"reasoning-delta\",\n delta: prefix + text,\n id: `reasoning-${activeExtraction.idCounter}`,\n }\n : {\n type: \"text-delta\",\n delta: prefix + text,\n id: activeExtraction.textId,\n }\n );\n }\n\n function updateExtractionState(activeExtraction: ExtractionState) {\n activeExtraction.afterSwitch = false;\n if (activeExtraction.isReasoning) {\n activeExtraction.isFirstReasoning = false;\n } else {\n activeExtraction.isFirstText = false;\n }\n }\n\n function handleFullMatch(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,\n startIndex: number,\n nextTag: string\n ) {\n activeExtraction.buffer = activeExtraction.buffer.slice(\n startIndex + nextTag.length\n );\n\n if (activeExtraction.isReasoning) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: `reasoning-${activeExtraction.idCounter}`,\n });\n activeExtraction.idCounter += 1;\n }\n\n activeExtraction.isReasoning = !activeExtraction.isReasoning;\n activeExtraction.afterSwitch = true;\n }\n\n function processTagMatch({\n activeExtraction,\n controller,\n publish,\n startIndex,\n nextTag,\n }: {\n activeExtraction: ExtractionState;\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>;\n publish: (text: string) => void;\n startIndex: number;\n nextTag: string;\n }): boolean {\n publish(activeExtraction.buffer.slice(0, startIndex));\n\n const foundFullMatch =\n startIndex + nextTag.length <= activeExtraction.buffer.length;\n\n if (foundFullMatch) {\n handleFullMatch(activeExtraction, controller, startIndex, nextTag);\n return true;\n }\n\n activeExtraction.buffer = activeExtraction.buffer.slice(startIndex);\n return false;\n }\n\n function processBuffer(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n const publish = createPublisher(activeExtraction, controller);\n let continueProcessing = true;\n\n while (continueProcessing) {\n const nextTag = activeExtraction.isReasoning\n ? closingTag\n : openingTag;\n const startIndex = getPotentialStartIndex(\n activeExtraction.buffer,\n nextTag\n );\n\n if (startIndex == null) {\n publish(activeExtraction.buffer);\n activeExtraction.buffer = \"\";\n break;\n }\n\n continueProcessing = processTagMatch({\n activeExtraction,\n controller,\n publish,\n startIndex,\n nextTag,\n });\n }\n }\n\n return {\n stream: stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform: (chunk, controller) => {\n if (chunk.type !== \"text-delta\") {\n controller.enqueue(chunk);\n return;\n }\n\n if (reasoningExtractions[chunk.id] == null) {\n reasoningExtractions[chunk.id] = {\n isFirstReasoning: true,\n isFirstText: true,\n afterSwitch: false,\n isReasoning: startWithReasoning,\n buffer: \"\",\n idCounter: 0,\n textId: chunk.id,\n };\n }\n\n const activeExtraction = reasoningExtractions[chunk.id];\n activeExtraction.buffer += chunk.delta;\n processBuffer(activeExtraction, controller);\n },\n })\n ),\n ...rest,\n };\n },\n };\n}\n"],"mappings":"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","names":["nextParams"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/default-system-prompt.ts","../src/disk-cache.ts","../src/reasoning-parser.ts"],"sourcesContent":["// biome-ignore lint/performance/noBarrelFile: Package entrypoint - must re-export for public API\nexport * from \"./default-system-prompt\";\nexport * from \"./disk-cache\";\nexport * from \"./reasoning-parser\";\n","import type {\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3Middleware,\n LanguageModelV3Prompt,\n} from \"@ai-sdk/provider\";\n\ntype SystemPromptPlacement = \"first\" | \"last\";\n\ninterface DefaultSystemPromptMiddlewareOptions {\n systemPrompt: string;\n placement?: SystemPromptPlacement;\n}\n\nfunction extractSystemText(content: unknown): string | undefined {\n if (typeof content === \"string\") {\n return content;\n }\n\n if (!Array.isArray(content)) {\n if (content == null) {\n return;\n }\n return String(content);\n }\n\n const parts = (content as LanguageModelV3Content[]).map((part) => {\n if (part?.type === \"text\" && \"text\" in part) {\n return String(part.text ?? \"\");\n }\n\n return JSON.stringify(part);\n });\n\n const textParts = parts.filter((value) => value.length > 0);\n if (textParts.length === 0) {\n return;\n }\n\n return textParts.join(\"\\n\");\n}\n\nfunction mergeSystemPrompts({\n base,\n addition,\n placement,\n}: {\n base?: string;\n addition: string;\n placement: SystemPromptPlacement;\n}): string {\n if (!base) {\n return addition;\n }\n\n if (addition.length === 0) {\n return base;\n }\n\n return placement === \"first\"\n ? `${addition}\\n\\n${base}`\n : `${base}\\n\\n${addition}`;\n}\n\nfunction ensurePromptArray(\n prompt?: LanguageModelV3Prompt\n): LanguageModelV3Prompt {\n if (!prompt) {\n return [];\n }\n\n return [...prompt];\n}\n\nexport function defaultSystemPromptMiddleware({\n systemPrompt,\n placement = \"first\",\n}: DefaultSystemPromptMiddlewareOptions): LanguageModelV3Middleware {\n return {\n specificationVersion: \"v3\",\n transformParams: ({ params }) => {\n const prompt = ensurePromptArray(params.prompt);\n const systemIndex = prompt.findIndex(\n (message) => message.role === \"system\"\n );\n\n if (systemIndex === -1) {\n const promptWithSystem =\n placement === \"first\"\n ? ([\n {\n role: \"system\" as const,\n content: systemPrompt,\n },\n ...prompt,\n ] as LanguageModelV3Prompt)\n : ([\n ...prompt,\n {\n role: \"system\" as const,\n content: systemPrompt,\n },\n ] as LanguageModelV3Prompt);\n\n const nextParams: LanguageModelV3CallOptions = {\n ...params,\n prompt: promptWithSystem,\n };\n\n return Promise.resolve<LanguageModelV3CallOptions>(nextParams);\n }\n\n const systemMessage = prompt[systemIndex];\n const baseText = extractSystemText(systemMessage.content);\n const mergedContent = mergeSystemPrompts({\n base: baseText,\n addition: systemPrompt,\n placement,\n });\n\n const updatedPrompt = prompt.map((message, index) =>\n index === systemIndex\n ? {\n ...message,\n content: mergedContent,\n }\n : message\n ) as LanguageModelV3Prompt;\n\n const nextParams: LanguageModelV3CallOptions = {\n ...params,\n prompt: updatedPrompt,\n };\n\n return Promise.resolve<LanguageModelV3CallOptions>(nextParams);\n },\n };\n}\n","import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return {\n specificationVersion: \"v3\",\n transformParams: async ({ params }) => params,\n };\n }\n\n return {\n specificationVersion: \"v3\",\n\n transformParams: async ({ params }) => params,\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n","/**\n * @license\n * Copyright (c) 2021-present, FriendliAI Inc. All rights reserved.\n */\n\nimport type {\n LanguageModelV3Content,\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\n/**\n * All code below is forked from the following link:\n * https://github.com/vercel/ai/blob/v5/packages/ai/core/middleware/extract-reasoning-middleware.ts\n */\n\n/**\n * Returns the index of the start of the searchedText in the text, or null if it\n * is not found.\n */\nexport function getPotentialStartIndex(\n text: string,\n searchedText: string\n): number | null {\n // Return null immediately if searchedText is empty.\n if (searchedText.length === 0) {\n return null;\n }\n\n // Check if the searchedText exists as a direct substring of text.\n const directIndex = text.indexOf(searchedText);\n if (directIndex !== -1) {\n return directIndex;\n }\n\n // Otherwise, look for the largest suffix of \"text\" that matches\n // a prefix of \"searchedText\". We go from the end of text inward.\n for (let i = text.length - 1; i >= 0; i -= 1) {\n const suffix = text.substring(i);\n if (searchedText.startsWith(suffix)) {\n return i;\n }\n }\n\n return null;\n}\n\n/**\n * Extract an XML-tagged reasoning section from the generated text and exposes it\n * as a `reasoning` property on the result.\n *\n * @param openingTag - The opening XML tag to extract reasoning from.\n * @param closingTag - The closing XML tag to extract reasoning from.\n * @param separator - The separator to use between reasoning and text sections.\n * @param startWithReasoning - Whether to start with reasoning tokens.\n */\nexport function extractReasoningMiddleware({\n openingTag,\n closingTag,\n separator = \"\\n\",\n startWithReasoning = false,\n}: {\n openingTag: string;\n closingTag: string;\n separator?: string;\n startWithReasoning?: boolean;\n}): LanguageModelV3Middleware {\n function processTextPart(\n text: string,\n transformedContent: LanguageModelV3Content[]\n ) {\n const regexp = new RegExp(`${openingTag}(.*?)${closingTag}`, \"gs\");\n const matches = Array.from(text.matchAll(regexp));\n\n if (!matches.length) {\n return;\n }\n\n const reasoningText = matches.map((match) => match[1]).join(separator);\n\n let textWithoutReasoning = text;\n for (let i = matches.length - 1; i >= 0; i -= 1) {\n const match = matches[i];\n\n const beforeMatch = textWithoutReasoning.slice(0, match.index);\n const matchIndex = match.index ?? 0;\n const afterMatch = textWithoutReasoning.slice(\n matchIndex + match[0].length\n );\n\n textWithoutReasoning =\n beforeMatch +\n (beforeMatch.length > 0 && afterMatch.length > 0 ? separator : \"\") +\n afterMatch;\n }\n\n transformedContent.push({\n type: \"reasoning\",\n text: reasoningText,\n });\n\n transformedContent.push({\n type: \"text\",\n text: textWithoutReasoning,\n });\n }\n\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate }) => {\n const { content, ...rest } = await doGenerate();\n\n const transformedContent: LanguageModelV3Content[] = [];\n for (const part of content) {\n if (part.type !== \"text\") {\n transformedContent.push(part);\n continue;\n }\n\n const text = startWithReasoning ? openingTag + part.text : part.text;\n const regexp = new RegExp(`${openingTag}(.*?)${closingTag}`, \"gs\");\n const matches = Array.from(text.matchAll(regexp));\n\n if (!matches.length) {\n transformedContent.push(part);\n continue;\n }\n\n processTextPart(text, transformedContent);\n }\n\n return { content: transformedContent, ...rest };\n },\n\n wrapStream: async ({ doStream }) => {\n const { stream, ...rest } = await doStream();\n\n interface ExtractionState {\n isFirstReasoning: boolean;\n isFirstText: boolean;\n afterSwitch: boolean;\n isReasoning: boolean;\n buffer: string;\n idCounter: number;\n textId: string;\n }\n\n const reasoningExtractions: Record<string, ExtractionState> = {};\n\n function createPublisher(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n return (text: string) => {\n if (text.length === 0) {\n return;\n }\n\n const prefix = getPrefix(activeExtraction);\n enqueueReasoningStart(activeExtraction, controller);\n enqueueDelta(activeExtraction, controller, prefix, text);\n updateExtractionState(activeExtraction);\n };\n }\n\n function getPrefix(activeExtraction: ExtractionState): string {\n return activeExtraction.afterSwitch &&\n (activeExtraction.isReasoning\n ? !activeExtraction.isFirstReasoning\n : !activeExtraction.isFirstText)\n ? separator\n : \"\";\n }\n\n function enqueueReasoningStart(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n if (\n (activeExtraction.afterSwitch && activeExtraction.isReasoning) ||\n activeExtraction.isFirstReasoning\n ) {\n controller.enqueue({\n type: \"reasoning-start\",\n id: `reasoning-${activeExtraction.idCounter}`,\n });\n }\n }\n\n function enqueueDelta(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,\n prefix: string,\n text: string\n ) {\n controller.enqueue(\n activeExtraction.isReasoning\n ? {\n type: \"reasoning-delta\",\n delta: prefix + text,\n id: `reasoning-${activeExtraction.idCounter}`,\n }\n : {\n type: \"text-delta\",\n delta: prefix + text,\n id: activeExtraction.textId,\n }\n );\n }\n\n function updateExtractionState(activeExtraction: ExtractionState) {\n activeExtraction.afterSwitch = false;\n if (activeExtraction.isReasoning) {\n activeExtraction.isFirstReasoning = false;\n } else {\n activeExtraction.isFirstText = false;\n }\n }\n\n function handleFullMatch(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,\n startIndex: number,\n nextTag: string\n ) {\n activeExtraction.buffer = activeExtraction.buffer.slice(\n startIndex + nextTag.length\n );\n\n if (activeExtraction.isReasoning) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: `reasoning-${activeExtraction.idCounter}`,\n });\n activeExtraction.idCounter += 1;\n }\n\n activeExtraction.isReasoning = !activeExtraction.isReasoning;\n activeExtraction.afterSwitch = true;\n }\n\n function processTagMatch({\n activeExtraction,\n controller,\n publish,\n startIndex,\n nextTag,\n }: {\n activeExtraction: ExtractionState;\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>;\n publish: (text: string) => void;\n startIndex: number;\n nextTag: string;\n }): boolean {\n publish(activeExtraction.buffer.slice(0, startIndex));\n\n const foundFullMatch =\n startIndex + nextTag.length <= activeExtraction.buffer.length;\n\n if (foundFullMatch) {\n handleFullMatch(activeExtraction, controller, startIndex, nextTag);\n return true;\n }\n\n activeExtraction.buffer = activeExtraction.buffer.slice(startIndex);\n return false;\n }\n\n function processBuffer(\n activeExtraction: ExtractionState,\n controller: TransformStreamDefaultController<LanguageModelV3StreamPart>\n ) {\n const publish = createPublisher(activeExtraction, controller);\n let continueProcessing = true;\n\n while (continueProcessing) {\n const nextTag = activeExtraction.isReasoning\n ? closingTag\n : openingTag;\n const startIndex = getPotentialStartIndex(\n activeExtraction.buffer,\n nextTag\n );\n\n if (startIndex == null) {\n publish(activeExtraction.buffer);\n activeExtraction.buffer = \"\";\n break;\n }\n\n continueProcessing = processTagMatch({\n activeExtraction,\n controller,\n publish,\n startIndex,\n nextTag,\n });\n }\n }\n\n return {\n stream: stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform: (chunk, controller) => {\n if (chunk.type !== \"text-delta\") {\n controller.enqueue(chunk);\n return;\n }\n\n if (reasoningExtractions[chunk.id] == null) {\n reasoningExtractions[chunk.id] = {\n isFirstReasoning: true,\n isFirstText: true,\n afterSwitch: false,\n isReasoning: startWithReasoning,\n buffer: \"\",\n idCounter: 0,\n textId: chunk.id,\n };\n }\n\n const activeExtraction = reasoningExtractions[chunk.id];\n activeExtraction.buffer += chunk.delta;\n processBuffer(activeExtraction, controller);\n },\n })\n ),\n ...rest,\n };\n },\n };\n}\n"],"mappings":"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","names":["nextParams"]}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/disk-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n mkdir,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type {\n LanguageModelV3Middleware,\n LanguageModelV3StreamPart,\n} from \"@ai-sdk/provider\";\n\ndeclare const __PACKAGE_VERSION__: string;\n\nexport interface DiskCacheMiddlewareOptions {\n cacheDir?: string;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n debug?: boolean;\n}\n\ninterface CachedGenerateResult {\n type: \"generate\";\n content: unknown;\n finishReason: unknown;\n usage: unknown;\n warnings: unknown;\n response: unknown;\n providerMetadata: unknown;\n request: unknown;\n}\n\ninterface CachedStreamResult {\n type: \"stream\";\n parts: LanguageModelV3StreamPart[];\n response: unknown;\n request: unknown;\n}\n\ntype CachedResult = CachedGenerateResult | CachedStreamResult;\n\nfunction defaultGenerateKey(modelId: string, params: unknown): string {\n const serialized = JSON.stringify(\n { version: __PACKAGE_VERSION__, modelId, params },\n (_key, value) => {\n if (typeof value === \"function\") {\n return \"[function]\";\n }\n if (value instanceof RegExp) {\n return value.toString();\n }\n return value;\n }\n );\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\nfunction getCachePath(cacheDir: string, key: string): string {\n return join(cacheDir, key.slice(0, 2), `${key}.json`);\n}\n\nasync function readCache(cachePath: string): Promise<CachedResult | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CachedResult;\n if (parsed.response && typeof parsed.response === \"object\") {\n const resp = parsed.response as Record<string, unknown>;\n if (typeof resp.timestamp === \"string\") {\n resp.timestamp = new Date(resp.timestamp);\n }\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nasync function writeCache(\n cachePath: string,\n result: CachedResult\n): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true });\n await writeFile(cachePath, JSON.stringify(result), \"utf-8\");\n } catch {\n // Silent fail\n }\n}\n\nfunction createStreamFromParts(\n parts: LanguageModelV3StreamPart[]\n): ReadableStream<LanguageModelV3StreamPart> {\n let index = 0;\n return new ReadableStream({\n pull(controller) {\n if (index < parts.length) {\n controller.enqueue(parts[index++]);\n } else {\n controller.close();\n }\n },\n });\n}\n\ntype FinishReasonLike = { unified?: string } | string | null | undefined;\n\nfunction isErrorFinishReason(finishReason: FinishReasonLike): boolean {\n if (!finishReason) {\n return false;\n }\n const unified =\n typeof finishReason === \"string\" ? finishReason : finishReason.unified;\n return unified === \"error\" || unified === \"other\";\n}\n\nexport function createDiskCacheMiddleware(\n options: DiskCacheMiddlewareOptions = {}\n): LanguageModelV3Middleware {\n const generateKey = options.generateKey ?? defaultGenerateKey;\n const resolvedCacheDir = resolve(options.cacheDir ?? \".ai-cache\");\n\n const envEnabled = process.env.AI_CACHE_ENABLED;\n const enabled =\n envEnabled !== undefined\n ? envEnabled.toLowerCase() === \"true\" || envEnabled === \"1\"\n : (options.enabled ?? true);\n\n const envDebug = process.env.AI_CACHE_DEBUG;\n const debug =\n envDebug !== undefined\n ? envDebug.toLowerCase() === \"true\" || envDebug === \"1\"\n : (options.debug ?? false);\n\n const envForceRefresh = process.env.AI_CACHE_FORCE_REFRESH;\n const forceRefresh =\n envForceRefresh !== undefined\n ? envForceRefresh.toLowerCase() === \"true\" || envForceRefresh === \"1\"\n : (options.forceRefresh ?? false);\n\n const log = debug\n ? (msg: string, data?: unknown) =>\n console.log(`[ai-cache] ${msg}`, data ?? \"\")\n : () => undefined;\n\n if (!enabled) {\n return { specificationVersion: \"v3\" };\n }\n\n return {\n specificationVersion: \"v3\",\n\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"generate\") {\n log(\"HIT generate\", cacheKey.slice(0, 8));\n return {\n content: cached.content,\n finishReason: cached.finishReason,\n usage: cached.usage,\n warnings: cached.warnings,\n response: cached.response,\n providerMetadata: cached.providerMetadata,\n request: cached.request,\n } as Awaited<ReturnType<typeof doGenerate>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH generate\" : \"MISS generate\",\n cacheKey.slice(0, 8)\n );\n const result = await doGenerate();\n\n if (isErrorFinishReason(result.finishReason)) {\n log(\"SKIP cache (error response)\", result.finishReason);\n } else {\n await writeCache(cachePath, {\n type: \"generate\",\n content: result.content,\n finishReason: result.finishReason,\n usage: result.usage,\n warnings: result.warnings,\n response: result.response,\n providerMetadata: result.providerMetadata,\n request: result.request,\n });\n }\n\n return result;\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const cacheKey = generateKey(model.modelId, params);\n const cachePath = getCachePath(resolvedCacheDir, cacheKey);\n\n if (!forceRefresh) {\n const cached = await readCache(cachePath);\n if (cached?.type === \"stream\") {\n log(\"HIT stream\", {\n key: cacheKey.slice(0, 8),\n parts: cached.parts.length,\n });\n return {\n stream: createStreamFromParts(cached.parts),\n response: cached.response,\n request: cached.request,\n } as Awaited<ReturnType<typeof doStream>>;\n }\n }\n\n log(\n forceRefresh ? \"REFRESH stream\" : \"MISS stream\",\n cacheKey.slice(0, 8)\n );\n const result = await doStream();\n\n const collectedParts: LanguageModelV3StreamPart[] = [];\n\n const cachedStream = result.stream.pipeThrough(\n new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n collectedParts.push(chunk);\n controller.enqueue(chunk);\n },\n flush() {\n const finishPart = collectedParts.find((p) => p.type === \"finish\");\n if (finishPart && isErrorFinishReason(finishPart.finishReason)) {\n return;\n }\n\n writeCache(cachePath, {\n type: \"stream\",\n parts: collectedParts,\n response: result.response,\n request: result.request,\n });\n },\n })\n );\n\n return { ...result, stream: cachedStream };\n },\n };\n}\n\nexport async function clearDiskCache(cacheDir = \".ai-cache\"): Promise<void> {\n try {\n await rm(resolve(cacheDir), { recursive: true, force: true });\n } catch {\n // Directory doesn't exist\n }\n}\n\nexport async function getCacheStats(cacheDir = \".ai-cache\"): Promise<{\n totalFiles: number;\n totalSizeBytes: number;\n generateCount: number;\n streamCount: number;\n}> {\n const resolvedDir = resolve(cacheDir);\n let totalFiles = 0;\n let totalSizeBytes = 0;\n let generateCount = 0;\n let streamCount = 0;\n\n async function walkDir(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(fullPath);\n } else if (entry.name.endsWith(\".json\")) {\n totalFiles++;\n const fileStat = await stat(fullPath);\n totalSizeBytes += fileStat.size;\n\n try {\n const content = JSON.parse(\n await readFile(fullPath, \"utf-8\")\n ) as CachedResult;\n if (content.type === \"generate\") {\n generateCount++;\n } else if (content.type === \"stream\") {\n streamCount++;\n }\n } catch {\n // Skip malformed\n }\n }\n })\n );\n } catch {\n // Directory doesn't exist\n }\n }\n\n await walkDir(resolvedDir);\n return { totalFiles, totalSizeBytes, generateCount, streamCount };\n}\n"],"mappings":"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","names":[]}
|