@ai-sdk-tool/middleware 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # AI SDK Middleware
2
+
3
+ [![npm](https://img.shields.io/npm/v/@ai-sdk-tool/middleware)](https://www.npmjs.com/package/@ai-sdk-tool/middleware)
4
+ [![npm](https://img.shields.io/npm/dt/@ai-sdk-tool/middleware)](https://www.npmjs.com/package/@ai-sdk-tool/middleware)
5
+
6
+ Reusable middleware utilities for the Vercel AI SDK.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pnpm add @ai-sdk-tool/middleware
12
+ ```
13
+
14
+ ## Exports
15
+
16
+ - `@ai-sdk-tool/middleware`
17
+ - `@ai-sdk-tool/middleware/disk-cache`
18
+ - `@ai-sdk-tool/middleware/reasoning-parser`
19
+
20
+ ## Included middleware
21
+
22
+ - `createDiskCacheMiddleware`: Disk-based response cache middleware for `generate` and `stream`
23
+ - `defaultSystemPromptMiddleware`: Inject or merge system prompts into model input
24
+ - `extractReasoningMiddleware`: Extract XML-tagged reasoning into `reasoning` stream/content parts
25
+
26
+ ## Development
27
+
28
+ ```bash
29
+ pnpm install
30
+ pnpm run typecheck
31
+ pnpm run test
32
+ pnpm run build
33
+ ```
@@ -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.2", modelId, params },
14
+ { version: "0.0.3", modelId, params },
15
15
  (_key, value) => {
16
16
  if (typeof value === "function") {
17
17
  return "[function]";
@@ -224,4 +224,4 @@ export {
224
224
  clearDiskCache,
225
225
  getCacheStats
226
226
  };
227
- //# sourceMappingURL=chunk-ET4WAX7V.js.map
227
+ //# sourceMappingURL=chunk-F3QOGT7X.js.map
@@ -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 {\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":[]}
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 debug?: boolean;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n}\n\ninterface CachedGenerateResult {\n content: unknown;\n finishReason: unknown;\n providerMetadata: unknown;\n request: unknown;\n response: unknown;\n type: \"generate\";\n usage: unknown;\n warnings: unknown;\n}\n\ninterface CachedStreamResult {\n parts: LanguageModelV3StreamPart[];\n request: unknown;\n response: unknown;\n type: \"stream\";\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":[]}
@@ -207,4 +207,4 @@ export {
207
207
  * @license
208
208
  * Copyright (c) 2021-present, FriendliAI Inc. All rights reserved.
209
209
  */
210
- //# sourceMappingURL=chunk-R4PZN7IW.js.map
210
+ //# sourceMappingURL=chunk-MLWJBQIP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reasoning-parser.ts"],"sourcesContent":["/**\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":[]}
1
+ {"version":3,"sources":["../src/reasoning-parser.ts"],"sourcesContent":["/**\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 afterSwitch: boolean;\n buffer: string;\n idCounter: number;\n isFirstReasoning: boolean;\n isFirstText: boolean;\n isReasoning: boolean;\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":[]}
@@ -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.2", modelId, params },
33
+ { version: "0.0.3", modelId, params },
34
34
  (_key, value) => {
35
35
  if (typeof value === "function") {
36
36
  return "[function]";
@@ -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 {\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":[]}
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 debug?: boolean;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n}\n\ninterface CachedGenerateResult {\n content: unknown;\n finishReason: unknown;\n providerMetadata: unknown;\n request: unknown;\n response: unknown;\n type: \"generate\";\n usage: unknown;\n warnings: unknown;\n}\n\ninterface CachedStreamResult {\n parts: LanguageModelV3StreamPart[];\n request: unknown;\n response: unknown;\n type: \"stream\";\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA2B;AAC3B,sBAOO;AACP,uBAAuC;AAoCvC,SAAS,mBAAmB,SAAiB,QAAyB;AACpE,QAAM,aAAa,KAAK;AAAA,IACtB,EAAE,SAAS,SAAqB,SAAS,OAAO;AAAA,IAChD,CAAC,MAAM,UAAU;AACf,UAAI,OAAO,UAAU,YAAY;AAC/B,eAAO;AAAA,MACT;AACA,UAAI,iBAAiB,QAAQ;AAC3B,eAAO,MAAM,SAAS;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,aAAO,+BAAW,QAAQ,EAAE,OAAO,UAAU,EAAE,OAAO,KAAK;AAC7D;AAEA,SAAS,aAAa,UAAkB,KAAqB;AAC3D,aAAO,uBAAK,UAAU,IAAI,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,OAAO;AACtD;AAEA,eAAe,UAAU,WAAiD;AACxE,MAAI;AACF,UAAM,UAAU,UAAM,0BAAS,WAAW,OAAO;AACjD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,QAAI,OAAO,YAAY,OAAO,OAAO,aAAa,UAAU;AAC1D,YAAM,OAAO,OAAO;AACpB,UAAI,OAAO,KAAK,cAAc,UAAU;AACtC,aAAK,YAAY,IAAI,KAAK,KAAK,SAAS;AAAA,MAC1C;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,WACb,WACA,QACe;AACf,MAAI;AACF,cAAM,2BAAM,0BAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,cAAM,2BAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO;AAAA,EAC5D,SAAQ;AAAA,EAER;AACF;AAEA,SAAS,sBACP,OAC2C;AAC3C,MAAI,QAAQ;AACZ,SAAO,IAAI,eAAe;AAAA,IACxB,KAAK,YAAY;AACf,UAAI,QAAQ,MAAM,QAAQ;AACxB,mBAAW,QAAQ,MAAM,OAAO,CAAC;AAAA,MACnC,OAAO;AACL,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAIA,SAAS,oBAAoB,cAAyC;AACpE,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AACA,QAAM,UACJ,OAAO,iBAAiB,WAAW,eAAe,aAAa;AACjE,SAAO,YAAY,WAAW,YAAY;AAC5C;AAEO,SAAS,0BACd,UAAsC,CAAC,GACZ;AAzH7B;AA0HE,QAAM,eAAc,aAAQ,gBAAR,YAAuB;AAC3C,QAAM,uBAAmB,2BAAQ,aAAQ,aAAR,YAAoB,WAAW;AAEhE,QAAM,aAAa,QAAQ,IAAI;AAC/B,QAAM,UACJ,eAAe,SACX,WAAW,YAAY,MAAM,UAAU,eAAe,OACrD,aAAQ,YAAR,YAAmB;AAE1B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,QACJ,aAAa,SACT,SAAS,YAAY,MAAM,UAAU,aAAa,OACjD,aAAQ,UAAR,YAAiB;AAExB,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,eACJ,oBAAoB,SAChB,gBAAgB,YAAY,MAAM,UAAU,oBAAoB,OAC/D,aAAQ,iBAAR,YAAwB;AAE/B,QAAM,MAAM,QACR,CAAC,KAAa,SACZ,QAAQ,IAAI,cAAc,GAAG,IAAI,sBAAQ,EAAE,IAC7C,MAAM;AAEV,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL,sBAAsB;AAAA,MACtB,iBAAiB,OAAO,EAAE,OAAO,MAAM;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,sBAAsB;AAAA,IAEtB,iBAAiB,OAAO,EAAE,OAAO,MAAM;AAAA,IAEvC,cAAc,OAAO,EAAE,YAAY,QAAQ,MAAM,MAAM;AACrD,YAAM,WAAW,YAAY,MAAM,SAAS,MAAM;AAClD,YAAM,YAAY,aAAa,kBAAkB,QAAQ;AAEzD,UAAI,CAAC,cAAc;AACjB,cAAM,SAAS,MAAM,UAAU,SAAS;AACxC,aAAI,iCAAQ,UAAS,YAAY;AAC/B,cAAI,gBAAgB,SAAS,MAAM,GAAG,CAAC,CAAC;AACxC,iBAAO;AAAA,YACL,SAAS,OAAO;AAAA,YAChB,cAAc,OAAO;AAAA,YACrB,OAAO,OAAO;AAAA,YACd,UAAU,OAAO;AAAA,YACjB,UAAU,OAAO;AAAA,YACjB,kBAAkB,OAAO;AAAA,YACzB,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAEA;AAAA,QACE,eAAe,qBAAqB;AAAA,QACpC,SAAS,MAAM,GAAG,CAAC;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,WAAW;AAEhC,UAAI,oBAAoB,OAAO,YAAY,GAAG;AAC5C,YAAI,+BAA+B,OAAO,YAAY;AAAA,MACxD,OAAO;AACL,cAAM,WAAW,WAAW;AAAA,UAC1B,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,cAAc,OAAO;AAAA,UACrB,OAAO,OAAO;AAAA,UACd,UAAU,OAAO;AAAA,UACjB,UAAU,OAAO;AAAA,UACjB,kBAAkB,OAAO;AAAA,UACzB,SAAS,OAAO;AAAA,QAClB,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,OAAO,EAAE,UAAU,QAAQ,MAAM,MAAM;AACjD,YAAM,WAAW,YAAY,MAAM,SAAS,MAAM;AAClD,YAAM,YAAY,aAAa,kBAAkB,QAAQ;AAEzD,UAAI,CAAC,cAAc;AACjB,cAAM,SAAS,MAAM,UAAU,SAAS;AACxC,aAAI,iCAAQ,UAAS,UAAU;AAC7B,cAAI,cAAc;AAAA,YAChB,KAAK,SAAS,MAAM,GAAG,CAAC;AAAA,YACxB,OAAO,OAAO,MAAM;AAAA,UACtB,CAAC;AACD,iBAAO;AAAA,YACL,QAAQ,sBAAsB,OAAO,KAAK;AAAA,YAC1C,UAAU,OAAO;AAAA,YACjB,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAEA;AAAA,QACE,eAAe,mBAAmB;AAAA,QAClC,SAAS,MAAM,GAAG,CAAC;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,SAAS;AAE9B,YAAM,iBAA8C,CAAC;AAErD,YAAM,eAAe,OAAO,OAAO;AAAA,QACjC,IAAI,gBAGF;AAAA,UACA,UAAU,OAAO,YAAY;AAC3B,2BAAe,KAAK,KAAK;AACzB,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ;AACN,kBAAM,aAAa,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACjE,gBAAI,cAAc,oBAAoB,WAAW,YAAY,GAAG;AAC9D;AAAA,YACF;AAEA,uBAAW,WAAW;AAAA,cACpB,MAAM;AAAA,cACN,OAAO;AAAA,cACP,UAAU,OAAO;AAAA,cACjB,SAAS,OAAO;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO,EAAE,GAAG,QAAQ,QAAQ,aAAa;AAAA,IAC3C;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,WAAW,aAA4B;AAC1E,MAAI;AACF,cAAM,wBAAG,0BAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC9D,SAAQ;AAAA,EAER;AACF;AAEA,eAAsB,cAAc,WAAW,aAK5C;AACD,QAAM,kBAAc,0BAAQ,QAAQ;AACpC,MAAI,aAAa;AACjB,MAAI,iBAAiB;AACrB,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAElB,iBAAe,QAAQ,KAA4B;AACjD,QAAI;AACF,YAAM,UAAU,UAAM,yBAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,YAAM,QAAQ;AAAA,QACZ,QAAQ,IAAI,OAAO,UAAU;AAC3B,gBAAM,eAAW,uBAAK,KAAK,MAAM,IAAI;AACrC,cAAI,MAAM,YAAY,GAAG;AACvB,kBAAM,QAAQ,QAAQ;AAAA,UACxB,WAAW,MAAM,KAAK,SAAS,OAAO,GAAG;AACvC;AACA,kBAAM,WAAW,UAAM,sBAAK,QAAQ;AACpC,8BAAkB,SAAS;AAE3B,gBAAI;AACF,oBAAM,UAAU,KAAK;AAAA,gBACnB,UAAM,0BAAS,UAAU,OAAO;AAAA,cAClC;AACA,kBAAI,QAAQ,SAAS,YAAY;AAC/B;AAAA,cACF,WAAW,QAAQ,SAAS,UAAU;AACpC;AAAA,cACF;AAAA,YACF,SAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,SAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,QAAQ,WAAW;AACzB,SAAO,EAAE,YAAY,gBAAgB,eAAe,YAAY;AAClE;","names":[]}
@@ -2,10 +2,10 @@ import { LanguageModelV3Middleware } from '@ai-sdk/provider';
2
2
 
3
3
  interface DiskCacheMiddlewareOptions {
4
4
  cacheDir?: string;
5
+ debug?: boolean;
5
6
  enabled?: boolean;
6
7
  forceRefresh?: boolean;
7
8
  generateKey?: (modelId: string, params: unknown) => string;
8
- debug?: boolean;
9
9
  }
10
10
  declare function createDiskCacheMiddleware(options?: DiskCacheMiddlewareOptions): LanguageModelV3Middleware;
11
11
  declare function clearDiskCache(cacheDir?: string): Promise<void>;
@@ -2,10 +2,10 @@ import { LanguageModelV3Middleware } from '@ai-sdk/provider';
2
2
 
3
3
  interface DiskCacheMiddlewareOptions {
4
4
  cacheDir?: string;
5
+ debug?: boolean;
5
6
  enabled?: boolean;
6
7
  forceRefresh?: boolean;
7
8
  generateKey?: (modelId: string, params: unknown) => string;
8
- debug?: boolean;
9
9
  }
10
10
  declare function createDiskCacheMiddleware(options?: DiskCacheMiddlewareOptions): LanguageModelV3Middleware;
11
11
  declare function clearDiskCache(cacheDir?: string): Promise<void>;
@@ -2,7 +2,7 @@ import {
2
2
  clearDiskCache,
3
3
  createDiskCacheMiddleware,
4
4
  getCacheStats
5
- } from "./chunk-ET4WAX7V.js";
5
+ } from "./chunk-F3QOGT7X.js";
6
6
  export {
7
7
  clearDiskCache,
8
8
  createDiskCacheMiddleware,
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.2", modelId, params },
138
+ { version: "0.0.3", modelId, params },
139
139
  (_key, value) => {
140
140
  if (typeof value === "function") {
141
141
  return "[function]";
@@ -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 {\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"]}
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 placement?: SystemPromptPlacement;\n systemPrompt: string;\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 debug?: boolean;\n enabled?: boolean;\n forceRefresh?: boolean;\n generateKey?: (modelId: string, params: unknown) => string;\n}\n\ninterface CachedGenerateResult {\n content: unknown;\n finishReason: unknown;\n providerMetadata: unknown;\n request: unknown;\n response: unknown;\n type: \"generate\";\n usage: unknown;\n warnings: unknown;\n}\n\ninterface CachedStreamResult {\n parts: LanguageModelV3StreamPart[];\n request: unknown;\n response: unknown;\n type: \"stream\";\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 afterSwitch: boolean;\n buffer: string;\n idCounter: number;\n isFirstReasoning: boolean;\n isFirstText: boolean;\n isReasoning: boolean;\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,SAAS,kBAAkB,SAAsC;AAC/D,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,QAAQ,OAAO,GAAG;AAC3B,QAAI,WAAW,MAAM;AACnB;AAAA,IACF;AACA,WAAO,OAAO,OAAO;AAAA,EACvB;AAEA,QAAM,QAAS,QAAqC,IAAI,CAAC,SAAS;AA1BpE;AA2BI,SAAI,6BAAM,UAAS,UAAU,UAAU,MAAM;AAC3C,aAAO,QAAO,UAAK,SAAL,YAAa,EAAE;AAAA,IAC/B;AAEA,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,CAAC;AAED,QAAM,YAAY,MAAM,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAC1D,MAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,EACF;AAEA,SAAO,UAAU,KAAK,IAAI;AAC5B;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIW;AACT,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,cAAc,UACjB,GAAG,QAAQ;AAAA;AAAA,EAAO,IAAI,KACtB,GAAG,IAAI;AAAA;AAAA,EAAO,QAAQ;AAC5B;AAEA,SAAS,kBACP,QACuB;AACvB,MAAI,CAAC,QAAQ;AACX,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,CAAC,GAAG,MAAM;AACnB;AAEO,SAAS,8BAA8B;AAAA,EAC5C;AAAA,EACA,YAAY;AACd,GAAoE;AAClE,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,iBAAiB,CAAC,EAAE,OAAO,MAAM;AAC/B,YAAM,SAAS,kBAAkB,OAAO,MAAM;AAC9C,YAAM,cAAc,OAAO;AAAA,QACzB,CAAC,YAAY,QAAQ,SAAS;AAAA,MAChC;AAEA,UAAI,gBAAgB,IAAI;AACtB,cAAM,mBACJ,cAAc,UACT;AAAA,UACC;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,UACA,GAAG;AAAA,QACL,IACC;AAAA,UACC,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAEN,cAAMA,cAAyC;AAAA,UAC7C,GAAG;AAAA,UACH,QAAQ;AAAA,QACV;AAEA,eAAO,QAAQ,QAAoCA,WAAU;AAAA,MAC/D;AAEA,YAAM,gBAAgB,OAAO,WAAW;AACxC,YAAM,WAAW,kBAAkB,cAAc,OAAO;AACxD,YAAM,gBAAgB,mBAAmB;AAAA,QACvC,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,OAAO;AAAA,QAAI,CAAC,SAAS,UACzC,UAAU,cACN;AAAA,UACE,GAAG;AAAA,UACH,SAAS;AAAA,QACX,IACA;AAAA,MACN;AAEA,YAAM,aAAyC;AAAA,QAC7C,GAAG;AAAA,QACH,QAAQ;AAAA,MACV;AAEA,aAAO,QAAQ,QAAoC,UAAU;AAAA,IAC/D;AAAA,EACF;AACF;;;ACzIA,yBAA2B;AAC3B,sBAOO;AACP,uBAAuC;AAoCvC,SAAS,mBAAmB,SAAiB,QAAyB;AACpE,QAAM,aAAa,KAAK;AAAA,IACtB,EAAE,SAAS,SAAqB,SAAS,OAAO;AAAA,IAChD,CAAC,MAAM,UAAU;AACf,UAAI,OAAO,UAAU,YAAY;AAC/B,eAAO;AAAA,MACT;AACA,UAAI,iBAAiB,QAAQ;AAC3B,eAAO,MAAM,SAAS;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,aAAO,+BAAW,QAAQ,EAAE,OAAO,UAAU,EAAE,OAAO,KAAK;AAC7D;AAEA,SAAS,aAAa,UAAkB,KAAqB;AAC3D,aAAO,uBAAK,UAAU,IAAI,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,OAAO;AACtD;AAEA,eAAe,UAAU,WAAiD;AACxE,MAAI;AACF,UAAM,UAAU,UAAM,0BAAS,WAAW,OAAO;AACjD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,QAAI,OAAO,YAAY,OAAO,OAAO,aAAa,UAAU;AAC1D,YAAM,OAAO,OAAO;AACpB,UAAI,OAAO,KAAK,cAAc,UAAU;AACtC,aAAK,YAAY,IAAI,KAAK,KAAK,SAAS;AAAA,MAC1C;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,WACb,WACA,QACe;AACf,MAAI;AACF,cAAM,2BAAM,0BAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,cAAM,2BAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO;AAAA,EAC5D,SAAQ;AAAA,EAER;AACF;AAEA,SAAS,sBACP,OAC2C;AAC3C,MAAI,QAAQ;AACZ,SAAO,IAAI,eAAe;AAAA,IACxB,KAAK,YAAY;AACf,UAAI,QAAQ,MAAM,QAAQ;AACxB,mBAAW,QAAQ,MAAM,OAAO,CAAC;AAAA,MACnC,OAAO;AACL,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAIA,SAAS,oBAAoB,cAAyC;AACpE,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AACA,QAAM,UACJ,OAAO,iBAAiB,WAAW,eAAe,aAAa;AACjE,SAAO,YAAY,WAAW,YAAY;AAC5C;AAEO,SAAS,0BACd,UAAsC,CAAC,GACZ;AAzH7B;AA0HE,QAAM,eAAc,aAAQ,gBAAR,YAAuB;AAC3C,QAAM,uBAAmB,2BAAQ,aAAQ,aAAR,YAAoB,WAAW;AAEhE,QAAM,aAAa,QAAQ,IAAI;AAC/B,QAAM,UACJ,eAAe,SACX,WAAW,YAAY,MAAM,UAAU,eAAe,OACrD,aAAQ,YAAR,YAAmB;AAE1B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,QACJ,aAAa,SACT,SAAS,YAAY,MAAM,UAAU,aAAa,OACjD,aAAQ,UAAR,YAAiB;AAExB,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,eACJ,oBAAoB,SAChB,gBAAgB,YAAY,MAAM,UAAU,oBAAoB,OAC/D,aAAQ,iBAAR,YAAwB;AAE/B,QAAM,MAAM,QACR,CAAC,KAAa,SACZ,QAAQ,IAAI,cAAc,GAAG,IAAI,sBAAQ,EAAE,IAC7C,MAAM;AAEV,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL,sBAAsB;AAAA,MACtB,iBAAiB,OAAO,EAAE,OAAO,MAAM;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,sBAAsB;AAAA,IAEtB,iBAAiB,OAAO,EAAE,OAAO,MAAM;AAAA,IAEvC,cAAc,OAAO,EAAE,YAAY,QAAQ,MAAM,MAAM;AACrD,YAAM,WAAW,YAAY,MAAM,SAAS,MAAM;AAClD,YAAM,YAAY,aAAa,kBAAkB,QAAQ;AAEzD,UAAI,CAAC,cAAc;AACjB,cAAM,SAAS,MAAM,UAAU,SAAS;AACxC,aAAI,iCAAQ,UAAS,YAAY;AAC/B,cAAI,gBAAgB,SAAS,MAAM,GAAG,CAAC,CAAC;AACxC,iBAAO;AAAA,YACL,SAAS,OAAO;AAAA,YAChB,cAAc,OAAO;AAAA,YACrB,OAAO,OAAO;AAAA,YACd,UAAU,OAAO;AAAA,YACjB,UAAU,OAAO;AAAA,YACjB,kBAAkB,OAAO;AAAA,YACzB,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAEA;AAAA,QACE,eAAe,qBAAqB;AAAA,QACpC,SAAS,MAAM,GAAG,CAAC;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,WAAW;AAEhC,UAAI,oBAAoB,OAAO,YAAY,GAAG;AAC5C,YAAI,+BAA+B,OAAO,YAAY;AAAA,MACxD,OAAO;AACL,cAAM,WAAW,WAAW;AAAA,UAC1B,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,cAAc,OAAO;AAAA,UACrB,OAAO,OAAO;AAAA,UACd,UAAU,OAAO;AAAA,UACjB,UAAU,OAAO;AAAA,UACjB,kBAAkB,OAAO;AAAA,UACzB,SAAS,OAAO;AAAA,QAClB,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,OAAO,EAAE,UAAU,QAAQ,MAAM,MAAM;AACjD,YAAM,WAAW,YAAY,MAAM,SAAS,MAAM;AAClD,YAAM,YAAY,aAAa,kBAAkB,QAAQ;AAEzD,UAAI,CAAC,cAAc;AACjB,cAAM,SAAS,MAAM,UAAU,SAAS;AACxC,aAAI,iCAAQ,UAAS,UAAU;AAC7B,cAAI,cAAc;AAAA,YAChB,KAAK,SAAS,MAAM,GAAG,CAAC;AAAA,YACxB,OAAO,OAAO,MAAM;AAAA,UACtB,CAAC;AACD,iBAAO;AAAA,YACL,QAAQ,sBAAsB,OAAO,KAAK;AAAA,YAC1C,UAAU,OAAO;AAAA,YACjB,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAEA;AAAA,QACE,eAAe,mBAAmB;AAAA,QAClC,SAAS,MAAM,GAAG,CAAC;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,SAAS;AAE9B,YAAM,iBAA8C,CAAC;AAErD,YAAM,eAAe,OAAO,OAAO;AAAA,QACjC,IAAI,gBAGF;AAAA,UACA,UAAU,OAAO,YAAY;AAC3B,2BAAe,KAAK,KAAK;AACzB,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ;AACN,kBAAM,aAAa,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACjE,gBAAI,cAAc,oBAAoB,WAAW,YAAY,GAAG;AAC9D;AAAA,YACF;AAEA,uBAAW,WAAW;AAAA,cACpB,MAAM;AAAA,cACN,OAAO;AAAA,cACP,UAAU,OAAO;AAAA,cACjB,SAAS,OAAO;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO,EAAE,GAAG,QAAQ,QAAQ,aAAa;AAAA,IAC3C;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,WAAW,aAA4B;AAC1E,MAAI;AACF,cAAM,wBAAG,0BAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC9D,SAAQ;AAAA,EAER;AACF;AAEA,eAAsB,cAAc,WAAW,aAK5C;AACD,QAAM,kBAAc,0BAAQ,QAAQ;AACpC,MAAI,aAAa;AACjB,MAAI,iBAAiB;AACrB,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAElB,iBAAe,QAAQ,KAA4B;AACjD,QAAI;AACF,YAAM,UAAU,UAAM,yBAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,YAAM,QAAQ;AAAA,QACZ,QAAQ,IAAI,OAAO,UAAU;AAC3B,gBAAM,eAAW,uBAAK,KAAK,MAAM,IAAI;AACrC,cAAI,MAAM,YAAY,GAAG;AACvB,kBAAM,QAAQ,QAAQ;AAAA,UACxB,WAAW,MAAM,KAAK,SAAS,OAAO,GAAG;AACvC;AACA,kBAAM,WAAW,UAAM,sBAAK,QAAQ;AACpC,8BAAkB,SAAS;AAE3B,gBAAI;AACF,oBAAM,UAAU,KAAK;AAAA,gBACnB,UAAM,0BAAS,UAAU,OAAO;AAAA,cAClC;AACA,kBAAI,QAAQ,SAAS,YAAY;AAC/B;AAAA,cACF,WAAW,QAAQ,SAAS,UAAU;AACpC;AAAA,cACF;AAAA,YACF,SAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,SAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,QAAQ,WAAW;AACzB,SAAO,EAAE,YAAY,gBAAgB,eAAe,YAAY;AAClE;;;ACxSO,SAAS,uBACd,MACA,cACe;AAEf,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,KAAK,QAAQ,YAAY;AAC7C,MAAI,gBAAgB,IAAI;AACtB,WAAO;AAAA,EACT;AAIA,WAAS,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC5C,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,aAAa,WAAW,MAAM,GAAG;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAWO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,qBAAqB;AACvB,GAK8B;AAC5B,WAAS,gBACP,MACA,oBACA;AAtEJ;AAuEI,UAAM,SAAS,IAAI,OAAO,GAAG,UAAU,QAAQ,UAAU,IAAI,IAAI;AACjE,UAAM,UAAU,MAAM,KAAK,KAAK,SAAS,MAAM,CAAC;AAEhD,QAAI,CAAC,QAAQ,QAAQ;AACnB;AAAA,IACF;AAEA,UAAM,gBAAgB,QAAQ,IAAI,CAAC,UAAU,MAAM,CAAC,CAAC,EAAE,KAAK,SAAS;AAErE,QAAI,uBAAuB;AAC3B,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC/C,YAAM,QAAQ,QAAQ,CAAC;AAEvB,YAAM,cAAc,qBAAqB,MAAM,GAAG,MAAM,KAAK;AAC7D,YAAM,cAAa,WAAM,UAAN,YAAe;AAClC,YAAM,aAAa,qBAAqB;AAAA,QACtC,aAAa,MAAM,CAAC,EAAE;AAAA,MACxB;AAEA,6BACE,eACC,YAAY,SAAS,KAAK,WAAW,SAAS,IAAI,YAAY,MAC/D;AAAA,IACJ;AAEA,uBAAmB,KAAK;AAAA,MACtB,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAED,uBAAmB,KAAK;AAAA,MACtB,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,cAAc,OAAO,EAAE,WAAW,MAAM;AACtC,YAAM,EAAE,SAAS,GAAG,KAAK,IAAI,MAAM,WAAW;AAE9C,YAAM,qBAA+C,CAAC;AACtD,iBAAW,QAAQ,SAAS;AAC1B,YAAI,KAAK,SAAS,QAAQ;AACxB,6BAAmB,KAAK,IAAI;AAC5B;AAAA,QACF;AAEA,cAAM,OAAO,qBAAqB,aAAa,KAAK,OAAO,KAAK;AAChE,cAAM,SAAS,IAAI,OAAO,GAAG,UAAU,QAAQ,UAAU,IAAI,IAAI;AACjE,cAAM,UAAU,MAAM,KAAK,KAAK,SAAS,MAAM,CAAC;AAEhD,YAAI,CAAC,QAAQ,QAAQ;AACnB,6BAAmB,KAAK,IAAI;AAC5B;AAAA,QACF;AAEA,wBAAgB,MAAM,kBAAkB;AAAA,MAC1C;AAEA,aAAO,EAAE,SAAS,oBAAoB,GAAG,KAAK;AAAA,IAChD;AAAA,IAEA,YAAY,OAAO,EAAE,SAAS,MAAM;AAClC,YAAM,EAAE,QAAQ,GAAG,KAAK,IAAI,MAAM,SAAS;AAY3C,YAAM,uBAAwD,CAAC;AAE/D,eAAS,gBACP,kBACA,YACA;AACA,eAAO,CAAC,SAAiB;AACvB,cAAI,KAAK,WAAW,GAAG;AACrB;AAAA,UACF;AAEA,gBAAM,SAAS,UAAU,gBAAgB;AACzC,gCAAsB,kBAAkB,UAAU;AAClD,uBAAa,kBAAkB,YAAY,QAAQ,IAAI;AACvD,gCAAsB,gBAAgB;AAAA,QACxC;AAAA,MACF;AAEA,eAAS,UAAU,kBAA2C;AAC5D,eAAO,iBAAiB,gBACrB,iBAAiB,cACd,CAAC,iBAAiB,mBAClB,CAAC,iBAAiB,eACpB,YACA;AAAA,MACN;AAEA,eAAS,sBACP,kBACA,YACA;AACA,YACG,iBAAiB,eAAe,iBAAiB,eAClD,iBAAiB,kBACjB;AACA,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,IAAI,aAAa,iBAAiB,SAAS;AAAA,UAC7C,CAAC;AAAA,QACH;AAAA,MACF;AAEA,eAAS,aACP,kBACA,YACA,QACA,MACA;AACA,mBAAW;AAAA,UACT,iBAAiB,cACb;AAAA,YACE,MAAM;AAAA,YACN,OAAO,SAAS;AAAA,YAChB,IAAI,aAAa,iBAAiB,SAAS;AAAA,UAC7C,IACA;AAAA,YACE,MAAM;AAAA,YACN,OAAO,SAAS;AAAA,YAChB,IAAI,iBAAiB;AAAA,UACvB;AAAA,QACN;AAAA,MACF;AAEA,eAAS,sBAAsB,kBAAmC;AAChE,yBAAiB,cAAc;AAC/B,YAAI,iBAAiB,aAAa;AAChC,2BAAiB,mBAAmB;AAAA,QACtC,OAAO;AACL,2BAAiB,cAAc;AAAA,QACjC;AAAA,MACF;AAEA,eAAS,gBACP,kBACA,YACA,YACA,SACA;AACA,yBAAiB,SAAS,iBAAiB,OAAO;AAAA,UAChD,aAAa,QAAQ;AAAA,QACvB;AAEA,YAAI,iBAAiB,aAAa;AAChC,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,IAAI,aAAa,iBAAiB,SAAS;AAAA,UAC7C,CAAC;AACD,2BAAiB,aAAa;AAAA,QAChC;AAEA,yBAAiB,cAAc,CAAC,iBAAiB;AACjD,yBAAiB,cAAc;AAAA,MACjC;AAEA,eAAS,gBAAgB;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,GAMY;AACV,gBAAQ,iBAAiB,OAAO,MAAM,GAAG,UAAU,CAAC;AAEpD,cAAM,iBACJ,aAAa,QAAQ,UAAU,iBAAiB,OAAO;AAEzD,YAAI,gBAAgB;AAClB,0BAAgB,kBAAkB,YAAY,YAAY,OAAO;AACjE,iBAAO;AAAA,QACT;AAEA,yBAAiB,SAAS,iBAAiB,OAAO,MAAM,UAAU;AAClE,eAAO;AAAA,MACT;AAEA,eAAS,cACP,kBACA,YACA;AACA,cAAM,UAAU,gBAAgB,kBAAkB,UAAU;AAC5D,YAAI,qBAAqB;AAEzB,eAAO,oBAAoB;AACzB,gBAAM,UAAU,iBAAiB,cAC7B,aACA;AACJ,gBAAM,aAAa;AAAA,YACjB,iBAAiB;AAAA,YACjB;AAAA,UACF;AAEA,cAAI,cAAc,MAAM;AACtB,oBAAQ,iBAAiB,MAAM;AAC/B,6BAAiB,SAAS;AAC1B;AAAA,UACF;AAEA,+BAAqB,gBAAgB;AAAA,YACnC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,aAAO;AAAA,QACL,QAAQ,OAAO;AAAA,UACb,IAAI,gBAGF;AAAA,YACA,WAAW,CAAC,OAAO,eAAe;AAChC,kBAAI,MAAM,SAAS,cAAc;AAC/B,2BAAW,QAAQ,KAAK;AACxB;AAAA,cACF;AAEA,kBAAI,qBAAqB,MAAM,EAAE,KAAK,MAAM;AAC1C,qCAAqB,MAAM,EAAE,IAAI;AAAA,kBAC/B,kBAAkB;AAAA,kBAClB,aAAa;AAAA,kBACb,aAAa;AAAA,kBACb,aAAa;AAAA,kBACb,QAAQ;AAAA,kBACR,WAAW;AAAA,kBACX,QAAQ,MAAM;AAAA,gBAChB;AAAA,cACF;AAEA,oBAAM,mBAAmB,qBAAqB,MAAM,EAAE;AACtD,+BAAiB,UAAU,MAAM;AACjC,4BAAc,kBAAkB,UAAU;AAAA,YAC5C;AAAA,UACF,CAAC;AAAA,QACH;AAAA,QACA,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACF;","names":["nextParams"]}
package/dist/index.d.cts CHANGED
@@ -4,8 +4,8 @@ export { extractReasoningMiddleware, getPotentialStartIndex } from './reasoning-
4
4
 
5
5
  type SystemPromptPlacement = "first" | "last";
6
6
  interface DefaultSystemPromptMiddlewareOptions {
7
- systemPrompt: string;
8
7
  placement?: SystemPromptPlacement;
8
+ systemPrompt: string;
9
9
  }
10
10
  declare function defaultSystemPromptMiddleware({ systemPrompt, placement, }: DefaultSystemPromptMiddlewareOptions): LanguageModelV3Middleware;
11
11
 
package/dist/index.d.ts CHANGED
@@ -4,8 +4,8 @@ export { extractReasoningMiddleware, getPotentialStartIndex } from './reasoning-
4
4
 
5
5
  type SystemPromptPlacement = "first" | "last";
6
6
  interface DefaultSystemPromptMiddlewareOptions {
7
- systemPrompt: string;
8
7
  placement?: SystemPromptPlacement;
8
+ systemPrompt: string;
9
9
  }
10
10
  declare function defaultSystemPromptMiddleware({ systemPrompt, placement, }: DefaultSystemPromptMiddlewareOptions): LanguageModelV3Middleware;
11
11
 
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  extractReasoningMiddleware,
3
3
  getPotentialStartIndex
4
- } from "./chunk-R4PZN7IW.js";
4
+ } from "./chunk-MLWJBQIP.js";
5
5
  import {
6
6
  clearDiskCache,
7
7
  createDiskCacheMiddleware,
8
8
  getCacheStats
9
- } from "./chunk-ET4WAX7V.js";
9
+ } from "./chunk-F3QOGT7X.js";
10
10
 
11
11
  // src/default-system-prompt.ts
12
12
  function extractSystemText(content) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/default-system-prompt.ts"],"sourcesContent":["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"],"mappings":";;;;;;;;;;;AAcA,SAAS,kBAAkB,SAAsC;AAC/D,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,QAAQ,OAAO,GAAG;AAC3B,QAAI,WAAW,MAAM;AACnB;AAAA,IACF;AACA,WAAO,OAAO,OAAO;AAAA,EACvB;AAEA,QAAM,QAAS,QAAqC,IAAI,CAAC,SAAS;AA1BpE;AA2BI,SAAI,6BAAM,UAAS,UAAU,UAAU,MAAM;AAC3C,aAAO,QAAO,UAAK,SAAL,YAAa,EAAE;AAAA,IAC/B;AAEA,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,CAAC;AAED,QAAM,YAAY,MAAM,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAC1D,MAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,EACF;AAEA,SAAO,UAAU,KAAK,IAAI;AAC5B;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIW;AACT,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,cAAc,UACjB,GAAG,QAAQ;AAAA;AAAA,EAAO,IAAI,KACtB,GAAG,IAAI;AAAA;AAAA,EAAO,QAAQ;AAC5B;AAEA,SAAS,kBACP,QACuB;AACvB,MAAI,CAAC,QAAQ;AACX,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,CAAC,GAAG,MAAM;AACnB;AAEO,SAAS,8BAA8B;AAAA,EAC5C;AAAA,EACA,YAAY;AACd,GAAoE;AAClE,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,iBAAiB,CAAC,EAAE,OAAO,MAAM;AAC/B,YAAM,SAAS,kBAAkB,OAAO,MAAM;AAC9C,YAAM,cAAc,OAAO;AAAA,QACzB,CAAC,YAAY,QAAQ,SAAS;AAAA,MAChC;AAEA,UAAI,gBAAgB,IAAI;AACtB,cAAM,mBACJ,cAAc,UACT;AAAA,UACC;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,UACA,GAAG;AAAA,QACL,IACC;AAAA,UACC,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAEN,cAAMA,cAAyC;AAAA,UAC7C,GAAG;AAAA,UACH,QAAQ;AAAA,QACV;AAEA,eAAO,QAAQ,QAAoCA,WAAU;AAAA,MAC/D;AAEA,YAAM,gBAAgB,OAAO,WAAW;AACxC,YAAM,WAAW,kBAAkB,cAAc,OAAO;AACxD,YAAM,gBAAgB,mBAAmB;AAAA,QACvC,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,OAAO;AAAA,QAAI,CAAC,SAAS,UACzC,UAAU,cACN;AAAA,UACE,GAAG;AAAA,UACH,SAAS;AAAA,QACX,IACA;AAAA,MACN;AAEA,YAAM,aAAyC;AAAA,QAC7C,GAAG;AAAA,QACH,QAAQ;AAAA,MACV;AAEA,aAAO,QAAQ,QAAoC,UAAU;AAAA,IAC/D;AAAA,EACF;AACF;","names":["nextParams"]}
1
+ {"version":3,"sources":["../src/default-system-prompt.ts"],"sourcesContent":["import type {\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3Middleware,\n LanguageModelV3Prompt,\n} from \"@ai-sdk/provider\";\n\ntype SystemPromptPlacement = \"first\" | \"last\";\n\ninterface DefaultSystemPromptMiddlewareOptions {\n placement?: SystemPromptPlacement;\n systemPrompt: string;\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"],"mappings":";;;;;;;;;;;AAcA,SAAS,kBAAkB,SAAsC;AAC/D,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,QAAQ,OAAO,GAAG;AAC3B,QAAI,WAAW,MAAM;AACnB;AAAA,IACF;AACA,WAAO,OAAO,OAAO;AAAA,EACvB;AAEA,QAAM,QAAS,QAAqC,IAAI,CAAC,SAAS;AA1BpE;AA2BI,SAAI,6BAAM,UAAS,UAAU,UAAU,MAAM;AAC3C,aAAO,QAAO,UAAK,SAAL,YAAa,EAAE;AAAA,IAC/B;AAEA,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,CAAC;AAED,QAAM,YAAY,MAAM,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAC1D,MAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,EACF;AAEA,SAAO,UAAU,KAAK,IAAI;AAC5B;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIW;AACT,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,cAAc,UACjB,GAAG,QAAQ;AAAA;AAAA,EAAO,IAAI,KACtB,GAAG,IAAI;AAAA;AAAA,EAAO,QAAQ;AAC5B;AAEA,SAAS,kBACP,QACuB;AACvB,MAAI,CAAC,QAAQ;AACX,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,CAAC,GAAG,MAAM;AACnB;AAEO,SAAS,8BAA8B;AAAA,EAC5C;AAAA,EACA,YAAY;AACd,GAAoE;AAClE,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,iBAAiB,CAAC,EAAE,OAAO,MAAM;AAC/B,YAAM,SAAS,kBAAkB,OAAO,MAAM;AAC9C,YAAM,cAAc,OAAO;AAAA,QACzB,CAAC,YAAY,QAAQ,SAAS;AAAA,MAChC;AAEA,UAAI,gBAAgB,IAAI;AACtB,cAAM,mBACJ,cAAc,UACT;AAAA,UACC;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,UACA,GAAG;AAAA,QACL,IACC;AAAA,UACC,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAEN,cAAMA,cAAyC;AAAA,UAC7C,GAAG;AAAA,UACH,QAAQ;AAAA,QACV;AAEA,eAAO,QAAQ,QAAoCA,WAAU;AAAA,MAC/D;AAEA,YAAM,gBAAgB,OAAO,WAAW;AACxC,YAAM,WAAW,kBAAkB,cAAc,OAAO;AACxD,YAAM,gBAAgB,mBAAmB;AAAA,QACvC,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AAED,YAAM,gBAAgB,OAAO;AAAA,QAAI,CAAC,SAAS,UACzC,UAAU,cACN;AAAA,UACE,GAAG;AAAA,UACH,SAAS;AAAA,QACX,IACA;AAAA,MACN;AAEA,YAAM,aAAyC;AAAA,QAC7C,GAAG;AAAA,QACH,QAAQ;AAAA,MACV;AAEA,aAAO,QAAQ,QAAoC,UAAU;AAAA,IAC/D;AAAA,EACF;AACF;","names":["nextParams"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reasoning-parser.ts"],"sourcesContent":["/**\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":[]}
1
+ {"version":3,"sources":["../src/reasoning-parser.ts"],"sourcesContent":["/**\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 afterSwitch: boolean;\n buffer: string;\n idCounter: number;\n isFirstReasoning: boolean;\n isFirstText: boolean;\n isReasoning: boolean;\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":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  extractReasoningMiddleware,
3
3
  getPotentialStartIndex
4
- } from "./chunk-R4PZN7IW.js";
4
+ } from "./chunk-MLWJBQIP.js";
5
5
  export {
6
6
  extractReasoningMiddleware,
7
7
  getPotentialStartIndex
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@ai-sdk-tool/middleware",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Collection of reusable AI SDK middlewares",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/minpeter/ai-sdk-tool-call-middleware",
9
- "directory": "packages/middleware"
8
+ "url": "https://github.com/minpeter/ai-sdk-middleware"
10
9
  },
11
10
  "main": "./dist/index.js",
12
11
  "module": "./dist/index.mjs",
@@ -35,12 +34,17 @@
35
34
  "access": "public"
36
35
  },
37
36
  "dependencies": {
38
- "@ai-sdk/provider": "3.0.1"
37
+ "@ai-sdk/provider": "3.0.8"
39
38
  },
40
39
  "devDependencies": {
41
- "@types/node": "^25.0.3",
40
+ "@biomejs/biome": "2.4.3",
41
+ "@changesets/cli": "2.29.8",
42
+ "@types/node": "^25.3.0",
43
+ "@vitest/coverage-v8": "^4.0.18",
42
44
  "tsup": "^8.5.1",
43
- "@ai-sdkx/tsconfig": "0.0.1"
45
+ "typescript": "5.9.3",
46
+ "ultracite": "7.2.3",
47
+ "vitest": "^4.0.18"
44
48
  },
45
49
  "keywords": [
46
50
  "ai",
@@ -49,11 +53,23 @@
49
53
  ],
50
54
  "author": "",
51
55
  "license": "Apache-2.0",
56
+ "engines": {
57
+ "node": ">=18"
58
+ },
52
59
  "scripts": {
53
60
  "build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
54
61
  "build:watch": "pnpm clean && tsup --watch --tsconfig tsconfig.build.json",
55
62
  "clean": "rm -rf dist *.tsbuildinfo",
56
63
  "typecheck": "tsc --noEmit",
57
- "test": "vitest run"
64
+ "test": "vitest run",
65
+ "test:coverage": "vitest run --coverage",
66
+ "check:biome": "biome check",
67
+ "check:types": "pnpm run typecheck",
68
+ "check": "pnpm run check:biome && pnpm run check:types",
69
+ "fmt:biome": "biome check --write",
70
+ "fmt": "pnpm run fmt:biome",
71
+ "changeset": "changeset",
72
+ "ci:release": "pnpm build && changeset publish",
73
+ "ci:version": "changeset version"
58
74
  }
59
75
  }