@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.
- package/LICENSE +19 -0
- package/dist/src/accounts-Bt1oJb1Z.cjs +219 -0
- package/dist/src/accounts-DjOU8Rm3.js +178 -0
- package/dist/src/agentic-utils-D03IiXQc.js +153 -0
- package/dist/src/agentic-utils-Dh7xaMQM.cjs +180 -0
- package/dist/src/agents-C6BIMlZa.js +231 -0
- package/dist/src/agents-DvIpNX1L.cjs +666 -0
- package/dist/src/agents-ZP0RP9vV.cjs +231 -0
- package/dist/src/agents-maJXdjbR.js +665 -0
- package/dist/src/aimlapi-BTbQjG2E.cjs +30 -0
- package/dist/src/aimlapi-CwMxqfXP.js +30 -0
- package/dist/src/audio-BBUdvsde.cjs +97 -0
- package/dist/src/audio-D5DPZ7I-.js +97 -0
- package/dist/src/base-BEysXrkq.cjs +222 -0
- package/dist/src/base-C451JQfq.js +193 -0
- package/dist/src/blobs-BY8MDmpo.js +230 -0
- package/dist/src/blobs-BgcNn97m.cjs +256 -0
- package/dist/src/cache-BBE_lsTA.cjs +4 -0
- package/dist/src/cache-BkrqU5Ba.js +237 -0
- package/dist/src/cache-DsCxFlsZ.cjs +297 -0
- package/dist/src/chat-CPJWDP6a.cjs +289 -0
- package/dist/src/chat-CXX3xzkk.cjs +811 -0
- package/dist/src/chat-CcDgZFJ4.js +787 -0
- package/dist/src/chat-Dz5ZeGO2.js +289 -0
- package/dist/src/chatkit-Dw0mKkML.cjs +1158 -0
- package/dist/src/chatkit-swAIVuea.js +1157 -0
- package/dist/src/chunk-DEq-mXcV.js +15 -0
- package/dist/src/claude-agent-sdk-BXZJtOg6.js +379 -0
- package/dist/src/claude-agent-sdk-CkfyjDoG.cjs +383 -0
- package/dist/src/cloudflare-ai-BzpJcqUH.js +161 -0
- package/dist/src/cloudflare-ai-Cmy_R1y2.cjs +161 -0
- package/dist/src/cloudflare-gateway-B9tVQKok.cjs +272 -0
- package/dist/src/cloudflare-gateway-DrD3ew3H.js +272 -0
- package/dist/src/codex-sdk-Dezj9Nwm.js +1056 -0
- package/dist/src/codex-sdk-Dl9D4k5B.cjs +1060 -0
- package/dist/src/cometapi-C-9YvCHC.js +54 -0
- package/dist/src/cometapi-DHgDKoO2.cjs +54 -0
- package/dist/src/completion-B8Ctyxpr.js +120 -0
- package/dist/src/completion-Cxrt08sj.cjs +131 -0
- package/dist/src/createHash-BwgE13yv.cjs +27 -0
- package/dist/src/createHash-DmPQkvBh.js +15 -0
- package/dist/src/docker-BiqcTwLv.js +80 -0
- package/dist/src/docker-C7tEJnP-.cjs +80 -0
- package/dist/src/esm-C62Zofr1.cjs +409 -0
- package/dist/src/esm-DMVc93eh.js +379 -0
- package/dist/src/evalResult-C3NJPQOo.cjs +301 -0
- package/dist/src/evalResult-C7JJAPBb.js +295 -0
- package/dist/src/evalResult-DoVTZZWI.cjs +2 -0
- package/dist/src/extractor-DnMD3fwt.cjs +391 -0
- package/dist/src/extractor-DtlL28vL.js +374 -0
- package/dist/src/fetch-BTxakTSg.cjs +1133 -0
- package/dist/src/fetch-DQckpUFz.js +928 -0
- package/dist/src/fileExtensions-DnqA1y9x.js +85 -0
- package/dist/src/fileExtensions-bYh77CN8.cjs +114 -0
- package/dist/src/genaiTracer-CyZrmaK0.cjs +268 -0
- package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
- package/dist/src/graders-BNscxFrU.js +13644 -0
- package/dist/src/graders-D2oE9Msq.js +2 -0
- package/dist/src/graders-c0Ez_w-9.cjs +2 -0
- package/dist/src/graders-d0F2M3e9.cjs +14056 -0
- package/dist/src/image-0ZhE0VlR.cjs +280 -0
- package/dist/src/image-CWE1pdNv.js +257 -0
- package/dist/src/image-D9ZK6hwL.js +163 -0
- package/dist/src/image-DKZgZITg.cjs +163 -0
- package/dist/src/index.cjs +11366 -0
- package/dist/src/index.d.cts +19640 -0
- package/dist/src/index.d.ts +19641 -0
- package/dist/src/index.js +11306 -0
- package/dist/src/invariant-Ddh24eXh.js +25 -0
- package/dist/src/invariant-kfQ8Bu82.cjs +30 -0
- package/dist/src/knowledgeBase-BgPyGFUd.cjs +122 -0
- package/dist/src/knowledgeBase-DyHilYaP.js +122 -0
- package/dist/src/litellm-CyMeneHS.js +135 -0
- package/dist/src/litellm-DWDF73yF.cjs +135 -0
- package/dist/src/logger-C40ZGil9.js +717 -0
- package/dist/src/logger-DyfK9PBt.cjs +917 -0
- package/dist/src/luma-ray-BAU9X_ep.cjs +315 -0
- package/dist/src/luma-ray-nwVseBbv.js +313 -0
- package/dist/src/messages-B5ADWTTv.js +245 -0
- package/dist/src/messages-BCnZfqrS.cjs +257 -0
- package/dist/src/meteor-DLZZ3osF.cjs +134 -0
- package/dist/src/meteor-DUiCJRC-.js +134 -0
- package/dist/src/modelslab-00cveB8L.cjs +163 -0
- package/dist/src/modelslab-D9sCU_L7.js +163 -0
- package/dist/src/nova-reel-CTapvqYH.js +276 -0
- package/dist/src/nova-reel-DlWuuroF.cjs +278 -0
- package/dist/src/nova-sonic-5UPWfeMv.cjs +363 -0
- package/dist/src/nova-sonic-BhSwQNym.js +363 -0
- package/dist/src/openai-BWrJK9d8.cjs +52 -0
- package/dist/src/openai-DumO8WQn.js +47 -0
- package/dist/src/openclaw-B8brrjC_.cjs +577 -0
- package/dist/src/openclaw-Bkayww9q.js +571 -0
- package/dist/src/opencode-sdk-7xjoDNiM.cjs +562 -0
- package/dist/src/opencode-sdk-SGwAPxht.js +558 -0
- package/dist/src/otlpReceiver-CoAHfAN9.cjs +15 -0
- package/dist/src/otlpReceiver-oO3EQwI9.js +14 -0
- package/dist/src/providerRegistry-4yjhaEM8.js +45 -0
- package/dist/src/providerRegistry-DhV4rJIc.cjs +50 -0
- package/dist/src/providers-B5RJVG-7.cjs +33609 -0
- package/dist/src/providers-BdmZCLzV.js +33262 -0
- package/dist/src/providers-CxtRxn8e.js +2 -0
- package/dist/src/providers-DnQLNbx1.cjs +3 -0
- package/dist/src/pythonUtils-BD0druiM.cjs +275 -0
- package/dist/src/pythonUtils-IBhn5YGR.js +249 -0
- package/dist/src/quiverai-BDOwZBsM.cjs +213 -0
- package/dist/src/quiverai-D3JTF5lD.js +213 -0
- package/dist/src/responses-B2LCDCXZ.js +667 -0
- package/dist/src/responses-BvNm4Xv9.cjs +685 -0
- package/dist/src/rubyUtils-B0NwnfpY.cjs +245 -0
- package/dist/src/rubyUtils-BroxzZ7c.cjs +2 -0
- package/dist/src/rubyUtils-hqVw5UvJ.js +222 -0
- package/dist/src/sagemaker-Cno2V-Sx.js +689 -0
- package/dist/src/sagemaker-fV_KUgs5.cjs +691 -0
- package/dist/src/server-BOuAXb06.cjs +238 -0
- package/dist/src/server-CtI-EWzm.cjs +2 -0
- package/dist/src/server-Cy3DZymt.js +189 -0
- package/dist/src/slack-CP8xBePa.js +135 -0
- package/dist/src/slack-DSQ1yXVb.cjs +135 -0
- package/dist/src/store-BwDDaBjb.cjs +246 -0
- package/dist/src/store-DcbLC593.cjs +2 -0
- package/dist/src/store-IGpqMIkv.js +240 -0
- package/dist/src/tables-3Q2cL7So.cjs +373 -0
- package/dist/src/tables-Bi2fjr4W.js +288 -0
- package/dist/src/telemetry-Bg2WqF79.js +161 -0
- package/dist/src/telemetry-D0x6u5kX.cjs +166 -0
- package/dist/src/telemetry-DXNimrI0.cjs +2 -0
- package/dist/src/text-B_UCRPp2.js +22 -0
- package/dist/src/text-CW1cyrwj.cjs +33 -0
- package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
- package/dist/src/tokenUsageUtils-bVa1ga6f.cjs +173 -0
- package/dist/src/transcription-Cl_W16Pr.js +122 -0
- package/dist/src/transcription-yt1EecY8.cjs +124 -0
- package/dist/src/transform-BCtGrl_W.cjs +228 -0
- package/dist/src/transform-Bv6gG2MJ.cjs +1688 -0
- package/dist/src/transform-CY1wbpRy.js +1507 -0
- package/dist/src/transform-DU8rUL9P.cjs +2 -0
- package/dist/src/transform-yWaShiKr.js +216 -0
- package/dist/src/transformersAvailability-BGkzavwb.js +35 -0
- package/dist/src/transformersAvailability-DKoRtQLy.cjs +35 -0
- package/dist/src/types-5aqHpBwE.cjs +3769 -0
- package/dist/src/types-Bn6D9c4U.js +3300 -0
- package/dist/src/util-BkKlTkI2.js +293 -0
- package/dist/src/util-CTh0bfOm.cjs +1119 -0
- package/dist/src/util-D17oBwo7.cjs +328 -0
- package/dist/src/util-DsS_-v4p.js +613 -0
- package/dist/src/util-DuntT1Ga.js +951 -0
- package/dist/src/util-aWjdCYMI.cjs +667 -0
- package/dist/src/utils-CisQwpjA.js +94 -0
- package/dist/src/utils-yWamDvmz.cjs +123 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/drizzle/0000_lush_hellion.sql +36 -0
- package/drizzle/0001_wide_calypso.sql +3 -0
- package/drizzle/0002_tidy_juggernaut.sql +1 -0
- package/drizzle/0003_lively_naoko.sql +8 -0
- package/drizzle/0004_minor_peter_quill.sql +19 -0
- package/drizzle/0005_silky_millenium_guard.sql +2 -0
- package/drizzle/0006_harsh_caretaker.sql +42 -0
- package/drizzle/0007_cloudy_wong.sql +1 -0
- package/drizzle/0008_broad_boomer.sql +2 -0
- package/drizzle/0009_strong_marten_broadcloak.sql +19 -0
- package/drizzle/0010_needy_bishop.sql +11 -0
- package/drizzle/0011_moaning_millenium_guard.sql +1 -0
- package/drizzle/0012_late_marten_broadcloak.sql +2 -0
- package/drizzle/0013_previous_dormammu.sql +9 -0
- package/drizzle/0014_lazy_captain_universe.sql +2 -0
- package/drizzle/0015_zippy_wallop.sql +29 -0
- package/drizzle/0016_jazzy_zemo.sql +2 -0
- package/drizzle/0017_reflective_praxagora.sql +4 -0
- package/drizzle/0018_fat_vanisher.sql +22 -0
- package/drizzle/0019_new_clint_barton.sql +8 -0
- package/drizzle/0020_skinny_maverick.sql +1 -0
- package/drizzle/0021_mysterious_madelyne_pryor.sql +13 -0
- package/drizzle/0022_sleepy_ultimo.sql +25 -0
- package/drizzle/0023_wooden_mandrill.sql +2 -0
- package/drizzle/AGENTS.md +68 -0
- package/drizzle/CLAUDE.md +1 -0
- package/drizzle/meta/0000_snapshot.json +221 -0
- package/drizzle/meta/0001_snapshot.json +214 -0
- package/drizzle/meta/0002_snapshot.json +221 -0
- package/drizzle/meta/0005_snapshot.json +369 -0
- package/drizzle/meta/0006_snapshot.json +638 -0
- package/drizzle/meta/0007_snapshot.json +640 -0
- package/drizzle/meta/0008_snapshot.json +649 -0
- package/drizzle/meta/0009_snapshot.json +554 -0
- package/drizzle/meta/0010_snapshot.json +619 -0
- package/drizzle/meta/0011_snapshot.json +627 -0
- package/drizzle/meta/0012_snapshot.json +639 -0
- package/drizzle/meta/0013_snapshot.json +717 -0
- package/drizzle/meta/0014_snapshot.json +717 -0
- package/drizzle/meta/0015_snapshot.json +897 -0
- package/drizzle/meta/0016_snapshot.json +1031 -0
- package/drizzle/meta/0018_snapshot.json +1210 -0
- package/drizzle/meta/0019_snapshot.json +1165 -0
- package/drizzle/meta/0020_snapshot.json +1232 -0
- package/drizzle/meta/0021_snapshot.json +1311 -0
- package/drizzle/meta/0022_snapshot.json +1481 -0
- package/drizzle/meta/0023_snapshot.json +1496 -0
- package/drizzle/meta/_journal.json +174 -0
- package/package.json +240 -0
|
@@ -0,0 +1,1133 @@
|
|
|
1
|
+
const require_logger = require("./logger-DyfK9PBt.cjs");
|
|
2
|
+
const require_invariant = require("./invariant-kfQ8Bu82.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 js_yaml = require("js-yaml");
|
|
8
|
+
js_yaml = require_logger.__toESM(js_yaml);
|
|
9
|
+
let util = require("util");
|
|
10
|
+
let fs_promises = require("fs/promises");
|
|
11
|
+
fs_promises = require_logger.__toESM(fs_promises);
|
|
12
|
+
let proxy_from_env = require("proxy-from-env");
|
|
13
|
+
let undici = require("undici");
|
|
14
|
+
let zlib = require("zlib");
|
|
15
|
+
//#region src/providers/constants.ts
|
|
16
|
+
const FILE_METADATA_KEY = "_promptfooFileMetadata";
|
|
17
|
+
/**
|
|
18
|
+
* Identifier for manual user ratings in componentResults.
|
|
19
|
+
* Used to distinguish human ratings from automated assertions.
|
|
20
|
+
*/
|
|
21
|
+
const HUMAN_ASSERTION_TYPE = "human";
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/version.ts
|
|
24
|
+
/**
|
|
25
|
+
* Application version from package.json.
|
|
26
|
+
* Injected at build time, or read from npm environment in development.
|
|
27
|
+
*/
|
|
28
|
+
const VERSION = "0.121.3";
|
|
29
|
+
function getShareApiBaseUrl() {
|
|
30
|
+
return require_logger.getEnvString("PROMPTFOO_REMOTE_API_BASE_URL") || "https://api.promptfoo.app";
|
|
31
|
+
}
|
|
32
|
+
const TERMINAL_MAX_WIDTH = process?.stdout?.isTTY && process?.stdout?.columns && process?.stdout?.columns > 10 ? process?.stdout?.columns - 10 : 120;
|
|
33
|
+
const CLOUD_PROVIDER_PREFIX = "promptfoo://provider/";
|
|
34
|
+
const CONSENT_ENDPOINT = "https://api.promptfoo.dev/consent";
|
|
35
|
+
const EVENTS_ENDPOINT = "https://a.promptfoo.app";
|
|
36
|
+
const R_ENDPOINT = "https://r.promptfoo.app/";
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/providers/shared.ts
|
|
39
|
+
/**
|
|
40
|
+
* The default timeout for API requests in milliseconds.
|
|
41
|
+
*/
|
|
42
|
+
const REQUEST_TIMEOUT_MS = require_logger.getEnvInt("REQUEST_TIMEOUT_MS", 3e5);
|
|
43
|
+
/**
|
|
44
|
+
* Extended timeout for long-running models (deep research, gpt-5-pro, etc.) in milliseconds.
|
|
45
|
+
* These models can take significantly longer to respond due to their complex reasoning.
|
|
46
|
+
*/
|
|
47
|
+
const LONG_RUNNING_MODEL_TIMEOUT_MS = 6e5;
|
|
48
|
+
/**
|
|
49
|
+
* Calculates the cost of an API call based on the model and token usage.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} modelName The name of the model used.
|
|
52
|
+
* @param {ProviderConfig} config The provider configuration.
|
|
53
|
+
* @param {number | undefined} promptTokens The number of tokens in the prompt.
|
|
54
|
+
* @param {number | undefined} completionTokens The number of tokens in the completion.
|
|
55
|
+
* @param {ProviderModel[]} models An array of available models with their costs.
|
|
56
|
+
* @returns {number | undefined} The calculated cost, or undefined if it can't be calculated.
|
|
57
|
+
*/
|
|
58
|
+
function calculateCost(modelName, config, promptTokens, completionTokens, models) {
|
|
59
|
+
if (!Number.isFinite(promptTokens) || !Number.isFinite(completionTokens) || typeof promptTokens === "undefined" || typeof completionTokens === "undefined") return;
|
|
60
|
+
const model = models.find((m) => m.id === modelName);
|
|
61
|
+
if (!model || !model.cost) return;
|
|
62
|
+
const inputCost = config.cost ?? model.cost.input;
|
|
63
|
+
const outputCost = config.cost ?? model.cost.output;
|
|
64
|
+
return inputCost * promptTokens + outputCost * completionTokens;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Checks if a string looks like it's attempting to be JSON.
|
|
68
|
+
* This helps distinguish between actual JSON attempts and plain text that happens to start/end with brackets.
|
|
69
|
+
*/
|
|
70
|
+
function looksLikeJson(prompt) {
|
|
71
|
+
const trimmed = prompt.trim();
|
|
72
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return true;
|
|
73
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
74
|
+
const afterBracket = trimmed.slice(1).trimStart();
|
|
75
|
+
if (afterBracket.startsWith("\"") || afterBracket.startsWith("{") || afterBracket.startsWith("[") || /^[\d-]/.test(afterBracket) || /^(true|false|null)/.test(afterBracket)) return true;
|
|
76
|
+
if (afterBracket.length === 0 || /^\s+$/.test(afterBracket)) return true;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Parses a chat prompt string into a structured format.
|
|
83
|
+
*
|
|
84
|
+
* @template T The expected return type of the parsed prompt.
|
|
85
|
+
* @param {string} prompt The input prompt string to parse.
|
|
86
|
+
* @param {T} defaultValue The default value to return if parsing fails.
|
|
87
|
+
* @returns {T} The parsed prompt or the default value.
|
|
88
|
+
* @throws {Error} If the prompt is invalid YAML or JSON (when required).
|
|
89
|
+
*/
|
|
90
|
+
function parseChatPrompt(prompt, defaultValue) {
|
|
91
|
+
const trimmedPrompt = prompt.trim();
|
|
92
|
+
if (trimmedPrompt.startsWith("- role:")) try {
|
|
93
|
+
return js_yaml.default.load(prompt);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
throw new Error(`Chat Completion prompt is not a valid YAML string: ${err}\n\n${prompt}`);
|
|
96
|
+
}
|
|
97
|
+
else try {
|
|
98
|
+
return JSON.parse(prompt);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
if (require_logger.getEnvBool("PROMPTFOO_REQUIRE_JSON_PROMPTS") || looksLikeJson(trimmedPrompt)) throw new Error(`Chat Completion prompt is not a valid JSON string: ${err}\n\n${prompt}`);
|
|
101
|
+
return defaultValue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Converts a string to title case.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} str The input string to convert.
|
|
108
|
+
* @returns {string} The input string converted to title case.
|
|
109
|
+
*/
|
|
110
|
+
function toTitleCase(str) {
|
|
111
|
+
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
112
|
+
}
|
|
113
|
+
function isPromptfooSampleTarget(provider) {
|
|
114
|
+
const url = provider.config?.url;
|
|
115
|
+
return url?.includes("promptfoo.app") || url?.includes("promptfoo.dev");
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Checks if the given value is an OpenAI tool choice format.
|
|
119
|
+
* Detects string values ('auto', 'none', 'required') and
|
|
120
|
+
* the object form ({ type: 'function', function: { name } }).
|
|
121
|
+
*/
|
|
122
|
+
function isOpenAIToolChoice(obj) {
|
|
123
|
+
if (typeof obj === "string") return [
|
|
124
|
+
"auto",
|
|
125
|
+
"none",
|
|
126
|
+
"required"
|
|
127
|
+
].includes(obj);
|
|
128
|
+
if (typeof obj === "object" && obj !== null) {
|
|
129
|
+
const candidate = obj;
|
|
130
|
+
if (candidate.type === "function" && typeof candidate.function === "object" && candidate.function !== null) return typeof candidate.function.name === "string";
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Transforms an OpenAI tool choice to Anthropic format.
|
|
136
|
+
*/
|
|
137
|
+
function openaiToolChoiceToAnthropic(choice) {
|
|
138
|
+
if (typeof choice === "string") switch (choice) {
|
|
139
|
+
case "auto": return { type: "auto" };
|
|
140
|
+
case "none": return { type: "auto" };
|
|
141
|
+
case "required": return { type: "any" };
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
type: "tool",
|
|
145
|
+
name: choice.function.name
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Transforms an OpenAI tool choice to Bedrock Converse format.
|
|
150
|
+
*/
|
|
151
|
+
function openaiToolChoiceToBedrock(choice) {
|
|
152
|
+
if (typeof choice === "string") switch (choice) {
|
|
153
|
+
case "auto": return { auto: {} };
|
|
154
|
+
case "none": return;
|
|
155
|
+
case "required": return { any: {} };
|
|
156
|
+
}
|
|
157
|
+
return { tool: { name: choice.function.name } };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Transforms an OpenAI tool choice to Google (Gemini) format.
|
|
161
|
+
*/
|
|
162
|
+
function openaiToolChoiceToGoogle(choice) {
|
|
163
|
+
if (typeof choice === "string") switch (choice) {
|
|
164
|
+
case "auto": return { functionCallingConfig: { mode: "AUTO" } };
|
|
165
|
+
case "none": return { functionCallingConfig: { mode: "NONE" } };
|
|
166
|
+
case "required": return { functionCallingConfig: { mode: "ANY" } };
|
|
167
|
+
}
|
|
168
|
+
return { functionCallingConfig: {
|
|
169
|
+
mode: "ANY",
|
|
170
|
+
allowedFunctionNames: [choice.function.name]
|
|
171
|
+
} };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Transforms an OpenAI tool choice to the specified provider format.
|
|
175
|
+
* If the input is not in OpenAI format, it's returned as-is (native passthrough).
|
|
176
|
+
*/
|
|
177
|
+
function transformToolChoice(toolChoice, format) {
|
|
178
|
+
if (!isOpenAIToolChoice(toolChoice)) return toolChoice;
|
|
179
|
+
switch (format) {
|
|
180
|
+
case "openai": return toolChoice;
|
|
181
|
+
case "anthropic": return openaiToolChoiceToAnthropic(toolChoice);
|
|
182
|
+
case "bedrock": return openaiToolChoiceToBedrock(toolChoice);
|
|
183
|
+
case "google": return openaiToolChoiceToGoogle(toolChoice);
|
|
184
|
+
default: return toolChoice;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Checks if an array contains OpenAI-format tools.
|
|
189
|
+
* Returns true if the first tool has `type: 'function'` and `function.name`.
|
|
190
|
+
*/
|
|
191
|
+
function isOpenAIToolArray(tools) {
|
|
192
|
+
if (!Array.isArray(tools) || tools.length === 0) return false;
|
|
193
|
+
const first = tools[0];
|
|
194
|
+
if (typeof first !== "object" || first === null) return false;
|
|
195
|
+
const candidate = first;
|
|
196
|
+
return candidate.type === "function" && typeof candidate.function === "object" && candidate.function !== null && typeof candidate.function.name === "string";
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Transforms OpenAI-format tools to Anthropic format.
|
|
200
|
+
*/
|
|
201
|
+
function openaiToolsToAnthropic(tools) {
|
|
202
|
+
return tools.map((tool) => ({
|
|
203
|
+
name: tool.function.name,
|
|
204
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
205
|
+
input_schema: tool.function.parameters || {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {}
|
|
208
|
+
}
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Transforms OpenAI-format tools to Bedrock Converse format.
|
|
213
|
+
*/
|
|
214
|
+
function openaiToolsToBedrock(tools) {
|
|
215
|
+
return tools.map((tool) => ({ toolSpec: {
|
|
216
|
+
name: tool.function.name,
|
|
217
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
218
|
+
inputSchema: { json: tool.function.parameters || {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {}
|
|
221
|
+
} }
|
|
222
|
+
} }));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Sanitizes a schema for Google/Gemini compatibility.
|
|
226
|
+
* - Converts type strings to uppercase (string → STRING)
|
|
227
|
+
* - Removes unsupported properties (additionalProperties, $schema, default)
|
|
228
|
+
* - Recursively processes nested schemas
|
|
229
|
+
*/
|
|
230
|
+
function sanitizeSchemaForGoogle(schema) {
|
|
231
|
+
const result = {};
|
|
232
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
233
|
+
if ([
|
|
234
|
+
"additionalProperties",
|
|
235
|
+
"$schema",
|
|
236
|
+
"default",
|
|
237
|
+
"$id",
|
|
238
|
+
"$ref"
|
|
239
|
+
].includes(key)) continue;
|
|
240
|
+
if (key === "type" && typeof value === "string") result[key] = value.toUpperCase();
|
|
241
|
+
else if (key === "properties" && typeof value === "object" && value !== null) {
|
|
242
|
+
const sanitizedProps = {};
|
|
243
|
+
for (const [propKey, propValue] of Object.entries(value)) if (typeof propValue === "object" && propValue !== null) sanitizedProps[propKey] = sanitizeSchemaForGoogle(propValue);
|
|
244
|
+
else sanitizedProps[propKey] = propValue;
|
|
245
|
+
result[key] = sanitizedProps;
|
|
246
|
+
} else if (key === "items" && typeof value === "object" && value !== null) result[key] = sanitizeSchemaForGoogle(value);
|
|
247
|
+
else result[key] = value;
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Transforms OpenAI-format tools to Google/Gemini format.
|
|
253
|
+
*/
|
|
254
|
+
function openaiToolsToGoogle(tools) {
|
|
255
|
+
return [{ functionDeclarations: tools.map((tool) => ({
|
|
256
|
+
name: tool.function.name,
|
|
257
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
258
|
+
...tool.function.parameters ? { parameters: sanitizeSchemaForGoogle(tool.function.parameters) } : {}
|
|
259
|
+
})) }];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Transforms tools from OpenAI format to the specified provider format.
|
|
263
|
+
* If the input is not in OpenAI format, it's returned as-is.
|
|
264
|
+
*/
|
|
265
|
+
function transformTools(tools, format) {
|
|
266
|
+
if (!isOpenAIToolArray(tools)) return tools;
|
|
267
|
+
switch (format) {
|
|
268
|
+
case "openai": return tools;
|
|
269
|
+
case "anthropic": return openaiToolsToAnthropic(tools);
|
|
270
|
+
case "bedrock": return openaiToolsToBedrock(tools);
|
|
271
|
+
case "google": return openaiToolsToGoogle(tools);
|
|
272
|
+
default: return tools;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/scheduler/headerParser.ts
|
|
277
|
+
const OPENAI_HEADERS = {
|
|
278
|
+
remainingRequests: "x-ratelimit-remaining-requests",
|
|
279
|
+
remainingTokens: "x-ratelimit-remaining-tokens",
|
|
280
|
+
limitRequests: "x-ratelimit-limit-requests",
|
|
281
|
+
limitTokens: "x-ratelimit-limit-tokens",
|
|
282
|
+
resetRequests: "x-ratelimit-reset-requests",
|
|
283
|
+
resetTokens: "x-ratelimit-reset-tokens"
|
|
284
|
+
};
|
|
285
|
+
const ANTHROPIC_HEADERS = {
|
|
286
|
+
remainingRequests: "anthropic-ratelimit-requests-remaining",
|
|
287
|
+
remainingTokens: "anthropic-ratelimit-tokens-remaining",
|
|
288
|
+
limitRequests: "anthropic-ratelimit-requests-limit",
|
|
289
|
+
limitTokens: "anthropic-ratelimit-tokens-limit",
|
|
290
|
+
reset: "anthropic-ratelimit-requests-reset"
|
|
291
|
+
};
|
|
292
|
+
const STANDARD_HEADERS = {
|
|
293
|
+
remaining: "ratelimit-remaining",
|
|
294
|
+
limit: "ratelimit-limit",
|
|
295
|
+
reset: "ratelimit-reset",
|
|
296
|
+
remainingAlt: "x-ratelimit-remaining",
|
|
297
|
+
limitAlt: "x-ratelimit-limit",
|
|
298
|
+
resetAlt: "x-ratelimit-reset"
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Parse rate limit headers from response.
|
|
302
|
+
*/
|
|
303
|
+
function parseRateLimitHeaders(headers) {
|
|
304
|
+
const result = {};
|
|
305
|
+
const h = lowercaseKeys(headers);
|
|
306
|
+
result.remainingRequests = parseFirstMatch(h, [
|
|
307
|
+
OPENAI_HEADERS.remainingRequests,
|
|
308
|
+
ANTHROPIC_HEADERS.remainingRequests,
|
|
309
|
+
STANDARD_HEADERS.remainingAlt,
|
|
310
|
+
STANDARD_HEADERS.remaining
|
|
311
|
+
]);
|
|
312
|
+
result.remainingTokens = parseFirstMatch(h, [OPENAI_HEADERS.remainingTokens, ANTHROPIC_HEADERS.remainingTokens]);
|
|
313
|
+
result.limitRequests = parseFirstMatch(h, [
|
|
314
|
+
OPENAI_HEADERS.limitRequests,
|
|
315
|
+
ANTHROPIC_HEADERS.limitRequests,
|
|
316
|
+
STANDARD_HEADERS.limitAlt,
|
|
317
|
+
STANDARD_HEADERS.limit
|
|
318
|
+
]);
|
|
319
|
+
result.limitTokens = parseFirstMatch(h, [OPENAI_HEADERS.limitTokens, ANTHROPIC_HEADERS.limitTokens]);
|
|
320
|
+
for (const name of [
|
|
321
|
+
OPENAI_HEADERS.resetRequests,
|
|
322
|
+
OPENAI_HEADERS.resetTokens,
|
|
323
|
+
ANTHROPIC_HEADERS.reset,
|
|
324
|
+
STANDARD_HEADERS.resetAlt,
|
|
325
|
+
STANDARD_HEADERS.reset
|
|
326
|
+
]) if (h[name] !== void 0) {
|
|
327
|
+
const parsed = parseResetTime(h[name]);
|
|
328
|
+
if (parsed !== null) {
|
|
329
|
+
result.resetAt = parsed;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (h["retry-after-ms"] !== void 0) {
|
|
334
|
+
const ms = parseInt(h["retry-after-ms"], 10);
|
|
335
|
+
if (!isNaN(ms) && ms >= 0) {
|
|
336
|
+
result.retryAfterMs = ms;
|
|
337
|
+
if (result.resetAt === void 0) result.resetAt = Date.now() + ms;
|
|
338
|
+
}
|
|
339
|
+
} else if (h["retry-after"] !== void 0) {
|
|
340
|
+
const parsed = parseRetryAfter(h["retry-after"]);
|
|
341
|
+
if (parsed !== null) {
|
|
342
|
+
result.retryAfterMs = parsed;
|
|
343
|
+
if (result.resetAt === void 0) result.resetAt = Date.now() + parsed;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Parse Retry-After header value.
|
|
350
|
+
* Returns duration in milliseconds.
|
|
351
|
+
* Exported for integration use.
|
|
352
|
+
*/
|
|
353
|
+
function parseRetryAfter(value) {
|
|
354
|
+
const seconds = parseInt(value, 10);
|
|
355
|
+
if (!isNaN(seconds) && seconds >= 0 && String(seconds) === value.trim()) return seconds * 1e3;
|
|
356
|
+
const httpDate = parseHttpDate(value);
|
|
357
|
+
if (httpDate !== null) return Math.max(0, httpDate - Date.now());
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
function parseFirstMatch(headers, names) {
|
|
361
|
+
for (const name of names) {
|
|
362
|
+
const value = headers[name];
|
|
363
|
+
if (value !== void 0) {
|
|
364
|
+
const num = parseInt(value, 10);
|
|
365
|
+
if (!isNaN(num) && num >= 0) return num;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Parse reset time from various formats.
|
|
371
|
+
* Returns absolute Unix timestamp in milliseconds.
|
|
372
|
+
*/
|
|
373
|
+
function parseResetTime(value) {
|
|
374
|
+
const durationMs = parseDuration(value);
|
|
375
|
+
if (durationMs !== null) return Date.now() + durationMs;
|
|
376
|
+
const num = parseFloat(value);
|
|
377
|
+
if (!isNaN(num)) if (num < 1e9) return Date.now() + num * 1e3;
|
|
378
|
+
else if (num < 1e10) return num * 1e3;
|
|
379
|
+
else return num;
|
|
380
|
+
const httpDate = parseHttpDate(value);
|
|
381
|
+
if (httpDate !== null) return httpDate;
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Parse HTTP-date format (RFC 7231).
|
|
386
|
+
*/
|
|
387
|
+
function parseHttpDate(value) {
|
|
388
|
+
const timestamp = Date.parse(value);
|
|
389
|
+
if (!isNaN(timestamp)) {
|
|
390
|
+
const now = Date.now();
|
|
391
|
+
const oneYearMs = 365 * 24 * 60 * 60 * 1e3;
|
|
392
|
+
if (timestamp > now - oneYearMs && timestamp < now + oneYearMs) return timestamp;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Parse duration strings like "1s", "100ms", "1m30s", "1h30s", "2h15m30s".
|
|
398
|
+
*
|
|
399
|
+
* Supported formats:
|
|
400
|
+
* - Xms (milliseconds)
|
|
401
|
+
* - Xs or X.Xs (seconds)
|
|
402
|
+
* - Xm or XmYs (minutes with optional seconds)
|
|
403
|
+
* - Xh or XhYm or XhYs or XhYmZs (hours with optional minutes/seconds)
|
|
404
|
+
*/
|
|
405
|
+
function parseDuration(value) {
|
|
406
|
+
const match = value.match(/^(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+(?:\.\d+)?)(ms|s))?$/);
|
|
407
|
+
if (!match) return null;
|
|
408
|
+
const [, hours, minutes, secondsValue, secondsUnit] = match;
|
|
409
|
+
if (!hours && !minutes && !secondsValue) return null;
|
|
410
|
+
let ms = 0;
|
|
411
|
+
if (hours) ms += parseInt(hours, 10) * 36e5;
|
|
412
|
+
if (minutes) ms += parseInt(minutes, 10) * 6e4;
|
|
413
|
+
if (secondsValue) {
|
|
414
|
+
const num = parseFloat(secondsValue);
|
|
415
|
+
ms += secondsUnit === "ms" ? num : num * 1e3;
|
|
416
|
+
}
|
|
417
|
+
return ms;
|
|
418
|
+
}
|
|
419
|
+
function lowercaseKeys(obj) {
|
|
420
|
+
const result = {};
|
|
421
|
+
for (const [key, value] of Object.entries(obj)) result[key.toLowerCase()] = value;
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region src/util/time.ts
|
|
426
|
+
function getCurrentTimestamp() {
|
|
427
|
+
return Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
428
|
+
}
|
|
429
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
430
|
+
//#endregion
|
|
431
|
+
//#region src/globalConfig/globalConfig.ts
|
|
432
|
+
/**
|
|
433
|
+
* Functions for manipulating the global configuration file, which lives at
|
|
434
|
+
* ~/.promptfoo/promptfoo.yaml by default.
|
|
435
|
+
*/
|
|
436
|
+
function writeGlobalConfig(config) {
|
|
437
|
+
fs.writeFileSync(path.join(require_logger.getConfigDirectoryPath(true), "promptfoo.yaml"), js_yaml.default.dump(config));
|
|
438
|
+
}
|
|
439
|
+
function readGlobalConfig() {
|
|
440
|
+
const configDir = require_logger.getConfigDirectoryPath();
|
|
441
|
+
const configFilePath = path.join(configDir, "promptfoo.yaml");
|
|
442
|
+
let globalConfig = { id: crypto.randomUUID() };
|
|
443
|
+
if (fs.existsSync(configFilePath)) {
|
|
444
|
+
globalConfig = js_yaml.default.load(fs.readFileSync(configFilePath, "utf-8")) || {};
|
|
445
|
+
if (!globalConfig?.id) {
|
|
446
|
+
globalConfig = {
|
|
447
|
+
...globalConfig,
|
|
448
|
+
id: crypto.randomUUID()
|
|
449
|
+
};
|
|
450
|
+
writeGlobalConfig(globalConfig);
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
|
|
454
|
+
fs.writeFileSync(configFilePath, js_yaml.default.dump(globalConfig));
|
|
455
|
+
}
|
|
456
|
+
return globalConfig;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Merges the top-level keys into existing config.
|
|
460
|
+
* @param partialConfig New keys to merge into the existing config.
|
|
461
|
+
*/
|
|
462
|
+
function writeGlobalConfigPartial(partialConfig) {
|
|
463
|
+
const updatedConfig = { ...readGlobalConfig() };
|
|
464
|
+
Object.entries(partialConfig).forEach(([key, value]) => {
|
|
465
|
+
if (value !== void 0 && value !== null) updatedConfig[key] = value;
|
|
466
|
+
else delete updatedConfig[key];
|
|
467
|
+
});
|
|
468
|
+
writeGlobalConfig(updatedConfig);
|
|
469
|
+
}
|
|
470
|
+
const API_HOST = require_logger.getEnvString("API_HOST", "https://api.promptfoo.app");
|
|
471
|
+
const SHARING_CUTOFF_DATE = /* @__PURE__ */ new Date("2026-03-09T00:00:00Z");
|
|
472
|
+
var CloudConfig = class {
|
|
473
|
+
config;
|
|
474
|
+
constructor() {
|
|
475
|
+
const savedConfig = readGlobalConfig()?.cloud || {};
|
|
476
|
+
this.config = {
|
|
477
|
+
appUrl: savedConfig.appUrl || "https://www.promptfoo.app",
|
|
478
|
+
apiHost: savedConfig.apiHost,
|
|
479
|
+
apiKey: savedConfig.apiKey,
|
|
480
|
+
sharing: savedConfig.sharing,
|
|
481
|
+
currentOrganizationId: savedConfig.currentOrganizationId,
|
|
482
|
+
currentTeamId: savedConfig.currentTeamId,
|
|
483
|
+
teams: savedConfig.teams
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Returns the API key from config file or PROMPTFOO_API_KEY environment variable.
|
|
488
|
+
* Config file takes precedence over environment variable.
|
|
489
|
+
*/
|
|
490
|
+
resolveApiKey() {
|
|
491
|
+
return this.config.apiKey || process.env.PROMPTFOO_API_KEY;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Returns the API host from config file, PROMPTFOO_CLOUD_API_URL environment variable,
|
|
495
|
+
* or defaults to the standard cloud API host.
|
|
496
|
+
* Config file takes precedence over environment variable.
|
|
497
|
+
*/
|
|
498
|
+
resolveApiHost() {
|
|
499
|
+
return this.config.apiHost || process.env.PROMPTFOO_CLOUD_API_URL || API_HOST;
|
|
500
|
+
}
|
|
501
|
+
isEnabled() {
|
|
502
|
+
return !!this.resolveApiKey();
|
|
503
|
+
}
|
|
504
|
+
setApiHost(apiHost) {
|
|
505
|
+
this.config.apiHost = apiHost;
|
|
506
|
+
this.saveConfig();
|
|
507
|
+
}
|
|
508
|
+
setApiKey(apiKey) {
|
|
509
|
+
this.config.apiKey = apiKey;
|
|
510
|
+
this.saveConfig();
|
|
511
|
+
}
|
|
512
|
+
getApiKey() {
|
|
513
|
+
return this.resolveApiKey();
|
|
514
|
+
}
|
|
515
|
+
getApiHost() {
|
|
516
|
+
return this.resolveApiHost();
|
|
517
|
+
}
|
|
518
|
+
setAppUrl(appUrl) {
|
|
519
|
+
this.config.appUrl = appUrl;
|
|
520
|
+
this.saveConfig();
|
|
521
|
+
}
|
|
522
|
+
getAppUrl() {
|
|
523
|
+
return this.config.appUrl;
|
|
524
|
+
}
|
|
525
|
+
getSharing() {
|
|
526
|
+
return this.config.sharing;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Sets the sharing preference. Note: this value is only updated at authentication time
|
|
530
|
+
* (via `validateAndSetApiToken`) and may become stale if the user's license status
|
|
531
|
+
* changes between re-authentications.
|
|
532
|
+
*/
|
|
533
|
+
setSharing(sharing) {
|
|
534
|
+
this.config.sharing = sharing;
|
|
535
|
+
this.saveConfig();
|
|
536
|
+
}
|
|
537
|
+
delete() {
|
|
538
|
+
writeGlobalConfigPartial({ cloud: {} });
|
|
539
|
+
this.reload();
|
|
540
|
+
}
|
|
541
|
+
saveConfig() {
|
|
542
|
+
writeGlobalConfigPartial({ cloud: this.config });
|
|
543
|
+
this.reload();
|
|
544
|
+
}
|
|
545
|
+
reload() {
|
|
546
|
+
const savedConfig = readGlobalConfig()?.cloud || {};
|
|
547
|
+
this.config = {
|
|
548
|
+
appUrl: savedConfig.appUrl || "https://www.promptfoo.app",
|
|
549
|
+
apiHost: savedConfig.apiHost,
|
|
550
|
+
apiKey: savedConfig.apiKey,
|
|
551
|
+
sharing: savedConfig.sharing,
|
|
552
|
+
currentOrganizationId: savedConfig.currentOrganizationId,
|
|
553
|
+
currentTeamId: savedConfig.currentTeamId,
|
|
554
|
+
teams: savedConfig.teams
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
saveValidatedApiToken(token, apiHost, user, app, hasActiveLicense) {
|
|
558
|
+
this.setApiKey(token);
|
|
559
|
+
this.setApiHost(apiHost);
|
|
560
|
+
this.setAppUrl(app.url);
|
|
561
|
+
if (typeof hasActiveLicense === "boolean") {
|
|
562
|
+
const createdAt = user?.createdAt ? new Date(user.createdAt) : null;
|
|
563
|
+
const isGrandfathered = createdAt != null && createdAt < SHARING_CUTOFF_DATE;
|
|
564
|
+
this.setSharing(hasActiveLicense || isGrandfathered);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
async validateApiToken(token, apiHost) {
|
|
568
|
+
try {
|
|
569
|
+
const { fetchWithProxy } = await Promise.resolve().then(() => fetch_exports);
|
|
570
|
+
const response = await fetchWithProxy(`${apiHost}/api/v1/users/me`, { headers: { Authorization: `Bearer ${token}` } });
|
|
571
|
+
if (!response.ok) {
|
|
572
|
+
const errorMessage = await response.text();
|
|
573
|
+
require_logger.logger.error(`[Cloud] Failed to validate API token: ${errorMessage}. HTTP Status: ${response.status} - ${response.statusText}.`);
|
|
574
|
+
throw new Error("Failed to validate API token: " + response.statusText);
|
|
575
|
+
}
|
|
576
|
+
const { user, organization, app, hasActiveLicense } = await response.json();
|
|
577
|
+
return {
|
|
578
|
+
user,
|
|
579
|
+
organization,
|
|
580
|
+
app,
|
|
581
|
+
...typeof hasActiveLicense === "boolean" ? { hasActiveLicense } : {}
|
|
582
|
+
};
|
|
583
|
+
} catch (err) {
|
|
584
|
+
const error = err;
|
|
585
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
586
|
+
require_logger.logger.error(`[Cloud] Failed to validate API token with host ${apiHost}: ${errorMessage}`);
|
|
587
|
+
if (error.cause) require_logger.logger.error(`Cause: ${error.cause}`);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async validateAndSetApiToken(token, apiHost) {
|
|
592
|
+
const { user, organization, app, hasActiveLicense } = await this.validateApiToken(token, apiHost);
|
|
593
|
+
this.saveValidatedApiToken(token, apiHost, user, app, hasActiveLicense);
|
|
594
|
+
return {
|
|
595
|
+
user,
|
|
596
|
+
organization,
|
|
597
|
+
app,
|
|
598
|
+
hasActiveLicense: typeof hasActiveLicense === "boolean" ? hasActiveLicense : false
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
getCurrentOrganizationId() {
|
|
602
|
+
return this.config.currentOrganizationId;
|
|
603
|
+
}
|
|
604
|
+
setCurrentOrganization(organizationId) {
|
|
605
|
+
this.config.currentOrganizationId = organizationId;
|
|
606
|
+
this.saveConfig();
|
|
607
|
+
}
|
|
608
|
+
getCurrentTeamId(organizationId) {
|
|
609
|
+
if (organizationId) return this.config.teams?.[organizationId]?.currentTeamId;
|
|
610
|
+
return this.config.currentTeamId;
|
|
611
|
+
}
|
|
612
|
+
setCurrentTeamId(teamId, organizationId) {
|
|
613
|
+
if (organizationId) {
|
|
614
|
+
if (!this.config.teams) this.config.teams = {};
|
|
615
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
616
|
+
this.config.teams[organizationId].currentTeamId = teamId;
|
|
617
|
+
} else this.config.currentTeamId = teamId;
|
|
618
|
+
this.saveConfig();
|
|
619
|
+
}
|
|
620
|
+
clearCurrentTeamId(organizationId) {
|
|
621
|
+
if (organizationId) {
|
|
622
|
+
if (this.config.teams?.[organizationId]) delete this.config.teams[organizationId].currentTeamId;
|
|
623
|
+
} else delete this.config.currentTeamId;
|
|
624
|
+
this.saveConfig();
|
|
625
|
+
}
|
|
626
|
+
cacheTeams(teams, organizationId) {
|
|
627
|
+
if (organizationId) {
|
|
628
|
+
if (!this.config.teams) this.config.teams = {};
|
|
629
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
630
|
+
this.config.teams[organizationId].cache = teams.map((t) => ({
|
|
631
|
+
id: t.id,
|
|
632
|
+
name: t.name,
|
|
633
|
+
slug: t.slug,
|
|
634
|
+
lastFetched: (/* @__PURE__ */ new Date()).toISOString()
|
|
635
|
+
}));
|
|
636
|
+
}
|
|
637
|
+
this.saveConfig();
|
|
638
|
+
}
|
|
639
|
+
getCachedTeams(organizationId) {
|
|
640
|
+
if (organizationId) return this.config.teams?.[organizationId]?.cache;
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
const cloudConfig = new CloudConfig();
|
|
644
|
+
//#endregion
|
|
645
|
+
//#region src/util/fetch/monkeyPatchFetch.ts
|
|
646
|
+
const gzipAsync = (0, util.promisify)(zlib.gzip);
|
|
647
|
+
function isConnectionError(error) {
|
|
648
|
+
return error instanceof TypeError && error.message === "fetch failed" && error.cause?.stack?.includes("internalConnectMultiple");
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Enhanced fetch wrapper that adds logging, authentication, error handling, and optional compression
|
|
652
|
+
*/
|
|
653
|
+
async function monkeyPatchFetch(url, options) {
|
|
654
|
+
const NO_LOG_URLS = [
|
|
655
|
+
R_ENDPOINT,
|
|
656
|
+
CONSENT_ENDPOINT,
|
|
657
|
+
EVENTS_ENDPOINT
|
|
658
|
+
];
|
|
659
|
+
const isSilent = (options?.headers || {})["x-promptfoo-silent"] === "true";
|
|
660
|
+
const logEnabled = !NO_LOG_URLS.some((logUrl) => url.toString().startsWith(logUrl)) && !isSilent;
|
|
661
|
+
const opts = { ...options };
|
|
662
|
+
const originalBody = opts.body;
|
|
663
|
+
if (options?.compress && opts.body && typeof opts.body === "string") try {
|
|
664
|
+
opts.body = await gzipAsync(opts.body);
|
|
665
|
+
opts.headers = {
|
|
666
|
+
...opts.headers || {},
|
|
667
|
+
"Content-Encoding": "gzip"
|
|
668
|
+
};
|
|
669
|
+
} catch (e) {
|
|
670
|
+
require_logger.logger.warn(`Failed to compress request body: ${e}`);
|
|
671
|
+
}
|
|
672
|
+
if (typeof url === "string" && url.startsWith("https://api.promptfoo.app") || url instanceof URL && url.host === "https://api.promptfoo.app".replace(/^https?:\/\//, "")) {
|
|
673
|
+
const token = cloudConfig.getApiKey();
|
|
674
|
+
opts.headers = {
|
|
675
|
+
...opts.headers || {},
|
|
676
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
const response = await fetch(url, opts);
|
|
681
|
+
if (logEnabled) require_logger.logRequestResponse({
|
|
682
|
+
url: url.toString(),
|
|
683
|
+
requestBody: originalBody,
|
|
684
|
+
requestMethod: opts.method || "GET",
|
|
685
|
+
response
|
|
686
|
+
});
|
|
687
|
+
return response;
|
|
688
|
+
} catch (e) {
|
|
689
|
+
if (logEnabled) {
|
|
690
|
+
require_logger.logRequestResponse({
|
|
691
|
+
url: url.toString(),
|
|
692
|
+
requestBody: opts.body,
|
|
693
|
+
requestMethod: opts.method || "GET",
|
|
694
|
+
response: null
|
|
695
|
+
});
|
|
696
|
+
if (isConnectionError(e)) {
|
|
697
|
+
require_logger.logger.debug(`Connection error, please check your network connectivity to the host: ${url} ${process.env.HTTP_PROXY || process.env.HTTPS_PROXY ? `or Proxy: ${process.env.HTTP_PROXY || process.env.HTTPS_PROXY}` : ""}`);
|
|
698
|
+
throw e;
|
|
699
|
+
}
|
|
700
|
+
require_logger.logger.debug(`Error in fetch: ${JSON.stringify(e, Object.getOwnPropertyNames(e), 2)} ${e instanceof Error ? e.stack : ""}`);
|
|
701
|
+
}
|
|
702
|
+
throw e;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/util/fetch/index.ts
|
|
707
|
+
var fetch_exports = /* @__PURE__ */ require_logger.__exportAll({
|
|
708
|
+
fetchWithProxy: () => fetchWithProxy,
|
|
709
|
+
fetchWithRetries: () => fetchWithRetries,
|
|
710
|
+
fetchWithTimeout: () => fetchWithTimeout,
|
|
711
|
+
handleRateLimit: () => handleRateLimit,
|
|
712
|
+
isRateLimited: () => isRateLimited,
|
|
713
|
+
isTransientError: () => isTransientError
|
|
714
|
+
});
|
|
715
|
+
let cachedAgent = null;
|
|
716
|
+
let cachedAgentConcurrency;
|
|
717
|
+
let cachedProxyAgents = /* @__PURE__ */ new Map();
|
|
718
|
+
/**
|
|
719
|
+
* Get the connection pool size for HTTP agents.
|
|
720
|
+
* Priority: PROMPTFOO_FETCH_CONNECTIONS env var > CLI -j flag > DEFAULT_MAX_CONCURRENCY (4).
|
|
721
|
+
* Set PROMPTFOO_FETCH_CONNECTIONS to override independently of eval concurrency
|
|
722
|
+
* (e.g., server deployments that need more connections than the default 4).
|
|
723
|
+
*/
|
|
724
|
+
function getConnectionPoolSize() {
|
|
725
|
+
const envConnections = require_logger.getEnvString("PROMPTFOO_FETCH_CONNECTIONS");
|
|
726
|
+
if (envConnections != null) {
|
|
727
|
+
const parsed = parseInt(envConnections, 10);
|
|
728
|
+
if (!isNaN(parsed)) return parsed;
|
|
729
|
+
}
|
|
730
|
+
return require_logger.state.maxConcurrency || 4;
|
|
731
|
+
}
|
|
732
|
+
function getOrCreateAgent(tlsOptions) {
|
|
733
|
+
const concurrency = getConnectionPoolSize();
|
|
734
|
+
if (cachedAgent && cachedAgentConcurrency !== concurrency) {
|
|
735
|
+
if (typeof cachedAgent.close === "function") cachedAgent.close();
|
|
736
|
+
cachedAgent = null;
|
|
737
|
+
}
|
|
738
|
+
if (!cachedAgent) {
|
|
739
|
+
cachedAgent = new undici.Agent({
|
|
740
|
+
headersTimeout: REQUEST_TIMEOUT_MS,
|
|
741
|
+
keepAliveTimeout: 3e4,
|
|
742
|
+
keepAliveMaxTimeout: 6e4,
|
|
743
|
+
connections: concurrency,
|
|
744
|
+
connect: tlsOptions
|
|
745
|
+
});
|
|
746
|
+
cachedAgentConcurrency = concurrency;
|
|
747
|
+
}
|
|
748
|
+
return cachedAgent;
|
|
749
|
+
}
|
|
750
|
+
function getOrCreateProxyAgent(proxyUrl, tlsOptions) {
|
|
751
|
+
if (!cachedProxyAgents.has(proxyUrl)) {
|
|
752
|
+
const agent = new undici.ProxyAgent({
|
|
753
|
+
uri: proxyUrl,
|
|
754
|
+
proxyTls: tlsOptions,
|
|
755
|
+
requestTls: tlsOptions,
|
|
756
|
+
headersTimeout: REQUEST_TIMEOUT_MS,
|
|
757
|
+
keepAliveTimeout: 3e4,
|
|
758
|
+
keepAliveMaxTimeout: 6e4,
|
|
759
|
+
connections: getConnectionPoolSize()
|
|
760
|
+
});
|
|
761
|
+
cachedProxyAgents.set(proxyUrl, agent);
|
|
762
|
+
}
|
|
763
|
+
return cachedProxyAgents.get(proxyUrl);
|
|
764
|
+
}
|
|
765
|
+
async function fetchWithProxy(url, options = {}, abortSignal) {
|
|
766
|
+
let finalUrl = url;
|
|
767
|
+
let finalUrlString;
|
|
768
|
+
if (typeof url === "string") finalUrlString = url;
|
|
769
|
+
else if (url instanceof URL) finalUrlString = url.toString();
|
|
770
|
+
else if (url instanceof Request) finalUrlString = url.url;
|
|
771
|
+
if (!finalUrlString) throw new Error("Invalid URL");
|
|
772
|
+
const combinedSignal = abortSignal ? options.signal ? AbortSignal.any([options.signal, abortSignal]) : abortSignal : options.signal;
|
|
773
|
+
const finalOptions = {
|
|
774
|
+
...options,
|
|
775
|
+
headers: {
|
|
776
|
+
...options.headers,
|
|
777
|
+
"x-promptfoo-version": VERSION
|
|
778
|
+
},
|
|
779
|
+
signal: combinedSignal
|
|
780
|
+
};
|
|
781
|
+
if (typeof url === "string") try {
|
|
782
|
+
const parsedUrl = new URL(url);
|
|
783
|
+
if (parsedUrl.username || parsedUrl.password) {
|
|
784
|
+
if (finalOptions.headers && "Authorization" in finalOptions.headers) require_logger.logger.warn("Both URL credentials and Authorization header present - URL credentials will be ignored");
|
|
785
|
+
else {
|
|
786
|
+
const username = parsedUrl.username || "";
|
|
787
|
+
const password = parsedUrl.password || "";
|
|
788
|
+
const credentials = Buffer.from(`${username}:${password}`).toString("base64");
|
|
789
|
+
finalOptions.headers = {
|
|
790
|
+
...finalOptions.headers,
|
|
791
|
+
Authorization: `Basic ${credentials}`
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
parsedUrl.username = "";
|
|
795
|
+
parsedUrl.password = "";
|
|
796
|
+
finalUrl = parsedUrl.toString();
|
|
797
|
+
finalUrlString = finalUrl.toString();
|
|
798
|
+
}
|
|
799
|
+
} catch (e) {
|
|
800
|
+
require_logger.logger.debug(`URL parsing failed in fetchWithProxy: ${e}`);
|
|
801
|
+
}
|
|
802
|
+
const tlsOptions = { rejectUnauthorized: !require_logger.getEnvBool("PROMPTFOO_INSECURE_SSL", true) };
|
|
803
|
+
const caCertPath = require_logger.getEnvString("PROMPTFOO_CA_CERT_PATH");
|
|
804
|
+
if (caCertPath) try {
|
|
805
|
+
const resolvedPath = path.default.resolve(require_logger.state.basePath || "", caCertPath);
|
|
806
|
+
tlsOptions.ca = await fs_promises.readFile(resolvedPath, "utf8");
|
|
807
|
+
require_logger.logger.debug(`Using custom CA certificate from ${resolvedPath}`);
|
|
808
|
+
} catch (e) {
|
|
809
|
+
require_logger.logger.warn(`Failed to read CA certificate from ${caCertPath}: ${e}`);
|
|
810
|
+
}
|
|
811
|
+
const proxyUrl = finalUrlString ? (0, proxy_from_env.getProxyForUrl)(finalUrlString) : "";
|
|
812
|
+
if (!finalOptions.dispatcher) if (proxyUrl) {
|
|
813
|
+
require_logger.logger.debug(`Using proxy: ${require_logger.sanitizeUrl(proxyUrl)}`);
|
|
814
|
+
finalOptions.dispatcher = getOrCreateProxyAgent(proxyUrl, tlsOptions);
|
|
815
|
+
} else finalOptions.dispatcher = getOrCreateAgent(tlsOptions);
|
|
816
|
+
const maxTransientRetries = options.disableTransientRetries ? 0 : 3;
|
|
817
|
+
for (let attempt = 0; attempt <= maxTransientRetries; attempt++) {
|
|
818
|
+
const response = await monkeyPatchFetch(finalUrl, finalOptions);
|
|
819
|
+
if (!options.disableTransientRetries && isTransientError(response) && attempt < maxTransientRetries) {
|
|
820
|
+
const backoffMs = Math.pow(2, attempt) * 1e3;
|
|
821
|
+
require_logger.logger.debug(`Transient error (${response.status} ${response.statusText}), retry ${attempt + 1}/${maxTransientRetries} after ${backoffMs}ms`);
|
|
822
|
+
await sleep(backoffMs);
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
return response;
|
|
826
|
+
}
|
|
827
|
+
throw new Error("Unexpected end of transient retry loop");
|
|
828
|
+
}
|
|
829
|
+
function fetchWithTimeout(url, options = {}, timeout) {
|
|
830
|
+
return new Promise((resolve, reject) => {
|
|
831
|
+
const timeoutController = new AbortController();
|
|
832
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeoutController.signal]) : timeoutController.signal;
|
|
833
|
+
const timeoutId = setTimeout(() => {
|
|
834
|
+
timeoutController.abort();
|
|
835
|
+
reject(/* @__PURE__ */ new Error(`Request timed out after ${timeout} ms`));
|
|
836
|
+
}, timeout);
|
|
837
|
+
fetchWithProxy(url, {
|
|
838
|
+
...options,
|
|
839
|
+
signal
|
|
840
|
+
}).then((response) => {
|
|
841
|
+
clearTimeout(timeoutId);
|
|
842
|
+
resolve(response);
|
|
843
|
+
}).catch((error) => {
|
|
844
|
+
clearTimeout(timeoutId);
|
|
845
|
+
reject(error);
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Check if a response indicates rate limiting
|
|
851
|
+
*/
|
|
852
|
+
function isRateLimited(response) {
|
|
853
|
+
require_invariant.invariant(response.headers, "Response headers are missing");
|
|
854
|
+
require_invariant.invariant(response.status, "Response status is missing");
|
|
855
|
+
return response.headers.get("X-RateLimit-Remaining") === "0" || response.status === 429 || response.headers.get("x-ratelimit-remaining-requests") === "0" || response.headers.get("x-ratelimit-remaining-tokens") === "0";
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Handle rate limiting by waiting the appropriate amount of time
|
|
859
|
+
*/
|
|
860
|
+
async function handleRateLimit(response) {
|
|
861
|
+
const rateLimitReset = response.headers.get("X-RateLimit-Reset");
|
|
862
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
863
|
+
const openaiReset = response.headers.get("x-ratelimit-reset-requests") || response.headers.get("x-ratelimit-reset-tokens");
|
|
864
|
+
let waitTime = 6e4;
|
|
865
|
+
if (openaiReset) {
|
|
866
|
+
const parsedHeaders = parseRateLimitHeaders(Object.fromEntries(response.headers.entries()));
|
|
867
|
+
if (parsedHeaders.resetAt !== void 0) waitTime = Math.max(parsedHeaders.resetAt - Date.now(), 0);
|
|
868
|
+
} else if (rateLimitReset) {
|
|
869
|
+
const resetTime = /* @__PURE__ */ new Date(Number.parseInt(rateLimitReset) * 1e3);
|
|
870
|
+
const now = /* @__PURE__ */ new Date();
|
|
871
|
+
waitTime = Math.max(resetTime.getTime() - now.getTime() + 1e3, 0);
|
|
872
|
+
} else if (retryAfter) waitTime = parseRetryAfter(retryAfter) ?? waitTime;
|
|
873
|
+
require_logger.logger.debug(`Rate limited, waiting ${waitTime}ms before retry`);
|
|
874
|
+
await sleep(waitTime);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Check if a response indicates a transient server error that should be retried.
|
|
878
|
+
* Matches specific status codes with their expected status text to avoid
|
|
879
|
+
* retrying permanent failures (e.g., some APIs return 502 for auth errors).
|
|
880
|
+
*/
|
|
881
|
+
function isTransientError(response) {
|
|
882
|
+
if (!response?.statusText) return false;
|
|
883
|
+
const statusText = response.statusText.toLowerCase();
|
|
884
|
+
switch (response.status) {
|
|
885
|
+
case 502: return statusText.includes("bad gateway");
|
|
886
|
+
case 503: return statusText.includes("service unavailable");
|
|
887
|
+
case 504: return statusText.includes("gateway timeout");
|
|
888
|
+
case 524: return statusText.includes("timeout");
|
|
889
|
+
default: return false;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
async function fetchWithRetries(url, options = {}, timeout, maxRetries) {
|
|
893
|
+
maxRetries = Math.max(0, maxRetries ?? 4);
|
|
894
|
+
let lastErrorMessage;
|
|
895
|
+
const backoff = require_logger.getEnvInt("PROMPTFOO_REQUEST_BACKOFF_MS", 5e3);
|
|
896
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
897
|
+
let response;
|
|
898
|
+
try {
|
|
899
|
+
response = await fetchWithTimeout(url, {
|
|
900
|
+
...options,
|
|
901
|
+
disableTransientRetries: true
|
|
902
|
+
}, timeout);
|
|
903
|
+
if (require_logger.getEnvBool("PROMPTFOO_RETRY_5XX") && response.status >= 500 && response.status < 600) throw new Error(`Internal Server Error: ${response.status} ${response.statusText}`);
|
|
904
|
+
if (response && isRateLimited(response)) {
|
|
905
|
+
require_logger.logger.debug(`Rate limited on URL ${url}: ${response.status} ${response.statusText}, attempt ${i + 1}/${maxRetries + 1}, waiting before retry...`);
|
|
906
|
+
lastErrorMessage = `Rate limited: ${response.status} ${response.statusText}`;
|
|
907
|
+
await handleRateLimit(response);
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
return response;
|
|
911
|
+
} catch (error) {
|
|
912
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
913
|
+
let errorMessage;
|
|
914
|
+
if (error instanceof Error) {
|
|
915
|
+
const typedError = error;
|
|
916
|
+
errorMessage = `${typedError.name}: ${typedError.message}`;
|
|
917
|
+
if (typedError.cause) errorMessage += ` (Cause: ${typedError.cause})`;
|
|
918
|
+
if (typedError.code) errorMessage += ` (Code: ${typedError.code})`;
|
|
919
|
+
} else errorMessage = String(error);
|
|
920
|
+
require_logger.logger.debug(`Request to ${url} failed (attempt #${i + 1}), retrying: ${errorMessage}`);
|
|
921
|
+
if (i < maxRetries) await sleep(Math.pow(2, i) * (backoff + 1e3 * Math.random()));
|
|
922
|
+
lastErrorMessage = errorMessage;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
throw new Error(`Request failed after ${maxRetries} retries: ${lastErrorMessage}`);
|
|
926
|
+
}
|
|
927
|
+
//#endregion
|
|
928
|
+
Object.defineProperty(exports, "CLOUD_PROVIDER_PREFIX", {
|
|
929
|
+
enumerable: true,
|
|
930
|
+
get: function() {
|
|
931
|
+
return CLOUD_PROVIDER_PREFIX;
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
Object.defineProperty(exports, "CONSENT_ENDPOINT", {
|
|
935
|
+
enumerable: true,
|
|
936
|
+
get: function() {
|
|
937
|
+
return CONSENT_ENDPOINT;
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
Object.defineProperty(exports, "CloudConfig", {
|
|
941
|
+
enumerable: true,
|
|
942
|
+
get: function() {
|
|
943
|
+
return CloudConfig;
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
Object.defineProperty(exports, "EVENTS_ENDPOINT", {
|
|
947
|
+
enumerable: true,
|
|
948
|
+
get: function() {
|
|
949
|
+
return EVENTS_ENDPOINT;
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
Object.defineProperty(exports, "FILE_METADATA_KEY", {
|
|
953
|
+
enumerable: true,
|
|
954
|
+
get: function() {
|
|
955
|
+
return FILE_METADATA_KEY;
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
Object.defineProperty(exports, "HUMAN_ASSERTION_TYPE", {
|
|
959
|
+
enumerable: true,
|
|
960
|
+
get: function() {
|
|
961
|
+
return HUMAN_ASSERTION_TYPE;
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
Object.defineProperty(exports, "LONG_RUNNING_MODEL_TIMEOUT_MS", {
|
|
965
|
+
enumerable: true,
|
|
966
|
+
get: function() {
|
|
967
|
+
return LONG_RUNNING_MODEL_TIMEOUT_MS;
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
Object.defineProperty(exports, "REQUEST_TIMEOUT_MS", {
|
|
971
|
+
enumerable: true,
|
|
972
|
+
get: function() {
|
|
973
|
+
return REQUEST_TIMEOUT_MS;
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
Object.defineProperty(exports, "R_ENDPOINT", {
|
|
977
|
+
enumerable: true,
|
|
978
|
+
get: function() {
|
|
979
|
+
return R_ENDPOINT;
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
Object.defineProperty(exports, "TERMINAL_MAX_WIDTH", {
|
|
983
|
+
enumerable: true,
|
|
984
|
+
get: function() {
|
|
985
|
+
return TERMINAL_MAX_WIDTH;
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
Object.defineProperty(exports, "VERSION", {
|
|
989
|
+
enumerable: true,
|
|
990
|
+
get: function() {
|
|
991
|
+
return VERSION;
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
Object.defineProperty(exports, "calculateCost", {
|
|
995
|
+
enumerable: true,
|
|
996
|
+
get: function() {
|
|
997
|
+
return calculateCost;
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
Object.defineProperty(exports, "cloudConfig", {
|
|
1001
|
+
enumerable: true,
|
|
1002
|
+
get: function() {
|
|
1003
|
+
return cloudConfig;
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
Object.defineProperty(exports, "fetchWithProxy", {
|
|
1007
|
+
enumerable: true,
|
|
1008
|
+
get: function() {
|
|
1009
|
+
return fetchWithProxy;
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
Object.defineProperty(exports, "fetchWithRetries", {
|
|
1013
|
+
enumerable: true,
|
|
1014
|
+
get: function() {
|
|
1015
|
+
return fetchWithRetries;
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
Object.defineProperty(exports, "fetchWithTimeout", {
|
|
1019
|
+
enumerable: true,
|
|
1020
|
+
get: function() {
|
|
1021
|
+
return fetchWithTimeout;
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
Object.defineProperty(exports, "fetch_exports", {
|
|
1025
|
+
enumerable: true,
|
|
1026
|
+
get: function() {
|
|
1027
|
+
return fetch_exports;
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
Object.defineProperty(exports, "getCurrentTimestamp", {
|
|
1031
|
+
enumerable: true,
|
|
1032
|
+
get: function() {
|
|
1033
|
+
return getCurrentTimestamp;
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
Object.defineProperty(exports, "getShareApiBaseUrl", {
|
|
1037
|
+
enumerable: true,
|
|
1038
|
+
get: function() {
|
|
1039
|
+
return getShareApiBaseUrl;
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
Object.defineProperty(exports, "isOpenAIToolArray", {
|
|
1043
|
+
enumerable: true,
|
|
1044
|
+
get: function() {
|
|
1045
|
+
return isOpenAIToolArray;
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
Object.defineProperty(exports, "isOpenAIToolChoice", {
|
|
1049
|
+
enumerable: true,
|
|
1050
|
+
get: function() {
|
|
1051
|
+
return isOpenAIToolChoice;
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
Object.defineProperty(exports, "isPromptfooSampleTarget", {
|
|
1055
|
+
enumerable: true,
|
|
1056
|
+
get: function() {
|
|
1057
|
+
return isPromptfooSampleTarget;
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
Object.defineProperty(exports, "openaiToolChoiceToBedrock", {
|
|
1061
|
+
enumerable: true,
|
|
1062
|
+
get: function() {
|
|
1063
|
+
return openaiToolChoiceToBedrock;
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
Object.defineProperty(exports, "openaiToolsToBedrock", {
|
|
1067
|
+
enumerable: true,
|
|
1068
|
+
get: function() {
|
|
1069
|
+
return openaiToolsToBedrock;
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
Object.defineProperty(exports, "parseChatPrompt", {
|
|
1073
|
+
enumerable: true,
|
|
1074
|
+
get: function() {
|
|
1075
|
+
return parseChatPrompt;
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
Object.defineProperty(exports, "parseRateLimitHeaders", {
|
|
1079
|
+
enumerable: true,
|
|
1080
|
+
get: function() {
|
|
1081
|
+
return parseRateLimitHeaders;
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
Object.defineProperty(exports, "parseRetryAfter", {
|
|
1085
|
+
enumerable: true,
|
|
1086
|
+
get: function() {
|
|
1087
|
+
return parseRetryAfter;
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
Object.defineProperty(exports, "readGlobalConfig", {
|
|
1091
|
+
enumerable: true,
|
|
1092
|
+
get: function() {
|
|
1093
|
+
return readGlobalConfig;
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
Object.defineProperty(exports, "sleep", {
|
|
1097
|
+
enumerable: true,
|
|
1098
|
+
get: function() {
|
|
1099
|
+
return sleep;
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
Object.defineProperty(exports, "toTitleCase", {
|
|
1103
|
+
enumerable: true,
|
|
1104
|
+
get: function() {
|
|
1105
|
+
return toTitleCase;
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
Object.defineProperty(exports, "transformToolChoice", {
|
|
1109
|
+
enumerable: true,
|
|
1110
|
+
get: function() {
|
|
1111
|
+
return transformToolChoice;
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
Object.defineProperty(exports, "transformTools", {
|
|
1115
|
+
enumerable: true,
|
|
1116
|
+
get: function() {
|
|
1117
|
+
return transformTools;
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
Object.defineProperty(exports, "writeGlobalConfig", {
|
|
1121
|
+
enumerable: true,
|
|
1122
|
+
get: function() {
|
|
1123
|
+
return writeGlobalConfig;
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
Object.defineProperty(exports, "writeGlobalConfigPartial", {
|
|
1127
|
+
enumerable: true,
|
|
1128
|
+
get: function() {
|
|
1129
|
+
return writeGlobalConfigPartial;
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
//# sourceMappingURL=fetch-BTxakTSg.cjs.map
|