@use-lattice/litmus 0.121.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.
Files changed (199) hide show
  1. package/LICENSE +19 -0
  2. package/dist/src/accounts-Bt1oJb1Z.cjs +219 -0
  3. package/dist/src/accounts-DjOU8Rm3.js +178 -0
  4. package/dist/src/agentic-utils-D03IiXQc.js +153 -0
  5. package/dist/src/agentic-utils-Dh7xaMQM.cjs +180 -0
  6. package/dist/src/agents-C6BIMlZa.js +231 -0
  7. package/dist/src/agents-DvIpNX1L.cjs +666 -0
  8. package/dist/src/agents-ZP0RP9vV.cjs +231 -0
  9. package/dist/src/agents-maJXdjbR.js +665 -0
  10. package/dist/src/aimlapi-BTbQjG2E.cjs +30 -0
  11. package/dist/src/aimlapi-CwMxqfXP.js +30 -0
  12. package/dist/src/audio-BBUdvsde.cjs +97 -0
  13. package/dist/src/audio-D5DPZ7I-.js +97 -0
  14. package/dist/src/base-BEysXrkq.cjs +222 -0
  15. package/dist/src/base-C451JQfq.js +193 -0
  16. package/dist/src/blobs-BY8MDmpo.js +230 -0
  17. package/dist/src/blobs-BgcNn97m.cjs +256 -0
  18. package/dist/src/cache-BBE_lsTA.cjs +4 -0
  19. package/dist/src/cache-BkrqU5Ba.js +237 -0
  20. package/dist/src/cache-DsCxFlsZ.cjs +297 -0
  21. package/dist/src/chat-CPJWDP6a.cjs +289 -0
  22. package/dist/src/chat-CXX3xzkk.cjs +811 -0
  23. package/dist/src/chat-CcDgZFJ4.js +787 -0
  24. package/dist/src/chat-Dz5ZeGO2.js +289 -0
  25. package/dist/src/chatkit-Dw0mKkML.cjs +1158 -0
  26. package/dist/src/chatkit-swAIVuea.js +1157 -0
  27. package/dist/src/chunk-DEq-mXcV.js +15 -0
  28. package/dist/src/claude-agent-sdk-BXZJtOg6.js +379 -0
  29. package/dist/src/claude-agent-sdk-CkfyjDoG.cjs +383 -0
  30. package/dist/src/cloudflare-ai-BzpJcqUH.js +161 -0
  31. package/dist/src/cloudflare-ai-Cmy_R1y2.cjs +161 -0
  32. package/dist/src/cloudflare-gateway-B9tVQKok.cjs +272 -0
  33. package/dist/src/cloudflare-gateway-DrD3ew3H.js +272 -0
  34. package/dist/src/codex-sdk-Dezj9Nwm.js +1056 -0
  35. package/dist/src/codex-sdk-Dl9D4k5B.cjs +1060 -0
  36. package/dist/src/cometapi-C-9YvCHC.js +54 -0
  37. package/dist/src/cometapi-DHgDKoO2.cjs +54 -0
  38. package/dist/src/completion-B8Ctyxpr.js +120 -0
  39. package/dist/src/completion-Cxrt08sj.cjs +131 -0
  40. package/dist/src/createHash-BwgE13yv.cjs +27 -0
  41. package/dist/src/createHash-DmPQkvBh.js +15 -0
  42. package/dist/src/docker-BiqcTwLv.js +80 -0
  43. package/dist/src/docker-C7tEJnP-.cjs +80 -0
  44. package/dist/src/esm-C62Zofr1.cjs +409 -0
  45. package/dist/src/esm-DMVc93eh.js +379 -0
  46. package/dist/src/evalResult-C3NJPQOo.cjs +301 -0
  47. package/dist/src/evalResult-C7JJAPBb.js +295 -0
  48. package/dist/src/evalResult-DoVTZZWI.cjs +2 -0
  49. package/dist/src/extractor-DnMD3fwt.cjs +391 -0
  50. package/dist/src/extractor-DtlL28vL.js +374 -0
  51. package/dist/src/fetch-BTxakTSg.cjs +1133 -0
  52. package/dist/src/fetch-DQckpUFz.js +928 -0
  53. package/dist/src/fileExtensions-DnqA1y9x.js +85 -0
  54. package/dist/src/fileExtensions-bYh77CN8.cjs +114 -0
  55. package/dist/src/genaiTracer-CyZrmaK0.cjs +268 -0
  56. package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
  57. package/dist/src/graders-BNscxFrU.js +13644 -0
  58. package/dist/src/graders-D2oE9Msq.js +2 -0
  59. package/dist/src/graders-c0Ez_w-9.cjs +2 -0
  60. package/dist/src/graders-d0F2M3e9.cjs +14056 -0
  61. package/dist/src/image-0ZhE0VlR.cjs +280 -0
  62. package/dist/src/image-CWE1pdNv.js +257 -0
  63. package/dist/src/image-D9ZK6hwL.js +163 -0
  64. package/dist/src/image-DKZgZITg.cjs +163 -0
  65. package/dist/src/index.cjs +11366 -0
  66. package/dist/src/index.d.cts +19640 -0
  67. package/dist/src/index.d.ts +19641 -0
  68. package/dist/src/index.js +11306 -0
  69. package/dist/src/invariant-Ddh24eXh.js +25 -0
  70. package/dist/src/invariant-kfQ8Bu82.cjs +30 -0
  71. package/dist/src/knowledgeBase-BgPyGFUd.cjs +122 -0
  72. package/dist/src/knowledgeBase-DyHilYaP.js +122 -0
  73. package/dist/src/litellm-CyMeneHS.js +135 -0
  74. package/dist/src/litellm-DWDF73yF.cjs +135 -0
  75. package/dist/src/logger-C40ZGil9.js +717 -0
  76. package/dist/src/logger-DyfK9PBt.cjs +917 -0
  77. package/dist/src/luma-ray-BAU9X_ep.cjs +315 -0
  78. package/dist/src/luma-ray-nwVseBbv.js +313 -0
  79. package/dist/src/messages-B5ADWTTv.js +245 -0
  80. package/dist/src/messages-BCnZfqrS.cjs +257 -0
  81. package/dist/src/meteor-DLZZ3osF.cjs +134 -0
  82. package/dist/src/meteor-DUiCJRC-.js +134 -0
  83. package/dist/src/modelslab-00cveB8L.cjs +163 -0
  84. package/dist/src/modelslab-D9sCU_L7.js +163 -0
  85. package/dist/src/nova-reel-CTapvqYH.js +276 -0
  86. package/dist/src/nova-reel-DlWuuroF.cjs +278 -0
  87. package/dist/src/nova-sonic-5UPWfeMv.cjs +363 -0
  88. package/dist/src/nova-sonic-BhSwQNym.js +363 -0
  89. package/dist/src/openai-BWrJK9d8.cjs +52 -0
  90. package/dist/src/openai-DumO8WQn.js +47 -0
  91. package/dist/src/openclaw-B8brrjC_.cjs +577 -0
  92. package/dist/src/openclaw-Bkayww9q.js +571 -0
  93. package/dist/src/opencode-sdk-7xjoDNiM.cjs +562 -0
  94. package/dist/src/opencode-sdk-SGwAPxht.js +558 -0
  95. package/dist/src/otlpReceiver-CoAHfAN9.cjs +15 -0
  96. package/dist/src/otlpReceiver-oO3EQwI9.js +14 -0
  97. package/dist/src/providerRegistry-4yjhaEM8.js +45 -0
  98. package/dist/src/providerRegistry-DhV4rJIc.cjs +50 -0
  99. package/dist/src/providers-B5RJVG-7.cjs +33609 -0
  100. package/dist/src/providers-BdmZCLzV.js +33262 -0
  101. package/dist/src/providers-CxtRxn8e.js +2 -0
  102. package/dist/src/providers-DnQLNbx1.cjs +3 -0
  103. package/dist/src/pythonUtils-BD0druiM.cjs +275 -0
  104. package/dist/src/pythonUtils-IBhn5YGR.js +249 -0
  105. package/dist/src/quiverai-BDOwZBsM.cjs +213 -0
  106. package/dist/src/quiverai-D3JTF5lD.js +213 -0
  107. package/dist/src/responses-B2LCDCXZ.js +667 -0
  108. package/dist/src/responses-BvNm4Xv9.cjs +685 -0
  109. package/dist/src/rubyUtils-B0NwnfpY.cjs +245 -0
  110. package/dist/src/rubyUtils-BroxzZ7c.cjs +2 -0
  111. package/dist/src/rubyUtils-hqVw5UvJ.js +222 -0
  112. package/dist/src/sagemaker-Cno2V-Sx.js +689 -0
  113. package/dist/src/sagemaker-fV_KUgs5.cjs +691 -0
  114. package/dist/src/server-BOuAXb06.cjs +238 -0
  115. package/dist/src/server-CtI-EWzm.cjs +2 -0
  116. package/dist/src/server-Cy3DZymt.js +189 -0
  117. package/dist/src/slack-CP8xBePa.js +135 -0
  118. package/dist/src/slack-DSQ1yXVb.cjs +135 -0
  119. package/dist/src/store-BwDDaBjb.cjs +246 -0
  120. package/dist/src/store-DcbLC593.cjs +2 -0
  121. package/dist/src/store-IGpqMIkv.js +240 -0
  122. package/dist/src/tables-3Q2cL7So.cjs +373 -0
  123. package/dist/src/tables-Bi2fjr4W.js +288 -0
  124. package/dist/src/telemetry-Bg2WqF79.js +161 -0
  125. package/dist/src/telemetry-D0x6u5kX.cjs +166 -0
  126. package/dist/src/telemetry-DXNimrI0.cjs +2 -0
  127. package/dist/src/text-B_UCRPp2.js +22 -0
  128. package/dist/src/text-CW1cyrwj.cjs +33 -0
  129. package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
  130. package/dist/src/tokenUsageUtils-bVa1ga6f.cjs +173 -0
  131. package/dist/src/transcription-Cl_W16Pr.js +122 -0
  132. package/dist/src/transcription-yt1EecY8.cjs +124 -0
  133. package/dist/src/transform-BCtGrl_W.cjs +228 -0
  134. package/dist/src/transform-Bv6gG2MJ.cjs +1688 -0
  135. package/dist/src/transform-CY1wbpRy.js +1507 -0
  136. package/dist/src/transform-DU8rUL9P.cjs +2 -0
  137. package/dist/src/transform-yWaShiKr.js +216 -0
  138. package/dist/src/transformersAvailability-BGkzavwb.js +35 -0
  139. package/dist/src/transformersAvailability-DKoRtQLy.cjs +35 -0
  140. package/dist/src/types-5aqHpBwE.cjs +3769 -0
  141. package/dist/src/types-Bn6D9c4U.js +3300 -0
  142. package/dist/src/util-BkKlTkI2.js +293 -0
  143. package/dist/src/util-CTh0bfOm.cjs +1119 -0
  144. package/dist/src/util-D17oBwo7.cjs +328 -0
  145. package/dist/src/util-DsS_-v4p.js +613 -0
  146. package/dist/src/util-DuntT1Ga.js +951 -0
  147. package/dist/src/util-aWjdCYMI.cjs +667 -0
  148. package/dist/src/utils-CisQwpjA.js +94 -0
  149. package/dist/src/utils-yWamDvmz.cjs +123 -0
  150. package/dist/tsconfig.tsbuildinfo +1 -0
  151. package/drizzle/0000_lush_hellion.sql +36 -0
  152. package/drizzle/0001_wide_calypso.sql +3 -0
  153. package/drizzle/0002_tidy_juggernaut.sql +1 -0
  154. package/drizzle/0003_lively_naoko.sql +8 -0
  155. package/drizzle/0004_minor_peter_quill.sql +19 -0
  156. package/drizzle/0005_silky_millenium_guard.sql +2 -0
  157. package/drizzle/0006_harsh_caretaker.sql +42 -0
  158. package/drizzle/0007_cloudy_wong.sql +1 -0
  159. package/drizzle/0008_broad_boomer.sql +2 -0
  160. package/drizzle/0009_strong_marten_broadcloak.sql +19 -0
  161. package/drizzle/0010_needy_bishop.sql +11 -0
  162. package/drizzle/0011_moaning_millenium_guard.sql +1 -0
  163. package/drizzle/0012_late_marten_broadcloak.sql +2 -0
  164. package/drizzle/0013_previous_dormammu.sql +9 -0
  165. package/drizzle/0014_lazy_captain_universe.sql +2 -0
  166. package/drizzle/0015_zippy_wallop.sql +29 -0
  167. package/drizzle/0016_jazzy_zemo.sql +2 -0
  168. package/drizzle/0017_reflective_praxagora.sql +4 -0
  169. package/drizzle/0018_fat_vanisher.sql +22 -0
  170. package/drizzle/0019_new_clint_barton.sql +8 -0
  171. package/drizzle/0020_skinny_maverick.sql +1 -0
  172. package/drizzle/0021_mysterious_madelyne_pryor.sql +13 -0
  173. package/drizzle/0022_sleepy_ultimo.sql +25 -0
  174. package/drizzle/0023_wooden_mandrill.sql +2 -0
  175. package/drizzle/AGENTS.md +68 -0
  176. package/drizzle/CLAUDE.md +1 -0
  177. package/drizzle/meta/0000_snapshot.json +221 -0
  178. package/drizzle/meta/0001_snapshot.json +214 -0
  179. package/drizzle/meta/0002_snapshot.json +221 -0
  180. package/drizzle/meta/0005_snapshot.json +369 -0
  181. package/drizzle/meta/0006_snapshot.json +638 -0
  182. package/drizzle/meta/0007_snapshot.json +640 -0
  183. package/drizzle/meta/0008_snapshot.json +649 -0
  184. package/drizzle/meta/0009_snapshot.json +554 -0
  185. package/drizzle/meta/0010_snapshot.json +619 -0
  186. package/drizzle/meta/0011_snapshot.json +627 -0
  187. package/drizzle/meta/0012_snapshot.json +639 -0
  188. package/drizzle/meta/0013_snapshot.json +717 -0
  189. package/drizzle/meta/0014_snapshot.json +717 -0
  190. package/drizzle/meta/0015_snapshot.json +897 -0
  191. package/drizzle/meta/0016_snapshot.json +1031 -0
  192. package/drizzle/meta/0018_snapshot.json +1210 -0
  193. package/drizzle/meta/0019_snapshot.json +1165 -0
  194. package/drizzle/meta/0020_snapshot.json +1232 -0
  195. package/drizzle/meta/0021_snapshot.json +1311 -0
  196. package/drizzle/meta/0022_snapshot.json +1481 -0
  197. package/drizzle/meta/0023_snapshot.json +1496 -0
  198. package/drizzle/meta/_journal.json +174 -0
  199. package/package.json +240 -0
@@ -0,0 +1,237 @@
1
+ import { t as __exportAll } from "./chunk-DEq-mXcV.js";
2
+ import { _ as getConfigDirectoryPath, b as getEnvInt, r as logger, v as getEnvBool, x as getEnvString } from "./logger-C40ZGil9.js";
3
+ import { d as sleep, h as REQUEST_TIMEOUT_MS, n as fetchWithRetries } from "./fetch-DQckpUFz.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { createCache } from "cache-manager";
7
+ import { Keyv } from "keyv";
8
+ import { KeyvFile } from "keyv-file";
9
+ //#region src/util/fetch/errors.ts
10
+ /**
11
+ * Non-transient HTTP status codes that indicate the target is unavailable or misconfigured.
12
+ * These errors will not resolve on retry and should abort the scan immediately.
13
+ *
14
+ * - 401: Unauthorized - authentication required or invalid credentials
15
+ * - 403: Forbidden - valid credentials but access denied
16
+ * - 404: Not Found - target endpoint doesn't exist
17
+ * - 501: Not Implemented - server doesn't support the request method
18
+ *
19
+ * Excluded: 500 (often transient — server crashes, DB timeouts, deployment rollouts,
20
+ * or input-dependent bugs where one prompt triggers it but the next doesn't),
21
+ * 502/503/504 (typically transient gateway issues).
22
+ */
23
+ const NON_TRANSIENT_HTTP_STATUSES = [
24
+ 401,
25
+ 403,
26
+ 404,
27
+ 501
28
+ ];
29
+ function isNonTransientHttpStatus(status) {
30
+ return NON_TRANSIENT_HTTP_STATUSES.includes(status);
31
+ }
32
+ function isTransientConnectionError(error) {
33
+ if (!error) return false;
34
+ const code = error.code;
35
+ if (code === "ECONNRESET" || code === "EPIPE") return true;
36
+ const message = (error.message ?? "").toLowerCase();
37
+ if (message.includes("eproto") && (message.includes("wrong version number") || message.includes("self signed") || message.includes("unable to verify") || message.includes("unknown ca") || message.includes("cert"))) return false;
38
+ return message.includes("bad record mac") || message.includes("eproto") || message.includes("econnreset") || message.includes("socket hang up");
39
+ }
40
+ //#endregion
41
+ //#region src/cache.ts
42
+ var cache_exports = /* @__PURE__ */ __exportAll({
43
+ clearCache: () => clearCache,
44
+ disableCache: () => disableCache,
45
+ enableCache: () => enableCache,
46
+ fetchWithCache: () => fetchWithCache,
47
+ getCache: () => getCache,
48
+ isCacheEnabled: () => isCacheEnabled
49
+ });
50
+ let cacheInstance;
51
+ let enabled = getEnvBool("PROMPTFOO_CACHE_ENABLED", true);
52
+ const cacheType = getEnvString("PROMPTFOO_CACHE_TYPE") || (getEnvString("NODE_ENV") === "test" ? "memory" : "disk");
53
+ /** Default cache TTL: 14 days in seconds */
54
+ const DEFAULT_CACHE_TTL_SECONDS = 3600 * 24 * 14;
55
+ /**
56
+ * Get the cache TTL in milliseconds.
57
+ * Reads from PROMPTFOO_CACHE_TTL environment variable (in seconds) or uses default.
58
+ */
59
+ function getCacheTtlMs() {
60
+ return getEnvInt("PROMPTFOO_CACHE_TTL", DEFAULT_CACHE_TTL_SECONDS) * 1e3;
61
+ }
62
+ function getCache() {
63
+ if (!cacheInstance) {
64
+ let cachePath = "";
65
+ const stores = [];
66
+ if (cacheType === "disk" && enabled) {
67
+ cachePath = getEnvString("PROMPTFOO_CACHE_PATH") || path.join(getConfigDirectoryPath(), "cache");
68
+ if (!fs.existsSync(cachePath)) {
69
+ logger.info(`Creating cache folder at ${cachePath}.`);
70
+ fs.mkdirSync(cachePath, { recursive: true });
71
+ }
72
+ const newCacheFile = path.join(cachePath, "cache.json");
73
+ try {
74
+ const keyv = new Keyv({
75
+ store: new KeyvFile({ filename: newCacheFile }),
76
+ ttl: getCacheTtlMs()
77
+ });
78
+ stores.push(keyv);
79
+ } catch (err) {
80
+ logger.warn(`[Cache] Failed to initialize disk cache: ${err.message}. Using memory cache instead.`);
81
+ }
82
+ }
83
+ cacheInstance = createCache({
84
+ stores,
85
+ ttl: getCacheTtlMs(),
86
+ refreshThreshold: 0
87
+ });
88
+ }
89
+ return cacheInstance;
90
+ }
91
+ const inflightFetchResponses = /* @__PURE__ */ new Map();
92
+ function serializeFetchResponse(data, status, statusText, headers, latencyMs) {
93
+ return JSON.stringify({
94
+ data,
95
+ status,
96
+ statusText,
97
+ headers,
98
+ latencyMs
99
+ });
100
+ }
101
+ function deserializeFetchResponse(response, cached, cache, cacheKey) {
102
+ const parsedResponse = JSON.parse(response);
103
+ return {
104
+ cached,
105
+ data: parsedResponse.data,
106
+ status: parsedResponse.status,
107
+ statusText: parsedResponse.statusText,
108
+ headers: parsedResponse.headers,
109
+ latencyMs: parsedResponse.latencyMs,
110
+ deleteFromCache: async () => {
111
+ await cache.del(cacheKey);
112
+ logger.debug(`Evicted from cache: ${cacheKey}`);
113
+ }
114
+ };
115
+ }
116
+ async function fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent) {
117
+ const maxBodyRetries = isIdempotent ? 2 : 0;
118
+ for (let bodyAttempt = 0; bodyAttempt <= maxBodyRetries; bodyAttempt++) {
119
+ const fetchStart = Date.now();
120
+ const resp = await fetchWithRetries(url, options, timeout, maxRetries);
121
+ const fetchLatencyMs = Date.now() - fetchStart;
122
+ try {
123
+ return {
124
+ respText: await resp.text(),
125
+ resp,
126
+ fetchLatencyMs
127
+ };
128
+ } catch (err) {
129
+ if (isTransientConnectionError(err) && bodyAttempt < maxBodyRetries) {
130
+ const backoffMs = Math.pow(2, bodyAttempt) * 1e3;
131
+ logger.debug("[Cache] Body stream failed with transient error, retrying", {
132
+ attempt: bodyAttempt + 1,
133
+ maxRetries: maxBodyRetries,
134
+ backoffMs,
135
+ error: err?.message?.slice(0, 200)
136
+ });
137
+ await sleep(backoffMs);
138
+ continue;
139
+ }
140
+ throw err;
141
+ }
142
+ }
143
+ throw new Error("Exhausted body retries without returning or throwing");
144
+ }
145
+ async function prepareFetchResponse(url, options, timeout, maxRetries, isIdempotent, format) {
146
+ const result = await fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent);
147
+ const response = result.resp;
148
+ const responseText = result.respText;
149
+ const fetchLatencyMs = result.fetchLatencyMs;
150
+ const headers = Object.fromEntries(response.headers.entries());
151
+ try {
152
+ const parsedData = format === "json" ? JSON.parse(responseText) : responseText;
153
+ const serializedResponse = serializeFetchResponse(parsedData, response.status, response.statusText, headers, fetchLatencyMs);
154
+ if (!response.ok) return {
155
+ response: responseText === "" ? serializeFetchResponse(`Empty Response: ${response.status}: ${response.statusText}`, response.status, response.statusText, headers, fetchLatencyMs) : serializedResponse,
156
+ cacheable: false
157
+ };
158
+ if (format === "json" && parsedData?.error) {
159
+ logger.debug(`Not caching ${url} because it contains an 'error' key: ${parsedData.error}`);
160
+ return {
161
+ response: serializedResponse,
162
+ cacheable: false
163
+ };
164
+ }
165
+ logger.debug(`Storing ${url} response in cache with latencyMs=${fetchLatencyMs}: ${serializedResponse}`);
166
+ return {
167
+ response: serializedResponse,
168
+ cacheable: true
169
+ };
170
+ } catch (err) {
171
+ throw new Error(`Error parsing response from ${url}: ${err.message}. Received text: ${responseText}`);
172
+ }
173
+ }
174
+ async function fetchWithCache(url, options = {}, timeout = REQUEST_TIMEOUT_MS, format = "json", bust = false, maxRetries) {
175
+ const method = (options.method ?? (url instanceof Request ? url.method : "GET")).toUpperCase();
176
+ const isIdempotent = [
177
+ "GET",
178
+ "HEAD",
179
+ "OPTIONS",
180
+ "PUT",
181
+ "DELETE"
182
+ ].includes(method);
183
+ if (!enabled || bust) {
184
+ const { respText, resp, fetchLatencyMs } = await fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent);
185
+ try {
186
+ return {
187
+ cached: false,
188
+ data: format === "json" ? JSON.parse(respText) : respText,
189
+ status: resp.status,
190
+ statusText: resp.statusText,
191
+ headers: Object.fromEntries(resp.headers.entries()),
192
+ latencyMs: fetchLatencyMs,
193
+ deleteFromCache: async () => {}
194
+ };
195
+ } catch {
196
+ throw new Error(`Error parsing response as JSON: ${respText}`);
197
+ }
198
+ }
199
+ const copy = Object.assign({}, options);
200
+ delete copy.headers;
201
+ const cacheKey = `fetch:v2:${url}:${JSON.stringify(copy)}`;
202
+ const cache = await getCache();
203
+ const cachedResponse = await cache.get(cacheKey);
204
+ if (cachedResponse != null) {
205
+ logger.debug(`Returning cached response for ${url}: ${cachedResponse}`);
206
+ return deserializeFetchResponse(cachedResponse, true, cache, cacheKey);
207
+ }
208
+ let inflightResponse = inflightFetchResponses.get(cacheKey);
209
+ if (!inflightResponse) {
210
+ inflightResponse = (async () => {
211
+ const preparedResponse = await prepareFetchResponse(url, options, timeout, maxRetries, isIdempotent, format);
212
+ if (preparedResponse.cacheable) await cache.set(cacheKey, preparedResponse.response);
213
+ return preparedResponse.response;
214
+ })().finally(() => {
215
+ inflightFetchResponses.delete(cacheKey);
216
+ });
217
+ inflightFetchResponses.set(cacheKey, inflightResponse);
218
+ }
219
+ return deserializeFetchResponse(await inflightResponse, false, cache, cacheKey);
220
+ }
221
+ function enableCache() {
222
+ enabled = true;
223
+ }
224
+ function disableCache() {
225
+ enabled = false;
226
+ }
227
+ async function clearCache() {
228
+ inflightFetchResponses.clear();
229
+ return getCache().clear();
230
+ }
231
+ function isCacheEnabled() {
232
+ return enabled;
233
+ }
234
+ //#endregion
235
+ export { isCacheEnabled as a, isTransientConnectionError as c, getCache as i, disableCache as n, NON_TRANSIENT_HTTP_STATUSES as o, fetchWithCache as r, isNonTransientHttpStatus as s, cache_exports as t };
236
+
237
+ //# sourceMappingURL=cache-BkrqU5Ba.js.map
@@ -0,0 +1,297 @@
1
+ const require_logger = require("./logger-DyfK9PBt.cjs");
2
+ const require_fetch = require("./fetch-BTxakTSg.cjs");
3
+ let fs = require("fs");
4
+ fs = require_logger.__toESM(fs);
5
+ let path = require("path");
6
+ path = require_logger.__toESM(path);
7
+ let cache_manager = require("cache-manager");
8
+ let keyv = require("keyv");
9
+ let keyv_file = require("keyv-file");
10
+ //#region src/util/fetch/errors.ts
11
+ /**
12
+ * Non-transient HTTP status codes that indicate the target is unavailable or misconfigured.
13
+ * These errors will not resolve on retry and should abort the scan immediately.
14
+ *
15
+ * - 401: Unauthorized - authentication required or invalid credentials
16
+ * - 403: Forbidden - valid credentials but access denied
17
+ * - 404: Not Found - target endpoint doesn't exist
18
+ * - 501: Not Implemented - server doesn't support the request method
19
+ *
20
+ * Excluded: 500 (often transient — server crashes, DB timeouts, deployment rollouts,
21
+ * or input-dependent bugs where one prompt triggers it but the next doesn't),
22
+ * 502/503/504 (typically transient gateway issues).
23
+ */
24
+ const NON_TRANSIENT_HTTP_STATUSES = [
25
+ 401,
26
+ 403,
27
+ 404,
28
+ 501
29
+ ];
30
+ function isNonTransientHttpStatus(status) {
31
+ return NON_TRANSIENT_HTTP_STATUSES.includes(status);
32
+ }
33
+ function isTransientConnectionError(error) {
34
+ if (!error) return false;
35
+ const code = error.code;
36
+ if (code === "ECONNRESET" || code === "EPIPE") return true;
37
+ const message = (error.message ?? "").toLowerCase();
38
+ if (message.includes("eproto") && (message.includes("wrong version number") || message.includes("self signed") || message.includes("unable to verify") || message.includes("unknown ca") || message.includes("cert"))) return false;
39
+ return message.includes("bad record mac") || message.includes("eproto") || message.includes("econnreset") || message.includes("socket hang up");
40
+ }
41
+ //#endregion
42
+ //#region src/cache.ts
43
+ var cache_exports = /* @__PURE__ */ require_logger.__exportAll({
44
+ clearCache: () => clearCache,
45
+ disableCache: () => disableCache,
46
+ enableCache: () => enableCache,
47
+ fetchWithCache: () => fetchWithCache,
48
+ getCache: () => getCache,
49
+ isCacheEnabled: () => isCacheEnabled
50
+ });
51
+ let cacheInstance;
52
+ let enabled = require_logger.getEnvBool("PROMPTFOO_CACHE_ENABLED", true);
53
+ const cacheType = require_logger.getEnvString("PROMPTFOO_CACHE_TYPE") || (require_logger.getEnvString("NODE_ENV") === "test" ? "memory" : "disk");
54
+ /** Default cache TTL: 14 days in seconds */
55
+ const DEFAULT_CACHE_TTL_SECONDS = 3600 * 24 * 14;
56
+ /**
57
+ * Get the cache TTL in milliseconds.
58
+ * Reads from PROMPTFOO_CACHE_TTL environment variable (in seconds) or uses default.
59
+ */
60
+ function getCacheTtlMs() {
61
+ return require_logger.getEnvInt("PROMPTFOO_CACHE_TTL", DEFAULT_CACHE_TTL_SECONDS) * 1e3;
62
+ }
63
+ function getCache() {
64
+ if (!cacheInstance) {
65
+ let cachePath = "";
66
+ const stores = [];
67
+ if (cacheType === "disk" && enabled) {
68
+ cachePath = require_logger.getEnvString("PROMPTFOO_CACHE_PATH") || path.default.join(require_logger.getConfigDirectoryPath(), "cache");
69
+ if (!fs.default.existsSync(cachePath)) {
70
+ require_logger.logger.info(`Creating cache folder at ${cachePath}.`);
71
+ fs.default.mkdirSync(cachePath, { recursive: true });
72
+ }
73
+ const newCacheFile = path.default.join(cachePath, "cache.json");
74
+ try {
75
+ const keyv$1 = new keyv.Keyv({
76
+ store: new keyv_file.KeyvFile({ filename: newCacheFile }),
77
+ ttl: getCacheTtlMs()
78
+ });
79
+ stores.push(keyv$1);
80
+ } catch (err) {
81
+ require_logger.logger.warn(`[Cache] Failed to initialize disk cache: ${err.message}. Using memory cache instead.`);
82
+ }
83
+ }
84
+ cacheInstance = (0, cache_manager.createCache)({
85
+ stores,
86
+ ttl: getCacheTtlMs(),
87
+ refreshThreshold: 0
88
+ });
89
+ }
90
+ return cacheInstance;
91
+ }
92
+ const inflightFetchResponses = /* @__PURE__ */ new Map();
93
+ function serializeFetchResponse(data, status, statusText, headers, latencyMs) {
94
+ return JSON.stringify({
95
+ data,
96
+ status,
97
+ statusText,
98
+ headers,
99
+ latencyMs
100
+ });
101
+ }
102
+ function deserializeFetchResponse(response, cached, cache, cacheKey) {
103
+ const parsedResponse = JSON.parse(response);
104
+ return {
105
+ cached,
106
+ data: parsedResponse.data,
107
+ status: parsedResponse.status,
108
+ statusText: parsedResponse.statusText,
109
+ headers: parsedResponse.headers,
110
+ latencyMs: parsedResponse.latencyMs,
111
+ deleteFromCache: async () => {
112
+ await cache.del(cacheKey);
113
+ require_logger.logger.debug(`Evicted from cache: ${cacheKey}`);
114
+ }
115
+ };
116
+ }
117
+ async function fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent) {
118
+ const maxBodyRetries = isIdempotent ? 2 : 0;
119
+ for (let bodyAttempt = 0; bodyAttempt <= maxBodyRetries; bodyAttempt++) {
120
+ const fetchStart = Date.now();
121
+ const resp = await require_fetch.fetchWithRetries(url, options, timeout, maxRetries);
122
+ const fetchLatencyMs = Date.now() - fetchStart;
123
+ try {
124
+ return {
125
+ respText: await resp.text(),
126
+ resp,
127
+ fetchLatencyMs
128
+ };
129
+ } catch (err) {
130
+ if (isTransientConnectionError(err) && bodyAttempt < maxBodyRetries) {
131
+ const backoffMs = Math.pow(2, bodyAttempt) * 1e3;
132
+ require_logger.logger.debug("[Cache] Body stream failed with transient error, retrying", {
133
+ attempt: bodyAttempt + 1,
134
+ maxRetries: maxBodyRetries,
135
+ backoffMs,
136
+ error: err?.message?.slice(0, 200)
137
+ });
138
+ await require_fetch.sleep(backoffMs);
139
+ continue;
140
+ }
141
+ throw err;
142
+ }
143
+ }
144
+ throw new Error("Exhausted body retries without returning or throwing");
145
+ }
146
+ async function prepareFetchResponse(url, options, timeout, maxRetries, isIdempotent, format) {
147
+ const result = await fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent);
148
+ const response = result.resp;
149
+ const responseText = result.respText;
150
+ const fetchLatencyMs = result.fetchLatencyMs;
151
+ const headers = Object.fromEntries(response.headers.entries());
152
+ try {
153
+ const parsedData = format === "json" ? JSON.parse(responseText) : responseText;
154
+ const serializedResponse = serializeFetchResponse(parsedData, response.status, response.statusText, headers, fetchLatencyMs);
155
+ if (!response.ok) return {
156
+ response: responseText === "" ? serializeFetchResponse(`Empty Response: ${response.status}: ${response.statusText}`, response.status, response.statusText, headers, fetchLatencyMs) : serializedResponse,
157
+ cacheable: false
158
+ };
159
+ if (format === "json" && parsedData?.error) {
160
+ require_logger.logger.debug(`Not caching ${url} because it contains an 'error' key: ${parsedData.error}`);
161
+ return {
162
+ response: serializedResponse,
163
+ cacheable: false
164
+ };
165
+ }
166
+ require_logger.logger.debug(`Storing ${url} response in cache with latencyMs=${fetchLatencyMs}: ${serializedResponse}`);
167
+ return {
168
+ response: serializedResponse,
169
+ cacheable: true
170
+ };
171
+ } catch (err) {
172
+ throw new Error(`Error parsing response from ${url}: ${err.message}. Received text: ${responseText}`);
173
+ }
174
+ }
175
+ async function fetchWithCache(url, options = {}, timeout = require_fetch.REQUEST_TIMEOUT_MS, format = "json", bust = false, maxRetries) {
176
+ const method = (options.method ?? (url instanceof Request ? url.method : "GET")).toUpperCase();
177
+ const isIdempotent = [
178
+ "GET",
179
+ "HEAD",
180
+ "OPTIONS",
181
+ "PUT",
182
+ "DELETE"
183
+ ].includes(method);
184
+ if (!enabled || bust) {
185
+ const { respText, resp, fetchLatencyMs } = await fetchAndReadBody(url, options, timeout, maxRetries, isIdempotent);
186
+ try {
187
+ return {
188
+ cached: false,
189
+ data: format === "json" ? JSON.parse(respText) : respText,
190
+ status: resp.status,
191
+ statusText: resp.statusText,
192
+ headers: Object.fromEntries(resp.headers.entries()),
193
+ latencyMs: fetchLatencyMs,
194
+ deleteFromCache: async () => {}
195
+ };
196
+ } catch {
197
+ throw new Error(`Error parsing response as JSON: ${respText}`);
198
+ }
199
+ }
200
+ const copy = Object.assign({}, options);
201
+ delete copy.headers;
202
+ const cacheKey = `fetch:v2:${url}:${JSON.stringify(copy)}`;
203
+ const cache = await getCache();
204
+ const cachedResponse = await cache.get(cacheKey);
205
+ if (cachedResponse != null) {
206
+ require_logger.logger.debug(`Returning cached response for ${url}: ${cachedResponse}`);
207
+ return deserializeFetchResponse(cachedResponse, true, cache, cacheKey);
208
+ }
209
+ let inflightResponse = inflightFetchResponses.get(cacheKey);
210
+ if (!inflightResponse) {
211
+ inflightResponse = (async () => {
212
+ const preparedResponse = await prepareFetchResponse(url, options, timeout, maxRetries, isIdempotent, format);
213
+ if (preparedResponse.cacheable) await cache.set(cacheKey, preparedResponse.response);
214
+ return preparedResponse.response;
215
+ })().finally(() => {
216
+ inflightFetchResponses.delete(cacheKey);
217
+ });
218
+ inflightFetchResponses.set(cacheKey, inflightResponse);
219
+ }
220
+ return deserializeFetchResponse(await inflightResponse, false, cache, cacheKey);
221
+ }
222
+ function enableCache() {
223
+ enabled = true;
224
+ }
225
+ function disableCache() {
226
+ enabled = false;
227
+ }
228
+ async function clearCache() {
229
+ inflightFetchResponses.clear();
230
+ return getCache().clear();
231
+ }
232
+ function isCacheEnabled() {
233
+ return enabled;
234
+ }
235
+ //#endregion
236
+ Object.defineProperty(exports, "NON_TRANSIENT_HTTP_STATUSES", {
237
+ enumerable: true,
238
+ get: function() {
239
+ return NON_TRANSIENT_HTTP_STATUSES;
240
+ }
241
+ });
242
+ Object.defineProperty(exports, "cache_exports", {
243
+ enumerable: true,
244
+ get: function() {
245
+ return cache_exports;
246
+ }
247
+ });
248
+ Object.defineProperty(exports, "clearCache", {
249
+ enumerable: true,
250
+ get: function() {
251
+ return clearCache;
252
+ }
253
+ });
254
+ Object.defineProperty(exports, "disableCache", {
255
+ enumerable: true,
256
+ get: function() {
257
+ return disableCache;
258
+ }
259
+ });
260
+ Object.defineProperty(exports, "enableCache", {
261
+ enumerable: true,
262
+ get: function() {
263
+ return enableCache;
264
+ }
265
+ });
266
+ Object.defineProperty(exports, "fetchWithCache", {
267
+ enumerable: true,
268
+ get: function() {
269
+ return fetchWithCache;
270
+ }
271
+ });
272
+ Object.defineProperty(exports, "getCache", {
273
+ enumerable: true,
274
+ get: function() {
275
+ return getCache;
276
+ }
277
+ });
278
+ Object.defineProperty(exports, "isCacheEnabled", {
279
+ enumerable: true,
280
+ get: function() {
281
+ return isCacheEnabled;
282
+ }
283
+ });
284
+ Object.defineProperty(exports, "isNonTransientHttpStatus", {
285
+ enumerable: true,
286
+ get: function() {
287
+ return isNonTransientHttpStatus;
288
+ }
289
+ });
290
+ Object.defineProperty(exports, "isTransientConnectionError", {
291
+ enumerable: true,
292
+ get: function() {
293
+ return isTransientConnectionError;
294
+ }
295
+ });
296
+
297
+ //# sourceMappingURL=cache-DsCxFlsZ.cjs.map