promptfoo 0.125.0 → 0.125.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/src/accounts-BOxNYxVN.js +229 -0
- package/dist/src/accounts-BezXLdAW.cjs +276 -0
- package/dist/src/accounts-CPISMNlh.js +227 -0
- package/dist/src/accounts-vJBPs89d.js +222 -0
- package/dist/src/agentWorkspace-Bom7SpF2.js +592 -0
- package/dist/src/agentWorkspace-CEomAcws.cjs +619 -0
- package/dist/src/agentWorkspace-DPtMLrlI.js +593 -0
- package/dist/src/agentWorkspace-DkAeEYLz.js +592 -0
- package/dist/src/agentic-utils-121v7mJo.js +197 -0
- package/dist/src/agentic-utils-CBWD_rcd.cjs +237 -0
- package/dist/src/agentic-utils-D8wyQwPG.js +198 -0
- package/dist/src/agentic-utils-EP1QkQCs.js +197 -0
- package/dist/src/agents-Br3ah8iA.js +778 -0
- package/dist/src/agents-C5wj_UTv.js +777 -0
- package/dist/src/agents-C9Efp4mC.js +266 -0
- package/dist/src/agents-CQYwaVCO.js +267 -0
- package/dist/src/agents-CjCd4Vo8.js +777 -0
- package/dist/src/agents-DRWiCHYz.js +266 -0
- package/dist/src/agents-F2rhx0iL.cjs +266 -0
- package/dist/src/agents-RTYyy69h.cjs +777 -0
- package/dist/src/agents-api-CY72O86e.js +711 -0
- package/dist/src/agents-api-CsSsC21b.js +710 -0
- package/dist/src/agents-api-DN-ibDc8.js +710 -0
- package/dist/src/agents-api-cNDsCCv1.cjs +710 -0
- package/dist/src/ai.studio-Bj6Dyi6o.js +2 -0
- package/dist/src/ai.studio-C-e0LzyW.js +369 -0
- package/dist/src/ai.studio-CR8M6Aqf.cjs +391 -0
- package/dist/src/ai.studio-YKk7F0tv.js +374 -0
- package/dist/src/aimlapi-BGunsuTu.js +30 -0
- package/dist/src/aimlapi-BkzcfBsL.js +29 -0
- package/dist/src/aimlapi-DXFcEdP8.js +28 -0
- package/dist/src/aimlapi-r-8iCcAw.cjs +29 -0
- package/dist/src/anthropicMessages-7-YNRgQR.js +114 -0
- package/dist/src/anthropicMessages-C8m9Whrd.cjs +114 -0
- package/dist/src/anthropicMessages-DS1vXD0f.js +115 -0
- package/dist/src/anthropicMessages-DmvGkz-0.js +114 -0
- package/dist/src/app/assets/index-CrdXWTXm.js +392 -0
- package/dist/src/app/browser-inventory.json +4 -4
- package/dist/src/app/index.html +1 -1
- package/dist/src/audio-BLgFgyw-.cjs +104 -0
- package/dist/src/audio-C2sibT7Q.js +104 -0
- package/dist/src/audio-DXEXb03H.js +104 -0
- package/dist/src/audio-TkvojpwO.js +105 -0
- package/dist/src/authoritativeMarkupInjection-BAT9B-Wj.js +79 -0
- package/dist/src/authoritativeMarkupInjection-B_yHETKT.js +78 -0
- package/dist/src/authoritativeMarkupInjection-BfjecQ3Q.cjs +78 -0
- package/dist/src/authoritativeMarkupInjection-D-hDKrhD.js +78 -0
- package/dist/src/aws-B_9unI_-.js +1149 -0
- package/dist/src/aws-D0FY3qkc.js +2433 -0
- package/dist/src/aws-Dh1-a90n.js +2433 -0
- package/dist/src/aws-qskKGczY.cjs +2454 -0
- package/dist/src/base-CaWlBkVz.js +462 -0
- package/dist/src/base-CcViNwVs.js +255 -0
- package/dist/src/base-CsVbPJkU.cjs +467 -0
- package/dist/src/base-DNK9py25.js +255 -0
- package/dist/src/base-DlH5tjEZ.js +463 -0
- package/dist/src/base-DyLOZ_iX.js +256 -0
- package/dist/src/base-RRYXfG7l.cjs +305 -0
- package/dist/src/base-oXVgvtVn.js +462 -0
- package/dist/src/bedrock-BP17ZXbR.cjs +2 -0
- package/dist/src/bedrock-CsjM1dOt.js +1300 -0
- package/dist/src/bedrock-CzPwj8GO.js +1 -0
- package/dist/src/bedrock-eGXZw8oh.js +1 -0
- package/dist/src/bedrock-tSWjmXdj.js +2 -0
- package/dist/src/bestOfN-BQ93nUKV.js +160 -0
- package/dist/src/bestOfN-ByGGwlcb.cjs +161 -0
- package/dist/src/bestOfN-CbP7csHr.js +159 -0
- package/dist/src/bestOfN-xn6i2Xs2.js +159 -0
- package/dist/src/cache-CHq9hPz9.js +2 -0
- package/dist/src/cache-D-9eZN3I.js +754 -0
- package/dist/src/cache-DSm6t1MZ.js +741 -0
- package/dist/src/cache-DeF5zIEN.js +782 -0
- package/dist/src/cache-iG87oYan.cjs +856 -0
- package/dist/src/cerebras-CTtM4yMH.js +98 -0
- package/dist/src/cerebras-CogcJ3ce.cjs +97 -0
- package/dist/src/cerebras-DmkRypTh.js +97 -0
- package/dist/src/cerebras-GUp0B8JY.js +97 -0
- package/dist/src/chat-B4ihd3fw.js +226 -0
- package/dist/src/chat-B9cAQ_rS.js +227 -0
- package/dist/src/chat-BU7EB4R-.cjs +76 -0
- package/dist/src/chat-C8yxJ2-s.cjs +226 -0
- package/dist/src/chat-ClTFM3qq.js +934 -0
- package/dist/src/chat-Cnq0Is68.js +123 -0
- package/dist/src/chat-CwrurR57.cjs +987 -0
- package/dist/src/chat-D0lsojPv.js +124 -0
- package/dist/src/chat-D7_8yFIx.js +76 -0
- package/dist/src/chat-DCoETlLG.js +226 -0
- package/dist/src/chat-DHE7xaTR.js +76 -0
- package/dist/src/chat-DmhT6rz2.cjs +123 -0
- package/dist/src/chat-DvfMe8KI.js +123 -0
- package/dist/src/chat-DwP5kVnl.js +935 -0
- package/dist/src/chat-MB7U-QE2.js +77 -0
- package/dist/src/chat-zCWuBKAj.js +934 -0
- package/dist/src/claude-agent-sdk-23bIVI75.js +999 -0
- package/dist/src/claude-agent-sdk-BACpnIJi.cjs +1005 -0
- package/dist/src/claude-agent-sdk-BlYlmcEr.js +999 -0
- package/dist/src/claude-agent-sdk-Cr-jqJZl.js +1000 -0
- package/dist/src/client-BktbiPx9.cjs +357 -0
- package/dist/src/client-CjztEBmW.js +351 -0
- package/dist/src/client-mRs2UvH9.js +351 -0
- package/dist/src/client-uCz0ydjC.js +352 -0
- package/dist/src/cloud-BtYF555L.js +2 -0
- package/dist/src/cloud-CVSUEmAy.js +287 -0
- package/dist/src/cloud-D7Ifb-Fl.js +599 -0
- package/dist/src/cloud-DB5oK5NX.cjs +666 -0
- package/dist/src/cloud-ecu989Em.js +657 -0
- package/dist/src/cloud-ramjGURF.js +599 -0
- package/dist/src/cloudflare-ai-BfDkIjO8.js +133 -0
- package/dist/src/cloudflare-ai-D9HaXz1M.js +134 -0
- package/dist/src/cloudflare-ai-HrD5WUK8.js +132 -0
- package/dist/src/cloudflare-ai-Qdj8aKP2.cjs +133 -0
- package/dist/src/cloudflare-gateway-C81uKETQ.js +276 -0
- package/dist/src/cloudflare-gateway-CX5Z9X0k.js +276 -0
- package/dist/src/cloudflare-gateway-DW-og8gb.js +277 -0
- package/dist/src/cloudflare-gateway-iRyXo9n2.cjs +276 -0
- package/dist/src/codex-app-server-BFIrSgdy.js +2155 -0
- package/dist/src/codex-app-server-By1eDVD4.js +2 -0
- package/dist/src/codex-app-server-DKsF4qmY.js +2156 -0
- package/dist/src/codex-app-server-n7n4uZzC.cjs +2162 -0
- package/dist/src/codex-app-server-xAoELVxu.js +2155 -0
- package/dist/src/codex-sdk-C2ZLxa6r.js +1552 -0
- package/dist/src/codex-sdk-CzG02tQt.cjs +1588 -0
- package/dist/src/codex-sdk-DNxAOhiV.js +2 -0
- package/dist/src/codex-sdk-DT3aQezK.js +1557 -0
- package/dist/src/codex-sdk-pw9DpcJP.js +1557 -0
- package/dist/src/codex-security-B0bLmXJF.js +406 -0
- package/dist/src/codex-security-BhYs-3iY.js +406 -0
- package/dist/src/codex-security-DQcOfxYW.js +407 -0
- package/dist/src/codex-security-DsCWr6GZ.cjs +411 -0
- package/dist/src/cometapi-B16asPLX.cjs +55 -0
- package/dist/src/cometapi-MgTbZA_H.js +53 -0
- package/dist/src/cometapi-i33vKzPV.js +55 -0
- package/dist/src/cometapi-wK2UWK0X.js +56 -0
- package/dist/src/compatible-COj8ZCy9.js +24 -0
- package/dist/src/compatible-CRSsIUGb.cjs +24 -0
- package/dist/src/compatible-Dy1vCcNF.js +25 -0
- package/dist/src/compatible-aUItQrpk.js +24 -0
- package/dist/src/completion-Bn7COFMk.js +119 -0
- package/dist/src/completion-Bub2N48b.js +118 -0
- package/dist/src/completion-Cf2G9OlM.cjs +123 -0
- package/dist/src/conversationScoring-BiTYJ9MD.js +374 -0
- package/dist/src/conversationScoring-CKL47uIu.js +373 -0
- package/dist/src/conversationScoring-DCvQKp6k.cjs +398 -0
- package/dist/src/conversationScoring-DLNJtDZI.js +373 -0
- package/dist/src/crescendo-5iLk9WiJ.cjs +734 -0
- package/dist/src/crescendo-B5ylG7kW.js +733 -0
- package/dist/src/crescendo-BD7k6TIx.js +730 -0
- package/dist/src/crescendo-DqO2Ch0P.js +732 -0
- package/dist/src/custom-8rv_aG2K.js +617 -0
- package/dist/src/custom-BOtrIHFN.js +619 -0
- package/dist/src/custom-BvN8seCs.cjs +620 -0
- package/dist/src/custom-Cld9v_Y4.js +618 -0
- package/dist/src/dataUrl-BdHUey2l.cjs +3187 -0
- package/dist/src/dataUrl-DBMifX6C.js +3133 -0
- package/dist/src/dataUrl-DUSr-ZvI.js +2868 -0
- package/dist/src/decisions-CLazADTw.js +516 -0
- package/dist/src/decisions-D5X93awq.js +515 -0
- package/dist/src/decisions-DmaeArEn.js +515 -0
- package/dist/src/decisions-DyQJyHJz.cjs +514 -0
- package/dist/src/docker-C9NYQFeu.js +94 -0
- package/dist/src/docker-CGq3mMol.cjs +94 -0
- package/dist/src/docker-DOm7nmpl.js +95 -0
- package/dist/src/docker-skFN-Kfo.js +93 -0
- package/dist/src/embedding-0UbzkTk9.js +82 -0
- package/dist/src/embedding-DTrSDt7k.cjs +86 -0
- package/dist/src/embedding-j2ORNl5H.js +81 -0
- package/dist/src/envoy-BvS8XTQO.js +60 -0
- package/dist/src/envoy-Cn6Hzw0e.js +60 -0
- package/dist/src/envoy-DC3KWcUY.cjs +60 -0
- package/dist/src/envoy-DetHjo5B.js +61 -0
- package/dist/src/eval-Cer9dPe9.js +1920 -0
- package/dist/src/eval-CytYmI2Z.js +2 -0
- package/dist/src/evalResult-DzOq5wt9.js +1300 -0
- package/dist/src/evaluator-BxCmvGtv.js +10578 -0
- package/dist/src/evaluator-Ct7YDELm.js +2 -0
- package/dist/src/evaluatorHelpers-B_AwIrsl.js +600 -0
- package/dist/src/evaluatorHelpers-D1Tpc7gd.js +601 -0
- package/dist/src/evaluatorHelpers-DRR3rLq0.cjs +622 -0
- package/dist/src/fetch-B5KxZuKu.js +1559 -0
- package/dist/src/fetch-BG_Jl7ty.js +1556 -0
- package/dist/src/fetch-BKlgUg08.cjs +1853 -0
- package/dist/src/fetch-C9Fsja0R.js +2 -0
- package/dist/src/fetch-CM7P8KWA.cjs +14 -0
- package/dist/src/fetch-D0FkIB3G.js +9 -0
- package/dist/src/fetch-RXHsZF-J.js +1280 -0
- package/dist/src/fetch-rSECy8jR.js +10 -0
- package/dist/src/functionCallbackUtils-BB9tmf6a.cjs +262 -0
- package/dist/src/functionCallbackUtils-BXk2iY01.js +246 -0
- package/dist/src/functionCallbackUtils-B_cYp7P5.js +245 -0
- package/dist/src/functionCallbackUtils-BfXnPo0o.js +245 -0
- package/dist/src/gemini-image-BXLMADDz.js +471 -0
- package/dist/src/gemini-image-BYUZyEUx.js +472 -0
- package/dist/src/gemini-image-DSt1QubZ.cjs +471 -0
- package/dist/src/gemini-image-DdNDMiZ_.js +471 -0
- package/dist/src/goat-9b-uU_k6.js +514 -0
- package/dist/src/goat-BCJuiDd0.js +515 -0
- package/dist/src/goat-CmAHbemm.js +512 -0
- package/dist/src/goat-DdOTdyoS.cjs +515 -0
- package/dist/src/goblin-Bp5C5lyb.js +22 -0
- package/dist/src/goblin-Dmr7GItI.js +23 -0
- package/dist/src/goblin-DpM8wxlf.cjs +22 -0
- package/dist/src/goblin-cJyH6Pq-.js +22 -0
- package/dist/src/google-B6eaPAbE.js +142 -0
- package/dist/src/google-D-FStdN6.cjs +142 -0
- package/dist/src/google-DAC7BsCW.js +142 -0
- package/dist/src/google-Id18H0Va.js +143 -0
- package/dist/src/graders-BjNQWkaB.js +2 -0
- package/dist/src/graders-CqdIp_vl.js +15470 -0
- package/dist/src/hydra-6Ouh74DE.js +610 -0
- package/dist/src/hydra-BHjhZnLB.cjs +612 -0
- package/dist/src/hydra-BL6k2REV.js +2 -0
- package/dist/src/hydra-BWUc0o67.js +613 -0
- package/dist/src/hydra-CUkEOLKw.js +612 -0
- package/dist/src/image-B6hG_xhb.js +481 -0
- package/dist/src/image-BFaq7Krd.cjs +209 -0
- package/dist/src/image-Bql5C0N9.cjs +504 -0
- package/dist/src/image-BtZfI2pT.js +163 -0
- package/dist/src/image-CIM4oN7Z.cjs +163 -0
- package/dist/src/image-CbAl7Jpu.js +209 -0
- package/dist/src/image-CiYm_yFw.js +209 -0
- package/dist/src/image-DCMNvBiV.js +163 -0
- package/dist/src/image-DL7pIuUZ.js +210 -0
- package/dist/src/image-ey0kxF8o.js +164 -0
- package/dist/src/image-smgIk3rE.js +482 -0
- package/dist/src/index.cjs +27 -27
- package/dist/src/index.d.cts +8 -8
- package/dist/src/index.js +27 -27
- package/dist/src/indirectWebPwn-B_-UpWY5.js +285 -0
- package/dist/src/indirectWebPwn-Cz3SLiEq.js +434 -0
- package/dist/src/indirectWebPwn-D2s8zGiM.cjs +463 -0
- package/dist/src/indirectWebPwn-DXbj2wGu.cjs +285 -0
- package/dist/src/indirectWebPwn-D_4H44E6.js +285 -0
- package/dist/src/indirectWebPwn-L044SrpV.js +434 -0
- package/dist/src/indirectWebPwn-cnmvdavH.js +435 -0
- package/dist/src/indirectWebPwn-zUBzsqTt.js +286 -0
- package/dist/src/interactions-Bu3oVQ-j.js +850 -0
- package/dist/src/interactions-CND010Lh.cjs +850 -0
- package/dist/src/interactions-DAx7tqUz.js +851 -0
- package/dist/src/interactions-DPkIumeh.js +850 -0
- package/dist/src/iterative-BO4JRnpG.js +545 -0
- package/dist/src/iterative-CAK-k9rz.js +548 -0
- package/dist/src/iterative-CDgHc0xO.cjs +549 -0
- package/dist/src/iterative-CXJlHLl9.js +547 -0
- package/dist/src/iterativeImage-BEdGKSmh.cjs +419 -0
- package/dist/src/iterativeImage-BfL0QZw5.js +417 -0
- package/dist/src/iterativeImage-C9KhnibC.js +417 -0
- package/dist/src/iterativeImage-btjkOfWG.js +418 -0
- package/dist/src/iterativeMeta-B5Bdw6QA.js +437 -0
- package/dist/src/iterativeMeta-C3mI4gpT.js +440 -0
- package/dist/src/iterativeMeta-Co2BKyqS.cjs +439 -0
- package/dist/src/iterativeMeta-DD8ihRWD.js +439 -0
- package/dist/src/iterativeTree-AMi4GV2O.js +878 -0
- package/dist/src/iterativeTree-C8FJOEje.cjs +879 -0
- package/dist/src/iterativeTree-CvrY1f2Z.js +877 -0
- package/dist/src/iterativeTree-bPtESmaD.js +876 -0
- package/dist/src/knowledgeBase-Cf5871e3.js +163 -0
- package/dist/src/knowledgeBase-D2PQhqLH.js +164 -0
- package/dist/src/knowledgeBase-DpoMw1IH.cjs +163 -0
- package/dist/src/knowledgeBase-npGFl763.js +163 -0
- package/dist/src/litellm-BBB5DQdt.cjs +146 -0
- package/dist/src/litellm-Bo43ow7r.js +145 -0
- package/dist/src/litellm-CaU8CMLS.js +146 -0
- package/dist/src/litellm-wY7Unh8o.js +147 -0
- package/dist/src/live-5g2w-M-E.js +891 -0
- package/dist/src/live-BfaXsDyD.cjs +891 -0
- package/dist/src/live-C1xewnGD.js +2 -0
- package/dist/src/live-DaF7nYy5.js +892 -0
- package/dist/src/live-lp3urLUw.js +891 -0
- package/dist/src/luma-ray-B7zvKGVG.js +309 -0
- package/dist/src/luma-ray-DCf5fnrz.cjs +309 -0
- package/dist/src/luma-ray-DepBcn6z.js +309 -0
- package/dist/src/luma-ray-XQC4M1pt.js +310 -0
- package/dist/src/main.js +26 -26
- package/dist/src/mantleChat-7xm95vSV.js +154 -0
- package/dist/src/mantleChat-CXhAs5RD.js +155 -0
- package/dist/src/mantleChat-CcE-zjfj.cjs +154 -0
- package/dist/src/mantleChat-Cpefl7Zm.js +154 -0
- package/dist/src/mcp-B0V0ikdM.js +84 -0
- package/dist/src/mcp-CV2k2gwj.js +85 -0
- package/dist/src/memoryPoisoning-BC5FQQWZ.js +111 -0
- package/dist/src/memoryPoisoning-BDwrtZKX.js +111 -0
- package/dist/src/memoryPoisoning-BFemS_FF.js +112 -0
- package/dist/src/memoryPoisoning-DRs2rbVV.cjs +111 -0
- package/dist/src/messages-Bc5oZoAJ.js +1128 -0
- package/dist/src/messages-BkaH8fRW.js +1127 -0
- package/dist/src/messages-CoHlziS5.js +1127 -0
- package/dist/src/messages-Cv_m6kYs.cjs +1161 -0
- package/dist/src/mischievousUser-BcyFxrqJ.js +51 -0
- package/dist/src/mischievousUser-BlhOQSd2.js +52 -0
- package/dist/src/mischievousUser-BuQiKE8D.js +51 -0
- package/dist/src/mischievousUser-ChIOk6cT.cjs +51 -0
- package/dist/src/modelslab-BXIWBTgX.js +171 -0
- package/dist/src/modelslab-ChVsUiaJ.js +170 -0
- package/dist/src/modelslab-DZ6BFlDv.js +170 -0
- package/dist/src/modelslab-DhRr-vyo.cjs +170 -0
- package/dist/src/nova-reel-BL8DrN4_.js +272 -0
- package/dist/src/nova-reel-BiOMUm5q.cjs +272 -0
- package/dist/src/nova-reel-CTsya1hV.js +273 -0
- package/dist/src/nova-reel-kgBY4Svd.js +272 -0
- package/dist/src/nova-sonic-CaSSA3Jq.cjs +374 -0
- package/dist/src/nova-sonic-DLpmoGsR.js +375 -0
- package/dist/src/nova-sonic-DwWc4SoN.js +374 -0
- package/dist/src/nova-sonic-qnReTaXm.js +374 -0
- package/dist/src/novita-B5mAt3Ui.js +114 -0
- package/dist/src/novita-D57vCjDD.js +116 -0
- package/dist/src/novita-ITRtG8IJ.js +115 -0
- package/dist/src/novita-Rh3T13YO.cjs +115 -0
- package/dist/src/nscale-8V7iZ_pI.js +210 -0
- package/dist/src/nscale-BQnMv4pH.cjs +209 -0
- package/dist/src/nscale-BWZXHZVF.js +209 -0
- package/dist/src/nscale-BqrHiUqU.js +207 -0
- package/dist/src/openaiResponses-BMtGTu8R.js +317 -0
- package/dist/src/openaiResponses-CvcI_viE.js +318 -0
- package/dist/src/openaiResponses-DYPZEMX1.cjs +318 -0
- package/dist/src/openaiResponses-DZTg1-kb.js +319 -0
- package/dist/src/openclaw-BeKuig4g.js +1338 -0
- package/dist/src/openclaw-COkdQ83y.js +1337 -0
- package/dist/src/openclaw-CSoX_HQ5.cjs +1344 -0
- package/dist/src/openclaw-DJlhPGCb.js +1336 -0
- package/dist/src/opencode-sdk-BEJftEs6.cjs +1018 -0
- package/dist/src/opencode-sdk-DMvBB3V7.js +1013 -0
- package/dist/src/opencode-sdk-Df7hMjLO.js +1012 -0
- package/dist/src/opencode-sdk-bQp5Xf3W.js +1012 -0
- package/dist/src/openinterpreter-B3GgVZjK.js +431 -0
- package/dist/src/openinterpreter-BiGcU-HN.cjs +435 -0
- package/dist/src/openinterpreter-Buj2l3Eh.js +432 -0
- package/dist/src/openinterpreter-C3_Cg5b_.js +431 -0
- package/dist/src/pricing-0Pampf0q.cjs +999 -0
- package/dist/src/pricing-B8VdhEhY.js +976 -0
- package/dist/src/pricing-CtLA-rKA.js +976 -0
- package/dist/src/promptfoo-4OkHtH9P.cjs +341 -0
- package/dist/src/promptfoo-Bhpm7sH6.js +286 -0
- package/dist/src/promptfoo-BqV8ogVA.js +285 -0
- package/dist/src/promptfoo-S1Q9OHk_.js +286 -0
- package/dist/src/providers-B5zs1ZRv.js +24437 -0
- package/dist/src/providers-Cd6CJh6m.js +2 -0
- package/dist/src/providers-CpJlgHET.js +24039 -0
- package/dist/src/providers-CzvLvK6G.cjs +24180 -0
- package/dist/src/quiverai-1E_IvLHn.js +325 -0
- package/dist/src/quiverai-BXH004Vv.js +325 -0
- package/dist/src/quiverai-Ccya2z8Y.cjs +325 -0
- package/dist/src/quiverai-Cez57oe-.js +326 -0
- package/dist/src/registry-3zX_M9Ik.js +131 -0
- package/dist/src/registry-AuCrKTvS.js +132 -0
- package/dist/src/registry-CxXWKtQG.js +131 -0
- package/dist/src/registry-DG3o66JQ.cjs +131 -0
- package/dist/src/remoteGeneration-CAtvamG6.js +370 -0
- package/dist/src/remoteGeneration-CF225pDl.cjs +482 -0
- package/dist/src/remoteGeneration-DtnqocnL.js +370 -0
- package/dist/src/remoteGeneration-g3KvW-sP.js +371 -0
- package/dist/src/responses-BMbaUXQI.js +1098 -0
- package/dist/src/responses-B_TOSof-.js +1099 -0
- package/dist/src/responses-v-yANwTA.cjs +1109 -0
- package/dist/src/runtimeTransform-BBZv-oUZ.js +156 -0
- package/dist/src/runtimeTransform-DT2v5ovW.js +156 -0
- package/dist/src/runtimeTransform-GXXEIXdS.cjs +161 -0
- package/dist/src/runtimeTransform-JTwBQc-P.js +157 -0
- package/dist/src/sagemaker-B8D0L4rj.cjs +716 -0
- package/dist/src/sagemaker-CR2_AGdB.js +713 -0
- package/dist/src/sagemaker-Cd9qSQUV.js +714 -0
- package/dist/src/sagemaker-mKlltN4p.js +713 -0
- package/dist/src/scanner-PG_8lh8r.js +1971 -0
- package/dist/src/server/index.js +55 -55
- package/dist/src/server-Bmh7sd9F.js +155 -0
- package/dist/src/server-C7b8z6aC.js +63 -0
- package/dist/src/server-CAcovgtL.js +2 -0
- package/dist/src/server-CuuMfKlh.cjs +74 -0
- package/dist/src/server-DMPCj9jy.js +189 -0
- package/dist/src/session-BksXvRH_.cjs +69 -0
- package/dist/src/session-DOxUjqGQ.js +63 -0
- package/dist/src/session-Dujsc-b9.js +62 -0
- package/dist/src/session-VjtIxREd.js +62 -0
- package/dist/src/shared-Bkyz_7e5.cjs +2185 -0
- package/dist/src/shared-DDJg8ycC.js +1929 -0
- package/dist/src/shared-Dh3yfRBd.js +1927 -0
- package/dist/src/shared-tfPT8Tna.js +1928 -0
- package/dist/src/simulatedUser-6JdkyL3k.cjs +252 -0
- package/dist/src/simulatedUser-CFr2mr3M.js +248 -0
- package/dist/src/simulatedUser-DjwwUqx-.js +247 -0
- package/dist/src/slack-C_gIYiUs.js +144 -0
- package/dist/src/slack-DhNbz9CM.cjs +144 -0
- package/dist/src/slack-Dm2iI1zu.js +145 -0
- package/dist/src/strategies-BIY-bNul.js +2248 -0
- package/dist/src/strategies-C1KQQj9G.cjs +2420 -0
- package/dist/src/strategies-DM_3mReG.js +2385 -0
- package/dist/src/telemetry-BhMTibZO.js +2 -0
- package/dist/src/telemetry-C04d25BM.cjs +216 -0
- package/dist/src/telemetry-CIqy4fSC.js +201 -0
- package/dist/src/telemetry-CXozp_6C.js +205 -0
- package/dist/src/telemetry-g7PkBxmY.js +205 -0
- package/dist/src/togetherai-BvCim68-.js +50 -0
- package/dist/src/togetherai-CWyDCA9m.cjs +49 -0
- package/dist/src/togetherai-CeSE31vK.js +49 -0
- package/dist/src/togetherai-_2W0URf8.js +48 -0
- package/dist/src/traceContext-Brw3Oe41.js +1016 -0
- package/dist/src/traceContext-DpAYv27G.js +1015 -0
- package/dist/src/traceContext-KSqhzZI2.cjs +1032 -0
- package/dist/src/transcription-BySopazW.js +175 -0
- package/dist/src/transcription-DO4UPkRI.cjs +178 -0
- package/dist/src/transcription-RJGBa-pC.js +176 -0
- package/dist/src/transcription-fZenF3G5.js +175 -0
- package/dist/src/transform-BzhRc5z2.js +125 -0
- package/dist/src/transform-CPQu05u6.cjs +154 -0
- package/dist/src/transform-DAED8imp.js +126 -0
- package/dist/src/transform-DIOIQDmM.js +125 -0
- package/dist/src/typesafe-ByE0Kxfg.js +253 -0
- package/dist/src/typesafe-CFqGFY34.js +253 -0
- package/dist/src/typesafe-DCWIw-CP.cjs +253 -0
- package/dist/src/typesafe-DyHFV5Qk.js +254 -0
- package/dist/src/util-BHkj9joz.js +2049 -0
- package/dist/src/util-CICwIpmu.cjs +934 -0
- package/dist/src/util-CePiY2xZ.js +286 -0
- package/dist/src/util-CgT3uf4p.js +743 -0
- package/dist/src/util-Cj9drvOY.js +1770 -0
- package/dist/src/util-ClJRjlQA.cjs +357 -0
- package/dist/src/util-Cxf8VF2M.js +395 -0
- package/dist/src/util-D7RdV7rS.js +395 -0
- package/dist/src/util-DMNFhk47.js +351 -0
- package/dist/src/util-DNzh3M0z.js +396 -0
- package/dist/src/util-Df7rfMGb.cjs +2305 -0
- package/dist/src/util-Kf0KnG_6.js +2051 -0
- package/dist/src/util-LEbeiYw_.js +743 -0
- package/dist/src/util-WH8CgVs4.js +352 -0
- package/dist/src/util-cx6Xw8Lw.cjs +478 -0
- package/dist/src/util-qzBqirBH.js +2050 -0
- package/dist/src/vertex-CBAMfRz3.js +854 -0
- package/dist/src/vertex-CMP2IpN9.js +2 -0
- package/dist/src/vertex-CZ5YOotT.js +848 -0
- package/dist/src/vertex-DNsh1F1M.cjs +871 -0
- package/dist/src/vertexLive-BTXYj3-F.js +55 -0
- package/dist/src/vertexLive-Ba_LAqEF.cjs +55 -0
- package/dist/src/vertexLive-DM4sFaaj.js +56 -0
- package/dist/src/vertexLive-JqZOw7HC.js +55 -0
- package/dist/src/video-BY7Q2Gii.cjs +706 -0
- package/dist/src/video-CYElP-CK.js +703 -0
- package/dist/src/video-C_AdEDH0.js +702 -0
- package/dist/src/video-DGvjQ_4j.js +702 -0
- package/package.json +1 -1
- package/dist/src/accounts-Dpb43yde.cjs +0 -276
- package/dist/src/accounts-GKrO1olH.js +0 -229
- package/dist/src/accounts-e4FGOsji.js +0 -227
- package/dist/src/accounts-sUiEJfkB.js +0 -222
- package/dist/src/agentWorkspace-B8A_wuMR.cjs +0 -619
- package/dist/src/agentWorkspace-DSPIscXR.js +0 -593
- package/dist/src/agentWorkspace-DxUqd-XK.js +0 -592
- package/dist/src/agentWorkspace-n0uOJXhD.js +0 -592
- package/dist/src/agentic-utils-9bCj4twQ.js +0 -197
- package/dist/src/agentic-utils-CyCLuLoj.js +0 -197
- package/dist/src/agentic-utils-D23qtOlx.cjs +0 -237
- package/dist/src/agentic-utils-DuBMMNCw.js +0 -198
- package/dist/src/agents-6hDRNoYD.js +0 -777
- package/dist/src/agents-B8w2o7Sc.cjs +0 -777
- package/dist/src/agents-Ck0NXl7v.js +0 -777
- package/dist/src/agents-CwT3B1qB.js +0 -778
- package/dist/src/agents-DOLjfOEq.js +0 -266
- package/dist/src/agents-OcF9hq--.js +0 -267
- package/dist/src/agents-api-CnZZt8bn.cjs +0 -710
- package/dist/src/agents-api-D04u3bmL.js +0 -711
- package/dist/src/agents-api-DWw0_Vio.js +0 -710
- package/dist/src/agents-api-IWHEwp8i.js +0 -710
- package/dist/src/agents-mHt2Mh9O.cjs +0 -266
- package/dist/src/agents-sICccZCA.js +0 -266
- package/dist/src/ai.studio-Ci4rypDT.js +0 -2
- package/dist/src/ai.studio-KkQ6KaHa.js +0 -369
- package/dist/src/ai.studio-gSXK2cGI.js +0 -374
- package/dist/src/ai.studio-nQF_B4DH.cjs +0 -391
- package/dist/src/aimlapi-BeSk5BDV.js +0 -30
- package/dist/src/aimlapi-CzNUhdKu.cjs +0 -29
- package/dist/src/aimlapi-DCkazDPI.js +0 -29
- package/dist/src/aimlapi-WeH5B6K1.js +0 -28
- package/dist/src/anthropicMessages-AcNWTpYO.js +0 -115
- package/dist/src/anthropicMessages-BUtvzbpL.js +0 -114
- package/dist/src/anthropicMessages-DTWhZDCd.js +0 -114
- package/dist/src/anthropicMessages-lGVnSgyx.cjs +0 -114
- package/dist/src/app/assets/index-CyEI5aDN.js +0 -392
- package/dist/src/audio-BE_Duiu1.js +0 -104
- package/dist/src/audio-CiUd00k2.cjs +0 -104
- package/dist/src/audio-Dop6omna.js +0 -105
- package/dist/src/audio-EZNTInr_.js +0 -104
- package/dist/src/authoritativeMarkupInjection-BUqH6hNk.js +0 -78
- package/dist/src/authoritativeMarkupInjection-BmbJdJO0.cjs +0 -78
- package/dist/src/authoritativeMarkupInjection-Cpz12Y3w.js +0 -78
- package/dist/src/authoritativeMarkupInjection-W4XfcH91.js +0 -79
- package/dist/src/aws-B2Jqbnet.js +0 -2433
- package/dist/src/aws-DHtuUIzQ.js +0 -1149
- package/dist/src/aws-DLqXP6wY.js +0 -2433
- package/dist/src/aws-DfduhmwL.cjs +0 -2454
- package/dist/src/base-B9L2Xvpc.js +0 -255
- package/dist/src/base-BTZnIsuH.cjs +0 -305
- package/dist/src/base-BhaWNQPJ.js +0 -462
- package/dist/src/base-Br-G_Xou.js +0 -256
- package/dist/src/base-Bzo8hiUS.cjs +0 -467
- package/dist/src/base-C82fd2jq.js +0 -463
- package/dist/src/base-CRdQwC1W.js +0 -462
- package/dist/src/base-CjGwZNOQ.js +0 -255
- package/dist/src/bedrock-BdMru_ro.js +0 -1300
- package/dist/src/bedrock-CBUmfLX1.cjs +0 -2
- package/dist/src/bedrock-DBDxBaF9.js +0 -2
- package/dist/src/bedrock-DDrECM21.js +0 -1
- package/dist/src/bedrock-DVwkgvRS.js +0 -1
- package/dist/src/bestOfN-AKF5beq4.js +0 -159
- package/dist/src/bestOfN-C1UIVGJV.js +0 -160
- package/dist/src/bestOfN-DlkETbXy.cjs +0 -161
- package/dist/src/bestOfN-XIfEgU-g.js +0 -159
- package/dist/src/cache-BZaVoKaA.js +0 -2
- package/dist/src/cache-CEYhFUsH.js +0 -741
- package/dist/src/cache-Cbw1zH6q.js +0 -782
- package/dist/src/cache-CdaZNb75.js +0 -754
- package/dist/src/cache-DpuIzPxh.cjs +0 -856
- package/dist/src/cerebras-Bpn6FPkJ.js +0 -97
- package/dist/src/cerebras-CqJ0BRCJ.cjs +0 -97
- package/dist/src/cerebras-D4ILa_TY.js +0 -97
- package/dist/src/cerebras-DR8uc7ee.js +0 -98
- package/dist/src/chat-B7PZfaYp.cjs +0 -987
- package/dist/src/chat-BVuSjdEb.js +0 -227
- package/dist/src/chat-BsaZRjXN.js +0 -76
- package/dist/src/chat-C5liqe3Y.js +0 -226
- package/dist/src/chat-C9hyHf5B.js +0 -124
- package/dist/src/chat-CFLalAck.js +0 -226
- package/dist/src/chat-CN8JGrHu.js +0 -76
- package/dist/src/chat-CaHBBpCb.js +0 -123
- package/dist/src/chat-CcbLSghx.js +0 -123
- package/dist/src/chat-CoSN6sr2.js +0 -934
- package/dist/src/chat-CsJRFX8a.cjs +0 -226
- package/dist/src/chat-DFoZLvO_.js +0 -934
- package/dist/src/chat-DiLQvL7r.js +0 -77
- package/dist/src/chat-F6qVSDk3.cjs +0 -123
- package/dist/src/chat-Oq4FaJgI.cjs +0 -76
- package/dist/src/chat-knMfQUKz.js +0 -935
- package/dist/src/claude-agent-sdk-CJkOLpnI.js +0 -999
- package/dist/src/claude-agent-sdk-Ckn76PRD.js +0 -999
- package/dist/src/claude-agent-sdk-DX0gTr11.cjs +0 -1005
- package/dist/src/claude-agent-sdk-LMeikxoR.js +0 -1000
- package/dist/src/client-BFf1r6Z8.js +0 -352
- package/dist/src/client-CdXyDdDi.js +0 -351
- package/dist/src/client-DV8XMu7i.js +0 -351
- package/dist/src/client-iEXtOBKW.cjs +0 -357
- package/dist/src/cloud-B02qcpjf.js +0 -2
- package/dist/src/cloud-BlHP-g1v.cjs +0 -666
- package/dist/src/cloud-C9BI4isF.js +0 -287
- package/dist/src/cloud-CTTLNdvj.js +0 -657
- package/dist/src/cloud-D5X5yQ_d.js +0 -599
- package/dist/src/cloud-QxPkpsHA.js +0 -599
- package/dist/src/cloudflare-ai-DRTRT1fc.js +0 -132
- package/dist/src/cloudflare-ai-DgaXapAV.cjs +0 -133
- package/dist/src/cloudflare-ai-DkuJXijS.js +0 -134
- package/dist/src/cloudflare-ai-wy9QCHku.js +0 -133
- package/dist/src/cloudflare-gateway-9cAN8g29.cjs +0 -276
- package/dist/src/cloudflare-gateway-BVFMD292.js +0 -277
- package/dist/src/cloudflare-gateway-BuUI4p6I.js +0 -276
- package/dist/src/cloudflare-gateway-TlYHizbW.js +0 -276
- package/dist/src/codex-app-server-BPz_feug.cjs +0 -2162
- package/dist/src/codex-app-server-C9noXM9C.js +0 -2155
- package/dist/src/codex-app-server-CEmD-Y5U.js +0 -2
- package/dist/src/codex-app-server-DZolc18r.js +0 -2156
- package/dist/src/codex-app-server-Dd0A37nO.js +0 -2155
- package/dist/src/codex-sdk-BbxjBvBk.cjs +0 -1588
- package/dist/src/codex-sdk-BpXeIs-_.js +0 -1557
- package/dist/src/codex-sdk-BtQp5X0f.js +0 -1552
- package/dist/src/codex-sdk-CH0v9Bk1.js +0 -2
- package/dist/src/codex-sdk-CgrN7JB8.js +0 -1557
- package/dist/src/codex-security-B5fENNUr.js +0 -406
- package/dist/src/codex-security-C6uuJx-y.cjs +0 -411
- package/dist/src/codex-security-CifWtZR_.js +0 -407
- package/dist/src/codex-security-Kg2bOEKy.js +0 -406
- package/dist/src/cometapi-BFpIDLIx.js +0 -55
- package/dist/src/cometapi-C6O2PIrN.js +0 -56
- package/dist/src/cometapi-D5Yupc6F.js +0 -53
- package/dist/src/cometapi-DzgIwJzJ.cjs +0 -55
- package/dist/src/compatible-0jhmVfOp.js +0 -25
- package/dist/src/compatible-DdtwWdJi.cjs +0 -24
- package/dist/src/compatible-Di4nR3sU.js +0 -24
- package/dist/src/compatible-iJKLWdnf.js +0 -24
- package/dist/src/completion-B0_hVUDG.js +0 -119
- package/dist/src/completion-BdI6v0-Q.cjs +0 -123
- package/dist/src/completion-CWcJscwv.js +0 -118
- package/dist/src/conversationScoring-99EOPJRa.js +0 -373
- package/dist/src/conversationScoring-BIq4J0Cq.js +0 -374
- package/dist/src/conversationScoring-DARyeATr.cjs +0 -398
- package/dist/src/conversationScoring-DS48t4Hb.js +0 -373
- package/dist/src/crescendo-CebvXIu4.js +0 -730
- package/dist/src/crescendo-CxrLS0oo.js +0 -732
- package/dist/src/crescendo-DZQXBSkj.js +0 -733
- package/dist/src/crescendo-wjEx68uD.cjs +0 -734
- package/dist/src/custom-BiQ-aUNw.cjs +0 -620
- package/dist/src/custom-ByyB5Z93.js +0 -619
- package/dist/src/custom-C5jOevNb.js +0 -617
- package/dist/src/custom-CCrsCz8o.js +0 -618
- package/dist/src/dataUrl-C9J7wz9H.js +0 -3133
- package/dist/src/dataUrl-CGsBT7UZ.cjs +0 -3187
- package/dist/src/dataUrl-CzKvvWNO.js +0 -2868
- package/dist/src/decisions-BTusXCUx.cjs +0 -514
- package/dist/src/decisions-BYtL9edD.js +0 -515
- package/dist/src/decisions-jYix_gCF.js +0 -515
- package/dist/src/decisions-nxxfHRAN.js +0 -516
- package/dist/src/docker-DDIv4zkg.js +0 -93
- package/dist/src/docker-DVLp_5OG.js +0 -94
- package/dist/src/docker-INm9w0SY.cjs +0 -94
- package/dist/src/docker-sGuoCul_.js +0 -95
- package/dist/src/embedding-CAhqLLrR.js +0 -82
- package/dist/src/embedding-D94GAsti.cjs +0 -86
- package/dist/src/embedding-zb4MAzQy.js +0 -81
- package/dist/src/envoy-BMPszwEa.cjs +0 -60
- package/dist/src/envoy-Bp-s39_8.js +0 -60
- package/dist/src/envoy-BswDt-Ou.js +0 -61
- package/dist/src/envoy-rVbYcV80.js +0 -60
- package/dist/src/eval-B1-XfrC7.js +0 -2
- package/dist/src/eval-pC4qCvLw.js +0 -1920
- package/dist/src/evalResult-CzjCb5bd.js +0 -1300
- package/dist/src/evaluator-CNq2K7J_.js +0 -10578
- package/dist/src/evaluator-LtfxdxEu.js +0 -2
- package/dist/src/evaluatorHelpers-C5J0NYpQ.js +0 -601
- package/dist/src/evaluatorHelpers-DcdCglCs.cjs +0 -622
- package/dist/src/evaluatorHelpers-Rvb88W6B.js +0 -600
- package/dist/src/fetch-5HtnNhHg.js +0 -1556
- package/dist/src/fetch-BCwjoIFd.js +0 -1559
- package/dist/src/fetch-BhfUnKen.js +0 -1280
- package/dist/src/fetch-C08w4vVl.js +0 -9
- package/dist/src/fetch-DBxy8Tlb.cjs +0 -14
- package/dist/src/fetch-DYte6oQX.cjs +0 -1853
- package/dist/src/fetch-ElpfcAVG.js +0 -10
- package/dist/src/fetch-lM1OIaSt.js +0 -2
- package/dist/src/functionCallbackUtils-BCAdWfHO.js +0 -246
- package/dist/src/functionCallbackUtils-BYq9qOhG.js +0 -245
- package/dist/src/functionCallbackUtils-ByWkmVud.cjs +0 -262
- package/dist/src/functionCallbackUtils-D_C_JaFd.js +0 -245
- package/dist/src/gemini-image-CAlwaJhZ.js +0 -471
- package/dist/src/gemini-image-DJoVoOnZ.cjs +0 -471
- package/dist/src/gemini-image-Waio-9Xx.js +0 -472
- package/dist/src/gemini-image-vbY6Ftbf.js +0 -471
- package/dist/src/goat-BWbtyY9c.js +0 -512
- package/dist/src/goat-CXzzeCOQ.cjs +0 -515
- package/dist/src/goat-CfgvzSgZ.js +0 -514
- package/dist/src/goat-DLeoIHBp.js +0 -515
- package/dist/src/goblin-CmfCfwqk.cjs +0 -22
- package/dist/src/goblin-CtcaORRH.js +0 -22
- package/dist/src/goblin-DF6DDhM-.js +0 -22
- package/dist/src/goblin-Nai0xxBV.js +0 -23
- package/dist/src/google-ClpkLtKL.cjs +0 -142
- package/dist/src/google-CrFgKu9k.js +0 -143
- package/dist/src/google-Dj_EIBqS.js +0 -142
- package/dist/src/google-e4-vARv6.js +0 -142
- package/dist/src/graders-BN1IIa71.js +0 -15470
- package/dist/src/graders-C5sCfSP5.js +0 -2
- package/dist/src/hydra-B7RfXVBK.js +0 -2
- package/dist/src/hydra-C1cFN-Jg.js +0 -613
- package/dist/src/hydra-CK6Uc1ia.js +0 -612
- package/dist/src/hydra-Dg65rAP_.js +0 -610
- package/dist/src/hydra-Fqx8l_Xu.cjs +0 -612
- package/dist/src/image-BdlUDAtp.js +0 -163
- package/dist/src/image-C-KwEhwt.cjs +0 -163
- package/dist/src/image-C76fp6NK.cjs +0 -209
- package/dist/src/image-D75ehv3D.js +0 -482
- package/dist/src/image-DKtc5v5W.js +0 -209
- package/dist/src/image-DT_xOkJY.js +0 -163
- package/dist/src/image-DVpdllEE.js +0 -209
- package/dist/src/image-DXZ8AzMf.js +0 -481
- package/dist/src/image-DZl_1mvy.js +0 -164
- package/dist/src/image-DcTD7Ak0.cjs +0 -504
- package/dist/src/image-ndxEvBKB.js +0 -210
- package/dist/src/indirectWebPwn-70WA5x_q.js +0 -435
- package/dist/src/indirectWebPwn-BRVyd3qV.cjs +0 -463
- package/dist/src/indirectWebPwn-BsJVsu0q.js +0 -285
- package/dist/src/indirectWebPwn-Cw6yJu0i.js +0 -285
- package/dist/src/indirectWebPwn-D-PhJzo4.js +0 -286
- package/dist/src/indirectWebPwn-DRPHt2I8.js +0 -434
- package/dist/src/indirectWebPwn-DXtfFhd9.cjs +0 -285
- package/dist/src/indirectWebPwn-DvpNouzc.js +0 -434
- package/dist/src/interactions-5ixfW7Cl.js +0 -850
- package/dist/src/interactions-BOUzfmg1.cjs +0 -850
- package/dist/src/interactions-CSSXcGNc.js +0 -851
- package/dist/src/interactions-WB_sgkN0.js +0 -850
- package/dist/src/iterative-BShRNqjC.cjs +0 -549
- package/dist/src/iterative-BcW_ELtB.js +0 -548
- package/dist/src/iterative-BpHbHz8B.js +0 -547
- package/dist/src/iterative-YcchP12d.js +0 -545
- package/dist/src/iterativeImage-BMFE5Hl7.cjs +0 -419
- package/dist/src/iterativeImage-CkSaYLMY.js +0 -417
- package/dist/src/iterativeImage-DAEUlQX_.js +0 -418
- package/dist/src/iterativeImage-DeUSkEnj.js +0 -417
- package/dist/src/iterativeMeta-DbdaKdEO.cjs +0 -439
- package/dist/src/iterativeMeta-DcF9JWEK.js +0 -437
- package/dist/src/iterativeMeta-DkfPqWIC.js +0 -439
- package/dist/src/iterativeMeta-DkrpnVSs.js +0 -440
- package/dist/src/iterativeTree-CEMY1FhE.cjs +0 -879
- package/dist/src/iterativeTree-Cv5W4tX9.js +0 -877
- package/dist/src/iterativeTree-DGvomd3M.js +0 -876
- package/dist/src/iterativeTree-KbtC2Mkz.js +0 -878
- package/dist/src/knowledgeBase-AYRXn4wO.js +0 -163
- package/dist/src/knowledgeBase-BNqQfx01.js +0 -163
- package/dist/src/knowledgeBase-C0IaCCVs.js +0 -164
- package/dist/src/knowledgeBase-C2uj6wK4.cjs +0 -163
- package/dist/src/litellm-CN1ScAwZ.js +0 -147
- package/dist/src/litellm-CTjwiKSa.js +0 -146
- package/dist/src/litellm-Cd4Nj5ue.js +0 -145
- package/dist/src/litellm-DMqLpEBg.cjs +0 -146
- package/dist/src/live-Bt50-InO.cjs +0 -891
- package/dist/src/live-Bu2ujadG.js +0 -2
- package/dist/src/live-C3M52i3p.js +0 -891
- package/dist/src/live-CRfHMO3B.js +0 -892
- package/dist/src/live-iCIpO14m.js +0 -891
- package/dist/src/luma-ray-B8eHC4c8.cjs +0 -309
- package/dist/src/luma-ray-BIvkPw0s.js +0 -309
- package/dist/src/luma-ray-ChsiBxMA.js +0 -309
- package/dist/src/luma-ray-DAvKrInz.js +0 -310
- package/dist/src/mantleChat-5YeEXAZI.js +0 -154
- package/dist/src/mantleChat-D--mW3hl.js +0 -154
- package/dist/src/mantleChat-DHP3siNg.cjs +0 -154
- package/dist/src/mantleChat-wBfAX46o.js +0 -155
- package/dist/src/mcp-49jlEcTQ.js +0 -85
- package/dist/src/mcp-__PIz81I.js +0 -84
- package/dist/src/memoryPoisoning-Chxge74M.js +0 -111
- package/dist/src/memoryPoisoning-QH6NCUVE.js +0 -112
- package/dist/src/memoryPoisoning-tCxZt8Nz.js +0 -111
- package/dist/src/memoryPoisoning-ymrRItRR.cjs +0 -111
- package/dist/src/messages-BE_eZ7NA.js +0 -1127
- package/dist/src/messages-Bo638ShI.js +0 -1127
- package/dist/src/messages-BzR0-NQ1.js +0 -1128
- package/dist/src/messages-Cbg5Pfd0.cjs +0 -1161
- package/dist/src/mischievousUser-BtED2Z5Z.cjs +0 -51
- package/dist/src/mischievousUser-KqSOjrDJ.js +0 -51
- package/dist/src/mischievousUser-YMD41YNZ.js +0 -51
- package/dist/src/mischievousUser-zBxlNdCu.js +0 -52
- package/dist/src/modelslab-B8gZxtfE.js +0 -171
- package/dist/src/modelslab-C7bmVpTB.js +0 -170
- package/dist/src/modelslab-CCeQCTZg.js +0 -170
- package/dist/src/modelslab-CvBJGv7y.cjs +0 -170
- package/dist/src/nova-reel-BOEWsEKA.cjs +0 -272
- package/dist/src/nova-reel-BtyBXq5M.js +0 -273
- package/dist/src/nova-reel-C1eF3-EE.js +0 -272
- package/dist/src/nova-reel-YhgrxEKT.js +0 -272
- package/dist/src/nova-sonic-BaERP20w.js +0 -374
- package/dist/src/nova-sonic-CxgM3O-P.cjs +0 -374
- package/dist/src/nova-sonic-DJS22fC3.js +0 -375
- package/dist/src/nova-sonic-DwvwHyRX.js +0 -374
- package/dist/src/novita-D1l2tG0O.js +0 -116
- package/dist/src/novita-D84VM3v0.cjs +0 -115
- package/dist/src/novita-DGInvwfR.js +0 -115
- package/dist/src/novita-vXHkXhTg.js +0 -114
- package/dist/src/nscale-CU0Ldr00.js +0 -209
- package/dist/src/nscale-ClfKlQeM.cjs +0 -209
- package/dist/src/nscale-DxaVnF0x.js +0 -210
- package/dist/src/nscale-_poVxIrO.js +0 -207
- package/dist/src/openaiResponses-CJGsneNj.js +0 -317
- package/dist/src/openaiResponses-CKSl5Sdg.js +0 -319
- package/dist/src/openaiResponses-DC8rzFUc.js +0 -318
- package/dist/src/openaiResponses-rMHIA3dV.cjs +0 -318
- package/dist/src/openclaw-CCbvsNFW.cjs +0 -1344
- package/dist/src/openclaw-CZw1lCDZ.js +0 -1336
- package/dist/src/openclaw-ChFLgC-a.js +0 -1338
- package/dist/src/openclaw-DrPMoRrH.js +0 -1337
- package/dist/src/opencode-sdk-2EfIDhFp.cjs +0 -1018
- package/dist/src/opencode-sdk-CHw3F845.js +0 -1013
- package/dist/src/opencode-sdk-D0EGGxvy.js +0 -1012
- package/dist/src/opencode-sdk-jSS7NI2Q.js +0 -1012
- package/dist/src/openinterpreter-CY622xHC.js +0 -431
- package/dist/src/openinterpreter-DATyhuhP.js +0 -432
- package/dist/src/openinterpreter-DDUMyIf1.js +0 -431
- package/dist/src/openinterpreter-s5Tz6Lid.cjs +0 -435
- package/dist/src/pricing-C498twd2.js +0 -976
- package/dist/src/pricing-DBd_bMIh.cjs +0 -999
- package/dist/src/pricing-ISrMCskv.js +0 -976
- package/dist/src/promptfoo-6p0WG2ih.js +0 -286
- package/dist/src/promptfoo-BQRfgNs_.js +0 -285
- package/dist/src/promptfoo-Bqo88z20.cjs +0 -341
- package/dist/src/promptfoo-Cq5AVI3I.js +0 -286
- package/dist/src/providers-ATbeMX5E.cjs +0 -24180
- package/dist/src/providers-B7F1FiNI.js +0 -2
- package/dist/src/providers-DazIw8mI.js +0 -24039
- package/dist/src/providers-QvGlNAD0.js +0 -24437
- package/dist/src/quiverai-BPlNcLkV.cjs +0 -325
- package/dist/src/quiverai-By99-YmJ.js +0 -325
- package/dist/src/quiverai-CmmByDqo.js +0 -325
- package/dist/src/quiverai-HZkoJJqw.js +0 -326
- package/dist/src/registry-BLTnTfjT.cjs +0 -131
- package/dist/src/registry-BMC-COHT.js +0 -132
- package/dist/src/registry-C8UZtmBp.js +0 -131
- package/dist/src/registry-CfwEUYXG.js +0 -131
- package/dist/src/remoteGeneration-Bgg6kC5V.js +0 -370
- package/dist/src/remoteGeneration-CKJFE960.cjs +0 -482
- package/dist/src/remoteGeneration-DaxwJDKv.js +0 -370
- package/dist/src/remoteGeneration-vHMGVKr1.js +0 -371
- package/dist/src/responses-BvnyIHrq.js +0 -1099
- package/dist/src/responses-CERAgvf-.js +0 -1098
- package/dist/src/responses-CwgjeuOg.cjs +0 -1109
- package/dist/src/runtimeTransform-3RF9Avyg.cjs +0 -161
- package/dist/src/runtimeTransform-C51Z5j4H.js +0 -156
- package/dist/src/runtimeTransform-CKE_Iyv8.js +0 -157
- package/dist/src/runtimeTransform-Cs11xJpj.js +0 -156
- package/dist/src/sagemaker-BJ4dOCxE.js +0 -713
- package/dist/src/sagemaker-Bt3JUAWq.js +0 -714
- package/dist/src/sagemaker-QVKyLgLF.cjs +0 -716
- package/dist/src/sagemaker-l3IB8iTs.js +0 -713
- package/dist/src/scanner-DGjIiTZ1.js +0 -1971
- package/dist/src/server-BNaLZH6u.js +0 -63
- package/dist/src/server-CBMuQul3.js +0 -189
- package/dist/src/server-CMKKIzKv.cjs +0 -74
- package/dist/src/server-CN2IS9Cs.js +0 -155
- package/dist/src/server-ua4cqbjC.js +0 -2
- package/dist/src/session-CQIjq_HZ.cjs +0 -69
- package/dist/src/session-FTlg8sEu.js +0 -62
- package/dist/src/session-GBQXtPWp.js +0 -63
- package/dist/src/session-_0-rTdwY.js +0 -62
- package/dist/src/shared-BtuO-OAM.js +0 -1927
- package/dist/src/shared-Ci78wTZX.js +0 -1928
- package/dist/src/shared-Dd3rimdM.js +0 -1929
- package/dist/src/shared-KAjFxRre.cjs +0 -2185
- package/dist/src/simulatedUser--OjW6xCx.js +0 -247
- package/dist/src/simulatedUser-D0-slRye.cjs +0 -252
- package/dist/src/simulatedUser-D1Kh_cwA.js +0 -248
- package/dist/src/slack-BX0FUevO.js +0 -144
- package/dist/src/slack-CcwXFiJE.cjs +0 -144
- package/dist/src/slack-CnfqKfXp.js +0 -145
- package/dist/src/strategies-2ntcK4tN.js +0 -2248
- package/dist/src/strategies-DZIW7Zcs.js +0 -2385
- package/dist/src/strategies-YRDF2j-N.cjs +0 -2420
- package/dist/src/telemetry-CL8CVA4p.js +0 -205
- package/dist/src/telemetry-Cix90qqz.js +0 -201
- package/dist/src/telemetry-CkmlqTjV.js +0 -205
- package/dist/src/telemetry-DQH7l5bb.js +0 -2
- package/dist/src/telemetry-DUCXc1-r.cjs +0 -216
- package/dist/src/togetherai-3yUt5Ck7.js +0 -49
- package/dist/src/togetherai-BJUByLwt.cjs +0 -49
- package/dist/src/togetherai-Bh6KZkmd.js +0 -50
- package/dist/src/togetherai-DDl9yk3b.js +0 -48
- package/dist/src/traceContext-BiJVppjT.cjs +0 -1032
- package/dist/src/traceContext-CKKluULP.js +0 -1016
- package/dist/src/traceContext-D3Sk8fO-.js +0 -1015
- package/dist/src/transcription-BoVQKIVz.js +0 -175
- package/dist/src/transcription-Bs5u8780.js +0 -175
- package/dist/src/transcription-Dl654d1p.cjs +0 -178
- package/dist/src/transcription-VEvhM1Fu.js +0 -176
- package/dist/src/transform-BAbkGpvl.js +0 -125
- package/dist/src/transform-C_j4hUH8.js +0 -126
- package/dist/src/transform-Da86hs4M.cjs +0 -154
- package/dist/src/transform-_i1raQNo.js +0 -125
- package/dist/src/typesafe-CKXBeh82.cjs +0 -253
- package/dist/src/typesafe-D3VaGeNm.js +0 -253
- package/dist/src/typesafe-DYM-SFNH.js +0 -253
- package/dist/src/typesafe-LXmSbw8F.js +0 -254
- package/dist/src/util-41rb0XHX.js +0 -743
- package/dist/src/util-B0WlHYFM.js +0 -2049
- package/dist/src/util-BD1R_PsR.js +0 -1770
- package/dist/src/util-BLbM_ItC.js +0 -395
- package/dist/src/util-C72P0iIa.js +0 -352
- package/dist/src/util-C7MpGlJ6.cjs +0 -2305
- package/dist/src/util-CCtqagKc.js +0 -286
- package/dist/src/util-CM2Ec_uY.js +0 -2051
- package/dist/src/util-CSSAWO8v.js +0 -351
- package/dist/src/util-CkQc-fx3.cjs +0 -934
- package/dist/src/util-DcOlFTFa.cjs +0 -478
- package/dist/src/util-DluGTGVB.js +0 -395
- package/dist/src/util-PfQ60chz.js +0 -743
- package/dist/src/util-f14-CPij.js +0 -396
- package/dist/src/util-l9pXX3Eu.cjs +0 -357
- package/dist/src/util-o_BnlYqS.js +0 -2050
- package/dist/src/vertex-B_-gQgzc.js +0 -2
- package/dist/src/vertex-CU4gqoYa.js +0 -854
- package/dist/src/vertex-DZ42UHb3.js +0 -848
- package/dist/src/vertex-DsYQJA0T.cjs +0 -871
- package/dist/src/vertexLive-BvIaHKGB.js +0 -55
- package/dist/src/vertexLive-CTULqNx7.js +0 -56
- package/dist/src/vertexLive-DjJ_YQBe.cjs +0 -55
- package/dist/src/vertexLive-STUGcteB.js +0 -55
- package/dist/src/video-CAQGmtEV.js +0 -703
- package/dist/src/video-_FSTWlwX.js +0 -702
- package/dist/src/video-aBPJPWdt.js +0 -702
- package/dist/src/video-gDjkWv5m.cjs +0 -706
|
@@ -0,0 +1,1556 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.js";
|
|
2
|
+
import { B as loadYaml, G as getEnvInt, S as sanitizeUrl, U as getEnvBool, V as getConfigDirectoryPath, a as logRequestResponse, o as logger, q as getEnvString, rt as state, w as sanitizeUrlForLogging } from "./logger-CNHLbG5Y.js";
|
|
3
|
+
import { t as invariant } from "./invariant-CHP0zrix.js";
|
|
4
|
+
import { a as getRequestTimeoutMs, x as getEffectiveRequestSignal } from "./shared-BnN4RWgk.js";
|
|
5
|
+
import { n as sleep } from "./time-Db5rO0GY.js";
|
|
6
|
+
import path, * as path$1 from "path";
|
|
7
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
|
+
import * as fs$1 from "fs";
|
|
9
|
+
import * as yaml from "js-yaml";
|
|
10
|
+
import * as fsPromises$1 from "node:fs/promises";
|
|
11
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
12
|
+
import { Agent, ProxyAgent, getGlobalDispatcher, interceptors } from "undici";
|
|
13
|
+
import { promisify } from "util";
|
|
14
|
+
import { gzip } from "zlib";
|
|
15
|
+
//#region src/version.ts
|
|
16
|
+
/**
|
|
17
|
+
* Application version from package.json.
|
|
18
|
+
* Injected at build time, or read from npm environment in development.
|
|
19
|
+
*/
|
|
20
|
+
const VERSION = "0.125.1";
|
|
21
|
+
/**
|
|
22
|
+
* PostHog analytics key.
|
|
23
|
+
* Only populated during production builds via PROMPTFOO_POSTHOG_KEY env var.
|
|
24
|
+
* Empty string in development/test.
|
|
25
|
+
*/
|
|
26
|
+
const POSTHOG_KEY = "phc_E5n5uHnDo2eREJL1uqX1cIlbkoRby4yFWt3V94HqRRg";
|
|
27
|
+
function getShareApiBaseUrl() {
|
|
28
|
+
return getEnvString("PROMPTFOO_REMOTE_API_BASE_URL") || "https://api.promptfoo.app";
|
|
29
|
+
}
|
|
30
|
+
function getDefaultShareViewBaseUrl() {
|
|
31
|
+
return getEnvString("PROMPTFOO_SHARING_APP_BASE_URL", `https://promptfoo.app`);
|
|
32
|
+
}
|
|
33
|
+
function getShareViewBaseUrl() {
|
|
34
|
+
return getEnvString("PROMPTFOO_REMOTE_APP_BASE_URL") || getDefaultShareViewBaseUrl();
|
|
35
|
+
}
|
|
36
|
+
const TERMINAL_MAX_WIDTH = process?.stdout?.isTTY && process?.stdout?.columns && process?.stdout?.columns > 10 ? process?.stdout?.columns - 10 : 120;
|
|
37
|
+
const CLOUD_PROVIDER_PREFIX = "promptfoo://provider/";
|
|
38
|
+
const CONSENT_ENDPOINT = "https://api.promptfoo.dev/consent";
|
|
39
|
+
const EVENTS_ENDPOINT = "https://a.promptfoo.app";
|
|
40
|
+
const R_ENDPOINT = "https://r.promptfoo.app/";
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/globalConfig/globalConfig.ts
|
|
43
|
+
/**
|
|
44
|
+
* Functions for manipulating the global configuration file, which lives at
|
|
45
|
+
* ~/.promptfoo/promptfoo.yaml by default.
|
|
46
|
+
*/
|
|
47
|
+
function writeGlobalConfig(config) {
|
|
48
|
+
fs$1.writeFileSync(path$1.join(getConfigDirectoryPath(true), "promptfoo.yaml"), yaml.dump(config));
|
|
49
|
+
}
|
|
50
|
+
function readGlobalConfig() {
|
|
51
|
+
const configDir = getConfigDirectoryPath();
|
|
52
|
+
const configFilePath = path$1.join(configDir, "promptfoo.yaml");
|
|
53
|
+
let globalConfig = { id: crypto.randomUUID() };
|
|
54
|
+
if (fs$1.existsSync(configFilePath)) {
|
|
55
|
+
globalConfig = loadYaml(fs$1.readFileSync(configFilePath, "utf-8")) || {};
|
|
56
|
+
if (!globalConfig?.id) {
|
|
57
|
+
globalConfig = {
|
|
58
|
+
...globalConfig,
|
|
59
|
+
id: crypto.randomUUID()
|
|
60
|
+
};
|
|
61
|
+
writeGlobalConfig(globalConfig);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
if (!fs$1.existsSync(configDir)) fs$1.mkdirSync(configDir, { recursive: true });
|
|
65
|
+
fs$1.writeFileSync(configFilePath, yaml.dump(globalConfig));
|
|
66
|
+
}
|
|
67
|
+
return globalConfig;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Merges the top-level keys into existing config.
|
|
71
|
+
* @param partialConfig New keys to merge into the existing config.
|
|
72
|
+
*/
|
|
73
|
+
function writeGlobalConfigPartial(partialConfig) {
|
|
74
|
+
const updatedConfig = { ...readGlobalConfig() };
|
|
75
|
+
Object.entries(partialConfig).forEach(([key, value]) => {
|
|
76
|
+
if (value !== void 0 && value !== null) updatedConfig[key] = value;
|
|
77
|
+
else delete updatedConfig[key];
|
|
78
|
+
});
|
|
79
|
+
writeGlobalConfig(updatedConfig);
|
|
80
|
+
}
|
|
81
|
+
const CLOUD_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
82
|
+
new URL("https://api.promptfoo.app").hostname,
|
|
83
|
+
new URL("https://www.promptfoo.app").hostname,
|
|
84
|
+
new URL("https://promptfoo.app").hostname
|
|
85
|
+
]);
|
|
86
|
+
const SHARING_CUTOFF_DATE = /* @__PURE__ */ new Date("2026-03-09T00:00:00Z");
|
|
87
|
+
function isPromptfooCloudHost(url) {
|
|
88
|
+
try {
|
|
89
|
+
const hostname = new URL(url).hostname.toLowerCase().replace(/\.$/, "");
|
|
90
|
+
return CLOUD_HOSTNAMES.has(hostname);
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
let hasWarnedAboutLegacyApiHost = false;
|
|
96
|
+
function warnOnceAboutLegacyApiHost() {
|
|
97
|
+
if (hasWarnedAboutLegacyApiHost) return;
|
|
98
|
+
hasWarnedAboutLegacyApiHost = true;
|
|
99
|
+
logger.warn("Ignoring the API_HOST environment variable for Promptfoo Cloud routing. To point at a self-hosted deployment, use PROMPTFOO_CLOUD_API_URL or `promptfoo auth login --host <url>`.");
|
|
100
|
+
}
|
|
101
|
+
var CloudConfig = class {
|
|
102
|
+
configState = null;
|
|
103
|
+
constructor(initializeImmediately = true) {
|
|
104
|
+
if (initializeImmediately) this.config;
|
|
105
|
+
}
|
|
106
|
+
get config() {
|
|
107
|
+
this.configState ??= this.readConfig();
|
|
108
|
+
return this.configState;
|
|
109
|
+
}
|
|
110
|
+
set config(config) {
|
|
111
|
+
this.configState = config;
|
|
112
|
+
}
|
|
113
|
+
readConfig() {
|
|
114
|
+
const savedConfig = readGlobalConfig()?.cloud || {};
|
|
115
|
+
return {
|
|
116
|
+
appUrl: savedConfig.appUrl || "https://www.promptfoo.app",
|
|
117
|
+
apiHost: savedConfig.apiHost,
|
|
118
|
+
apiKey: savedConfig.apiKey,
|
|
119
|
+
authHeaderName: savedConfig.authHeaderName,
|
|
120
|
+
sharing: savedConfig.sharing,
|
|
121
|
+
currentOrganizationId: savedConfig.currentOrganizationId,
|
|
122
|
+
currentTeamId: savedConfig.currentTeamId,
|
|
123
|
+
teams: savedConfig.teams
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Returns the API key from config file or PROMPTFOO_API_KEY environment variable.
|
|
128
|
+
* Config file takes precedence over environment variable.
|
|
129
|
+
*/
|
|
130
|
+
resolveApiKey() {
|
|
131
|
+
return this.config.apiKey || process.env.PROMPTFOO_API_KEY;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns the API host from config file, PROMPTFOO_CLOUD_API_URL environment variable,
|
|
135
|
+
* or defaults to the standard cloud API host.
|
|
136
|
+
* Config file takes precedence over environment variable.
|
|
137
|
+
*
|
|
138
|
+
* Trailing slashes are stripped so callers that append a path (e.g.
|
|
139
|
+
* `${getApiHost()}/api/v1/...`) never produce a double slash. On-prem hosts
|
|
140
|
+
* entered via `promptfoo auth login --host https://host/` commonly include one.
|
|
141
|
+
*/
|
|
142
|
+
resolveApiHost() {
|
|
143
|
+
const host = this.config.apiHost || process.env.PROMPTFOO_CLOUD_API_URL || "https://api.promptfoo.app";
|
|
144
|
+
if (!this.config.apiHost && !process.env.PROMPTFOO_CLOUD_API_URL && process.env.API_HOST && this.resolveApiKey()) warnOnceAboutLegacyApiHost();
|
|
145
|
+
return host.replace(/\/+$/, "");
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns the header name used to carry the Cloud API credential, from config file,
|
|
149
|
+
* PROMPTFOO_CLOUD_AUTH_HEADER environment variable, or the default `Authorization`.
|
|
150
|
+
* Config file takes precedence over environment variable, matching resolveApiHost().
|
|
151
|
+
*
|
|
152
|
+
* process.env is read directly (not getEnvString) for the same reason as
|
|
153
|
+
* PROMPTFOO_CLOUD_API_URL: an eval config's `env` block must never be able to
|
|
154
|
+
* influence Cloud auth routing.
|
|
155
|
+
*/
|
|
156
|
+
resolveAuthHeaderName() {
|
|
157
|
+
return this.config.authHeaderName || process.env.PROMPTFOO_CLOUD_AUTH_HEADER || "Authorization";
|
|
158
|
+
}
|
|
159
|
+
isEnabled() {
|
|
160
|
+
return !!this.resolveApiKey();
|
|
161
|
+
}
|
|
162
|
+
setApiHost(apiHost) {
|
|
163
|
+
this.config.apiHost = apiHost.replace(/\/+$/, "");
|
|
164
|
+
this.saveConfig();
|
|
165
|
+
}
|
|
166
|
+
setApiKey(apiKey) {
|
|
167
|
+
this.config.apiKey = apiKey;
|
|
168
|
+
this.saveConfig();
|
|
169
|
+
}
|
|
170
|
+
getApiKey() {
|
|
171
|
+
return this.resolveApiKey();
|
|
172
|
+
}
|
|
173
|
+
getApiHost() {
|
|
174
|
+
return this.resolveApiHost();
|
|
175
|
+
}
|
|
176
|
+
setAuthHeaderName(authHeaderName) {
|
|
177
|
+
this.config.authHeaderName = authHeaderName;
|
|
178
|
+
this.saveConfig();
|
|
179
|
+
}
|
|
180
|
+
getAuthHeaderName() {
|
|
181
|
+
return this.resolveAuthHeaderName();
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Returns the header(s) to attach to a Cloud API request for the current credential,
|
|
185
|
+
* or `undefined` when no API key is resolved. Callers should spread the result
|
|
186
|
+
* conditionally (`...(cloudConfig.getAuthHeaders() ?? {})`) rather than sending a
|
|
187
|
+
* header with a `Bearer undefined` value.
|
|
188
|
+
*/
|
|
189
|
+
getAuthHeaders() {
|
|
190
|
+
const token = this.getApiKey();
|
|
191
|
+
if (!token) return;
|
|
192
|
+
return { [this.getAuthHeaderName()]: `Bearer ${token}` };
|
|
193
|
+
}
|
|
194
|
+
setAppUrl(appUrl) {
|
|
195
|
+
this.config.appUrl = appUrl;
|
|
196
|
+
this.saveConfig();
|
|
197
|
+
}
|
|
198
|
+
getAppUrl() {
|
|
199
|
+
return this.config.appUrl;
|
|
200
|
+
}
|
|
201
|
+
getSharing() {
|
|
202
|
+
return this.config.sharing;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Sets the sharing preference. Note: this value is only updated at authentication time
|
|
206
|
+
* (via `validateAndSetApiToken`) and may become stale if the user's license status
|
|
207
|
+
* changes between re-authentications.
|
|
208
|
+
*/
|
|
209
|
+
setSharing(sharing) {
|
|
210
|
+
this.config.sharing = sharing;
|
|
211
|
+
this.saveConfig();
|
|
212
|
+
}
|
|
213
|
+
delete() {
|
|
214
|
+
writeGlobalConfigPartial({ cloud: {} });
|
|
215
|
+
this.reload();
|
|
216
|
+
}
|
|
217
|
+
saveConfig() {
|
|
218
|
+
writeGlobalConfigPartial({ cloud: this.config });
|
|
219
|
+
this.reload();
|
|
220
|
+
}
|
|
221
|
+
reload() {
|
|
222
|
+
this.config = this.readConfig();
|
|
223
|
+
}
|
|
224
|
+
saveValidatedApiToken(token, apiHost, user, app, hasActiveLicense, authHeaderName) {
|
|
225
|
+
this.setApiKey(token);
|
|
226
|
+
this.setApiHost(apiHost);
|
|
227
|
+
this.setAppUrl(app.url);
|
|
228
|
+
if (authHeaderName) this.setAuthHeaderName(authHeaderName);
|
|
229
|
+
if (!(isPromptfooCloudHost(apiHost) || isPromptfooCloudHost(app.url))) this.setSharing(true);
|
|
230
|
+
else if (typeof hasActiveLicense === "boolean") {
|
|
231
|
+
const createdAt = user?.createdAt ? new Date(user.createdAt) : null;
|
|
232
|
+
const isGrandfathered = createdAt != null && createdAt < SHARING_CUTOFF_DATE;
|
|
233
|
+
this.setSharing(hasActiveLicense || isGrandfathered);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async validateApiToken(token, apiHost, authHeaderName) {
|
|
237
|
+
try {
|
|
238
|
+
const { fetchWithProxy } = await Promise.resolve().then(() => fetch_exports);
|
|
239
|
+
const response = await fetchWithProxy(`${apiHost}/api/v1/users/me`, {
|
|
240
|
+
headers: { [authHeaderName || this.getAuthHeaderName()]: `Bearer ${token}` },
|
|
241
|
+
skipCloudAuthInjection: true
|
|
242
|
+
});
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
const errorMessage = await response.text();
|
|
245
|
+
logger.error(`[Cloud] Failed to validate API token: ${errorMessage}. HTTP Status: ${response.status} - ${response.statusText}.`);
|
|
246
|
+
throw new Error("Failed to validate API token: " + response.statusText);
|
|
247
|
+
}
|
|
248
|
+
const { user, organization, app, hasActiveLicense } = await response.json();
|
|
249
|
+
return {
|
|
250
|
+
user,
|
|
251
|
+
organization,
|
|
252
|
+
app,
|
|
253
|
+
...typeof hasActiveLicense === "boolean" ? { hasActiveLicense } : {}
|
|
254
|
+
};
|
|
255
|
+
} catch (err) {
|
|
256
|
+
const error = err;
|
|
257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
258
|
+
logger.error(`[Cloud] Failed to validate API token with host ${apiHost}: ${errorMessage}`);
|
|
259
|
+
if (error.cause) logger.error(`Cause: ${error.cause}`);
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async validateAndSetApiToken(token, apiHost) {
|
|
264
|
+
const authHeaderName = this.getAuthHeaderName();
|
|
265
|
+
const { user, organization, app, hasActiveLicense } = await this.validateApiToken(token, apiHost, authHeaderName);
|
|
266
|
+
this.saveValidatedApiToken(token, apiHost, user, app, hasActiveLicense, authHeaderName);
|
|
267
|
+
return {
|
|
268
|
+
user,
|
|
269
|
+
organization,
|
|
270
|
+
app,
|
|
271
|
+
hasActiveLicense: typeof hasActiveLicense === "boolean" ? hasActiveLicense : false
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
getCurrentOrganizationId() {
|
|
275
|
+
return this.config.currentOrganizationId;
|
|
276
|
+
}
|
|
277
|
+
setCurrentOrganization(organizationId) {
|
|
278
|
+
this.config.currentOrganizationId = organizationId;
|
|
279
|
+
this.saveConfig();
|
|
280
|
+
}
|
|
281
|
+
getCurrentTeamId(organizationId) {
|
|
282
|
+
if (organizationId) return this.config.teams?.[organizationId]?.currentTeamId;
|
|
283
|
+
return this.config.currentTeamId;
|
|
284
|
+
}
|
|
285
|
+
setCurrentTeamId(teamId, organizationId) {
|
|
286
|
+
if (organizationId) {
|
|
287
|
+
if (!this.config.teams) this.config.teams = {};
|
|
288
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
289
|
+
this.config.teams[organizationId].currentTeamId = teamId;
|
|
290
|
+
} else this.config.currentTeamId = teamId;
|
|
291
|
+
this.saveConfig();
|
|
292
|
+
}
|
|
293
|
+
clearCurrentTeamId(organizationId) {
|
|
294
|
+
if (organizationId) {
|
|
295
|
+
if (this.config.teams?.[organizationId]) delete this.config.teams[organizationId].currentTeamId;
|
|
296
|
+
} else delete this.config.currentTeamId;
|
|
297
|
+
this.saveConfig();
|
|
298
|
+
}
|
|
299
|
+
cacheTeams(teams, organizationId) {
|
|
300
|
+
if (organizationId) {
|
|
301
|
+
if (!this.config.teams) this.config.teams = {};
|
|
302
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
303
|
+
this.config.teams[organizationId].cache = teams.map((t) => ({
|
|
304
|
+
id: t.id,
|
|
305
|
+
name: t.name,
|
|
306
|
+
slug: t.slug,
|
|
307
|
+
lastFetched: (/* @__PURE__ */ new Date()).toISOString()
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
this.saveConfig();
|
|
311
|
+
}
|
|
312
|
+
getCachedTeams(organizationId) {
|
|
313
|
+
if (organizationId) return this.config.teams?.[organizationId]?.cache;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const cloudConfig = new CloudConfig(false);
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/scheduler/headerParser.ts
|
|
319
|
+
const OPENAI_HEADERS = {
|
|
320
|
+
remainingRequests: "x-ratelimit-remaining-requests",
|
|
321
|
+
remainingTokens: "x-ratelimit-remaining-tokens",
|
|
322
|
+
limitRequests: "x-ratelimit-limit-requests",
|
|
323
|
+
limitTokens: "x-ratelimit-limit-tokens",
|
|
324
|
+
resetRequests: "x-ratelimit-reset-requests",
|
|
325
|
+
resetTokens: "x-ratelimit-reset-tokens"
|
|
326
|
+
};
|
|
327
|
+
const ANTHROPIC_HEADERS = {
|
|
328
|
+
remainingRequests: "anthropic-ratelimit-requests-remaining",
|
|
329
|
+
remainingTokens: "anthropic-ratelimit-tokens-remaining",
|
|
330
|
+
limitRequests: "anthropic-ratelimit-requests-limit",
|
|
331
|
+
limitTokens: "anthropic-ratelimit-tokens-limit",
|
|
332
|
+
resetRequests: "anthropic-ratelimit-requests-reset",
|
|
333
|
+
resetTokens: "anthropic-ratelimit-tokens-reset"
|
|
334
|
+
};
|
|
335
|
+
const STANDARD_HEADERS = {
|
|
336
|
+
remaining: "ratelimit-remaining",
|
|
337
|
+
limit: "ratelimit-limit",
|
|
338
|
+
reset: "ratelimit-reset",
|
|
339
|
+
remainingAlt: "x-ratelimit-remaining",
|
|
340
|
+
limitAlt: "x-ratelimit-limit",
|
|
341
|
+
resetAlt: "x-ratelimit-reset"
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Parse rate limit headers from response.
|
|
345
|
+
*/
|
|
346
|
+
function parseRateLimitHeaders(headers) {
|
|
347
|
+
const result = {};
|
|
348
|
+
const h = lowercaseKeys(headers);
|
|
349
|
+
result.remainingRequests = parseFirstMatch(h, [
|
|
350
|
+
OPENAI_HEADERS.remainingRequests,
|
|
351
|
+
ANTHROPIC_HEADERS.remainingRequests,
|
|
352
|
+
STANDARD_HEADERS.remainingAlt,
|
|
353
|
+
STANDARD_HEADERS.remaining
|
|
354
|
+
]);
|
|
355
|
+
result.remainingTokens = parseFirstMatch(h, [OPENAI_HEADERS.remainingTokens, ANTHROPIC_HEADERS.remainingTokens]);
|
|
356
|
+
result.limitRequests = parseFirstMatch(h, [
|
|
357
|
+
OPENAI_HEADERS.limitRequests,
|
|
358
|
+
ANTHROPIC_HEADERS.limitRequests,
|
|
359
|
+
STANDARD_HEADERS.limitAlt,
|
|
360
|
+
STANDARD_HEADERS.limit
|
|
361
|
+
]);
|
|
362
|
+
result.limitTokens = parseFirstMatch(h, [OPENAI_HEADERS.limitTokens, ANTHROPIC_HEADERS.limitTokens]);
|
|
363
|
+
for (const name of [
|
|
364
|
+
OPENAI_HEADERS.resetRequests,
|
|
365
|
+
OPENAI_HEADERS.resetTokens,
|
|
366
|
+
ANTHROPIC_HEADERS.resetRequests,
|
|
367
|
+
ANTHROPIC_HEADERS.resetTokens,
|
|
368
|
+
STANDARD_HEADERS.resetAlt,
|
|
369
|
+
STANDARD_HEADERS.reset
|
|
370
|
+
]) if (h[name] !== void 0) {
|
|
371
|
+
const parsed = parseResetTime(h[name]);
|
|
372
|
+
if (parsed !== null) {
|
|
373
|
+
result.resetAt = parsed;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (h["retry-after-ms"] !== void 0) {
|
|
378
|
+
const ms = Number.parseInt(h["retry-after-ms"], 10);
|
|
379
|
+
if (Number.isFinite(ms) && ms >= 0) {
|
|
380
|
+
result.retryAfterMs = ms;
|
|
381
|
+
if (result.resetAt === void 0) result.resetAt = Date.now() + ms;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (result.retryAfterMs === void 0 && h["retry-after"] !== void 0) {
|
|
385
|
+
const parsed = parseRetryAfter(h["retry-after"]);
|
|
386
|
+
if (parsed !== null) {
|
|
387
|
+
result.retryAfterMs = parsed;
|
|
388
|
+
if (result.resetAt === void 0) result.resetAt = Date.now() + parsed;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Parse Retry-After header value.
|
|
395
|
+
* Returns duration in milliseconds.
|
|
396
|
+
* Exported for integration use.
|
|
397
|
+
*/
|
|
398
|
+
function parseRetryAfter(value) {
|
|
399
|
+
const seconds = Number.parseInt(value, 10);
|
|
400
|
+
const durationMs = seconds * 1e3;
|
|
401
|
+
if (Number.isFinite(seconds) && seconds >= 0 && String(seconds) === value.trim() && Number.isFinite(durationMs)) return durationMs;
|
|
402
|
+
const httpDate = parseHttpDate(value);
|
|
403
|
+
if (httpDate !== null) return Math.max(0, httpDate - Date.now());
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
function parseFirstMatch(headers, names) {
|
|
407
|
+
for (const name of names) {
|
|
408
|
+
const value = headers[name];
|
|
409
|
+
if (value !== void 0) {
|
|
410
|
+
const num = Number.parseInt(value, 10);
|
|
411
|
+
if (Number.isFinite(num) && num >= 0) return num;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* A bare non-negative decimal number and nothing else: "0", "120", "1.5".
|
|
417
|
+
*
|
|
418
|
+
* `Number.parseFloat` stops at the first character it cannot consume, so
|
|
419
|
+
* without this guard it silently reads the leading digits of a timestamp
|
|
420
|
+
* ("2026-08-07T21:00:00Z" -> 2026) and the magnitude heuristic below then
|
|
421
|
+
* treats them as relative seconds. Same intent as the `String(seconds) ===
|
|
422
|
+
* value.trim()` check in `parseRetryAfter`.
|
|
423
|
+
*/
|
|
424
|
+
const BARE_NUMBER_RE = /^\d+(?:\.\d+)?$/;
|
|
425
|
+
/**
|
|
426
|
+
* Parse reset time from various formats.
|
|
427
|
+
* Returns absolute Unix timestamp in milliseconds.
|
|
428
|
+
*/
|
|
429
|
+
function parseResetTime(value) {
|
|
430
|
+
const durationMs = parseDuration(value);
|
|
431
|
+
if (durationMs !== null) return Date.now() + durationMs;
|
|
432
|
+
if (BARE_NUMBER_RE.test(value.trim())) {
|
|
433
|
+
const num = Number.parseFloat(value);
|
|
434
|
+
if (Number.isFinite(num) && num >= 0) {
|
|
435
|
+
if (num < 1e9) return Date.now() + num * 1e3;
|
|
436
|
+
else if (num < 1e10) return num * 1e3;
|
|
437
|
+
else return num;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const httpDate = parseHttpDate(value);
|
|
441
|
+
if (httpDate !== null) return httpDate;
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Parse HTTP-date format (RFC 7231).
|
|
446
|
+
*/
|
|
447
|
+
function parseHttpDate(value) {
|
|
448
|
+
const timestamp = Date.parse(value);
|
|
449
|
+
if (Number.isFinite(timestamp)) {
|
|
450
|
+
const now = Date.now();
|
|
451
|
+
const oneYearMs = 31536e6;
|
|
452
|
+
if (timestamp > now - oneYearMs && timestamp < now + oneYearMs) return timestamp;
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Parse duration strings like "1s", "100ms", "1m30s", "1h30s", "2h15m30s".
|
|
458
|
+
*
|
|
459
|
+
* Supported formats:
|
|
460
|
+
* - Xms (milliseconds)
|
|
461
|
+
* - Xs or X.Xs (seconds)
|
|
462
|
+
* - Xm or XmYs (minutes with optional seconds)
|
|
463
|
+
* - Xh or XhYm or XhYs or XhYmZs (hours with optional minutes/seconds)
|
|
464
|
+
*/
|
|
465
|
+
function parseDuration(value) {
|
|
466
|
+
const match = value.match(/^(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+(?:\.\d+)?)(ms|s))?$/);
|
|
467
|
+
if (!match) return null;
|
|
468
|
+
const [, hours, minutes, secondsValue, secondsUnit] = match;
|
|
469
|
+
if (!hours && !minutes && !secondsValue) return null;
|
|
470
|
+
let ms = 0;
|
|
471
|
+
if (hours) ms += Number.parseInt(hours, 10) * 36e5;
|
|
472
|
+
if (minutes) ms += Number.parseInt(minutes, 10) * 6e4;
|
|
473
|
+
if (secondsValue) {
|
|
474
|
+
const num = Number.parseFloat(secondsValue);
|
|
475
|
+
ms += secondsUnit === "ms" ? num : num * 1e3;
|
|
476
|
+
}
|
|
477
|
+
return Number.isFinite(ms) ? ms : null;
|
|
478
|
+
}
|
|
479
|
+
function lowercaseKeys(obj) {
|
|
480
|
+
const result = {};
|
|
481
|
+
for (const [key, value] of Object.entries(obj)) result[key.toLowerCase()] = value;
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
//#endregion
|
|
485
|
+
//#region src/util/fetch/cloudAuthRedirects.ts
|
|
486
|
+
var CloudAuthRedirectError = class extends Error {
|
|
487
|
+
constructor() {
|
|
488
|
+
super("Cloud authentication cannot follow a redirect to a different origin. Use the final API URL with promptfoo auth login --host.");
|
|
489
|
+
this.name = "CloudAuthRedirectError";
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
function unwrapCloudAuthRedirectError(error) {
|
|
493
|
+
return error instanceof Error && "cause" in error && error.cause instanceof CloudAuthRedirectError ? error.cause : error;
|
|
494
|
+
}
|
|
495
|
+
/** Keep native fetch's redirect handling, but bind this authenticated request to its initial origin. */
|
|
496
|
+
function restrictCloudAuthRedirects(url, dispatcher = getGlobalDispatcher()) {
|
|
497
|
+
const origin = new URL(url).origin;
|
|
498
|
+
const dispatch = (options, handler) => {
|
|
499
|
+
if (!options.origin || new URL(options.origin.toString()).origin !== origin) throw new CloudAuthRedirectError();
|
|
500
|
+
return dispatcher.dispatch(options, handler);
|
|
501
|
+
};
|
|
502
|
+
return new Proxy({ dispatch }, { get(target, property) {
|
|
503
|
+
return property === "dispatch" ? target.dispatch : Reflect.get(dispatcher, property, dispatcher);
|
|
504
|
+
} });
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/util/fetch/decompress.ts
|
|
508
|
+
/** Create the required decoder without exposing undici's internal API notice to CLI users. */
|
|
509
|
+
function createDecompressionInterceptor() {
|
|
510
|
+
const originalEmitWarning = process.emitWarning;
|
|
511
|
+
const wrappedEmitWarning = ((warning, ...args) => {
|
|
512
|
+
if (warning === "DecompressInterceptor is experimental and subject to change" && args[0] === "ExperimentalWarning") return;
|
|
513
|
+
Reflect.apply(originalEmitWarning, process, [warning, ...args]);
|
|
514
|
+
});
|
|
515
|
+
process.emitWarning = wrappedEmitWarning;
|
|
516
|
+
try {
|
|
517
|
+
return interceptors.decompress({
|
|
518
|
+
skipErrorResponses: false,
|
|
519
|
+
maxSize: 67108864
|
|
520
|
+
});
|
|
521
|
+
} finally {
|
|
522
|
+
if (process.emitWarning === wrappedEmitWarning) process.emitWarning = originalEmitWarning;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function hasContentEncoding(headers) {
|
|
526
|
+
if ("content-encoding" in headers) return true;
|
|
527
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === "content-encoding");
|
|
528
|
+
}
|
|
529
|
+
var StripEncodingHandler = class {
|
|
530
|
+
#handler;
|
|
531
|
+
constructor(handler) {
|
|
532
|
+
this.#handler = handler;
|
|
533
|
+
}
|
|
534
|
+
onRequestStart(controller, context) {
|
|
535
|
+
return this.#handler.onRequestStart?.(controller, context);
|
|
536
|
+
}
|
|
537
|
+
onRequestUpgrade(controller, statusCode, headers, socket) {
|
|
538
|
+
return this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket);
|
|
539
|
+
}
|
|
540
|
+
onResponseStart(controller, statusCode, headers, statusMessage) {
|
|
541
|
+
const ctrl = controller;
|
|
542
|
+
if (Array.isArray(ctrl.rawHeaders) && !hasContentEncoding(headers)) {
|
|
543
|
+
const filtered = [];
|
|
544
|
+
let rawHadContentEncoding = false;
|
|
545
|
+
const pairCount = ctrl.rawHeaders.length - ctrl.rawHeaders.length % 2;
|
|
546
|
+
for (let i = 0; i < pairCount; i += 2) {
|
|
547
|
+
const name = ctrl.rawHeaders[i].toString().toLowerCase();
|
|
548
|
+
if (name === "content-encoding") rawHadContentEncoding = true;
|
|
549
|
+
else if (name !== "content-length") filtered.push(ctrl.rawHeaders[i], ctrl.rawHeaders[i + 1]);
|
|
550
|
+
}
|
|
551
|
+
if (rawHadContentEncoding) {
|
|
552
|
+
if (ctrl.rawHeaders.length % 2 === 1) filtered.push(ctrl.rawHeaders[ctrl.rawHeaders.length - 1]);
|
|
553
|
+
ctrl.rawHeaders = filtered;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return this.#handler.onResponseStart?.(controller, statusCode, headers, statusMessage);
|
|
557
|
+
}
|
|
558
|
+
onResponseStarted() {
|
|
559
|
+
return this.#handler.onResponseStarted?.();
|
|
560
|
+
}
|
|
561
|
+
onResponseData(controller, chunk) {
|
|
562
|
+
return this.#handler.onResponseData?.(controller, chunk);
|
|
563
|
+
}
|
|
564
|
+
onResponseEnd(controller, trailers) {
|
|
565
|
+
return this.#handler.onResponseEnd?.(controller, trailers);
|
|
566
|
+
}
|
|
567
|
+
onResponseError(controller, err) {
|
|
568
|
+
return this.#handler.onResponseError?.(controller, err);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
function stripDecompressionHeaders() {
|
|
572
|
+
return (dispatch) => (opts, handler) => dispatch(opts, new StripEncodingHandler(handler));
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/util/fetch/errors.ts
|
|
576
|
+
/**
|
|
577
|
+
* Body-level error codes that indicate a hard quota/billing failure rather than
|
|
578
|
+
* a transient per-window rate limit. Retrying these will not succeed and just
|
|
579
|
+
* amplifies load and cost; callers should fail fast.
|
|
580
|
+
*
|
|
581
|
+
* References:
|
|
582
|
+
* - OpenAI: https://platform.openai.com/docs/guides/error-codes/api-errors
|
|
583
|
+
* - Azure OpenAI: https://learn.microsoft.com/en-us/azure/ai-services/openai/reference
|
|
584
|
+
* - Anthropic: https://platform.claude.com/docs/en/api/errors
|
|
585
|
+
*
|
|
586
|
+
* Note: Azure OpenAI is known to return `insufficient_quota` for both billing
|
|
587
|
+
* exhaustion AND per-minute deployment quota saturation. The
|
|
588
|
+
* {@link HttpRateLimitError} constructor downgrades such an ambiguous quota
|
|
589
|
+
* code to `rate_limit` when the server also hints at a near-term recovery
|
|
590
|
+
* (`Retry-After` / reset header) — a billing server has no reason to hint at
|
|
591
|
+
* recovery time. The codes in {@link DEFINITIVE_BILLING_ERROR_CODES} name a
|
|
592
|
+
* billing state explicitly and are never downgraded.
|
|
593
|
+
*/
|
|
594
|
+
const HARD_QUOTA_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
595
|
+
"insufficient_quota",
|
|
596
|
+
"billing_hard_limit_reached",
|
|
597
|
+
"billing_not_active",
|
|
598
|
+
"access_terminated",
|
|
599
|
+
"quota_exceeded",
|
|
600
|
+
"credit_balance_exhausted"
|
|
601
|
+
]);
|
|
602
|
+
/**
|
|
603
|
+
* Subset of {@link HARD_QUOTA_ERROR_CODES} that unambiguously describes the
|
|
604
|
+
* account's billing state (no credits, hard limit reached, billing inactive,
|
|
605
|
+
* access terminated). Unlike `insufficient_quota` / `quota_exceeded`, these
|
|
606
|
+
* are not reused for per-window throttling, so a `Retry-After` next to them
|
|
607
|
+
* (some gateways attach one to every 429) must not turn them retryable.
|
|
608
|
+
*/
|
|
609
|
+
const DEFINITIVE_BILLING_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
610
|
+
"credit_balance_exhausted",
|
|
611
|
+
"billing_hard_limit_reached",
|
|
612
|
+
"billing_not_active",
|
|
613
|
+
"access_terminated"
|
|
614
|
+
]);
|
|
615
|
+
/**
|
|
616
|
+
* Body codes that name a per-window throttle outright (OpenAI / Azure OpenAI
|
|
617
|
+
* `rate_limit_exceeded`, Anthropic `rate_limit_error` / `overloaded_error`,
|
|
618
|
+
* per-minute token / request buckets). An AMBIGUOUS hard-quota `type`
|
|
619
|
+
* (`insufficient_quota`, `quota_exceeded`) is only a fallback for an
|
|
620
|
+
* unrecognized `code`; next to one of these it never promotes the error to
|
|
621
|
+
* `quota`, whether or not the server sent a `Retry-After` / reset header. A
|
|
622
|
+
* {@link DEFINITIVE_BILLING_ERROR_CODES} type still does, because it names the
|
|
623
|
+
* account's billing state rather than guessing at it.
|
|
624
|
+
*/
|
|
625
|
+
const TRANSIENT_RATE_LIMIT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
626
|
+
"rate_limit_exceeded",
|
|
627
|
+
"rate_limit_error",
|
|
628
|
+
"rate_limit",
|
|
629
|
+
"rate_limited",
|
|
630
|
+
"overloaded_error",
|
|
631
|
+
"too_many_requests",
|
|
632
|
+
"tokens",
|
|
633
|
+
"requests",
|
|
634
|
+
"tokens_per_min",
|
|
635
|
+
"requests_per_min",
|
|
636
|
+
"tokens_per_day",
|
|
637
|
+
"requests_per_day"
|
|
638
|
+
]);
|
|
639
|
+
/**
|
|
640
|
+
* Upper bound for the "server hinted a recovery time, so this isn't billing"
|
|
641
|
+
* heuristic. A server that says "retry in <= 1 hour" (via `Retry-After`
|
|
642
|
+
* or a reset timestamp) is signalling a per-window throttle, not a billing
|
|
643
|
+
* exhaustion. Above 1h we treat the hint as ambiguous and let the body code
|
|
644
|
+
* decide.
|
|
645
|
+
*/
|
|
646
|
+
const RATE_LIMIT_QUOTA_DOWNGRADE_THRESHOLD_MS = 36e5;
|
|
647
|
+
/**
|
|
648
|
+
* Normalize a caller-supplied millisecond duration / timestamp: a finite,
|
|
649
|
+
* non-negative number passes through; anything else maps to `undefined`.
|
|
650
|
+
*/
|
|
651
|
+
function normalizeNonNegativeMs(value) {
|
|
652
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Structured error representing a final HTTP rate limit (status 429 or an
|
|
656
|
+
* equivalent header signal) — i.e. retries are exhausted, or the response
|
|
657
|
+
* code indicates a hard quota that should not be retried.
|
|
658
|
+
*
|
|
659
|
+
* Distinguishes:
|
|
660
|
+
* - `kind: 'quota'` — daily / billing / contractual exhaustion. Don't retry.
|
|
661
|
+
* - `kind: 'rate_limit'` — per-window throttling. Retry honoring `retryAfterMs`.
|
|
662
|
+
*
|
|
663
|
+
* The rendered `message` always contains the kind prefix (`"Rate limit
|
|
664
|
+
* exceeded"` or `"Quota exceeded"`) plus the literal status code. When the
|
|
665
|
+
* status is 429 (the typical construction path) the message also contains
|
|
666
|
+
* `"429"` and the default `"Too Many Requests"` statusText, which is what
|
|
667
|
+
* legacy substring classifiers downstream rely on.
|
|
668
|
+
*
|
|
669
|
+
* The `Http` prefix on the name scopes this to the network transport
|
|
670
|
+
* boundary, distinguishing it from rate-limit-shaped errors in other
|
|
671
|
+
* layers (e.g. an in-process scheduler queue exhausting its budget).
|
|
672
|
+
*/
|
|
673
|
+
var HttpRateLimitError = class extends Error {
|
|
674
|
+
status;
|
|
675
|
+
statusText;
|
|
676
|
+
retryAfterMs;
|
|
677
|
+
resetAt;
|
|
678
|
+
code;
|
|
679
|
+
type;
|
|
680
|
+
kind;
|
|
681
|
+
headers;
|
|
682
|
+
body;
|
|
683
|
+
constructor(init) {
|
|
684
|
+
const status = init.status;
|
|
685
|
+
const statusText = init.statusText || "Too Many Requests";
|
|
686
|
+
const retryAfterMs = normalizeNonNegativeMs(init.retryAfterMs);
|
|
687
|
+
const resetAt = normalizeNonNegativeMs(init.resetAt);
|
|
688
|
+
const typeImpliesQuota = isHardQuotaCode(init.type) && (isDefinitiveBillingCode(init.type) || !isTransientRateLimitCode(init.code));
|
|
689
|
+
let kind = isHardQuotaCode(init.code) || typeImpliesQuota ? "quota" : "rate_limit";
|
|
690
|
+
if (kind === "quota" && !isDefinitiveBillingCode(init.code) && !isDefinitiveBillingCode(init.type) && hasNearTermRecoveryHint(retryAfterMs, resetAt)) kind = "rate_limit";
|
|
691
|
+
const prefix = kind === "quota" ? "Quota exceeded" : "Rate limit exceeded";
|
|
692
|
+
const codeSuffix = init.code ? ` (code: ${init.code})` : "";
|
|
693
|
+
super(`${prefix}: HTTP ${status} ${statusText}${codeSuffix}`);
|
|
694
|
+
this.name = "HttpRateLimitError";
|
|
695
|
+
this.status = status;
|
|
696
|
+
this.statusText = statusText;
|
|
697
|
+
this.retryAfterMs = retryAfterMs;
|
|
698
|
+
this.resetAt = resetAt;
|
|
699
|
+
this.code = init.code;
|
|
700
|
+
this.type = init.type;
|
|
701
|
+
this.kind = kind;
|
|
702
|
+
this.headers = init.headers ? { ...init.headers } : void 0;
|
|
703
|
+
this.body = init.body;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
function isHardQuotaCode(code) {
|
|
707
|
+
return code !== void 0 && HARD_QUOTA_ERROR_CODES.has(code);
|
|
708
|
+
}
|
|
709
|
+
function isDefinitiveBillingCode(code) {
|
|
710
|
+
return code !== void 0 && DEFINITIVE_BILLING_ERROR_CODES.has(code);
|
|
711
|
+
}
|
|
712
|
+
function isTransientRateLimitCode(code) {
|
|
713
|
+
return code !== void 0 && TRANSIENT_RATE_LIMIT_ERROR_CODES.has(code);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Whether the server advertised a recovery within
|
|
717
|
+
* {@link RATE_LIMIT_QUOTA_DOWNGRADE_THRESHOLD_MS}: an explicit `Retry-After`,
|
|
718
|
+
* or a reset timestamp whose remaining wait (from now) is within the bound.
|
|
719
|
+
* A reset timestamp in the past counts as "recovers now".
|
|
720
|
+
*/
|
|
721
|
+
function hasNearTermRecoveryHint(retryAfterMs, resetAt) {
|
|
722
|
+
if (retryAfterMs !== void 0) return retryAfterMs <= RATE_LIMIT_QUOTA_DOWNGRADE_THRESHOLD_MS;
|
|
723
|
+
if (resetAt !== void 0) return resetAt - Date.now() <= RATE_LIMIT_QUOTA_DOWNGRADE_THRESHOLD_MS;
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Render the per-window retry-after / reset detail for a structured
|
|
728
|
+
* rate-limit error. Returns empty string when:
|
|
729
|
+
* - No retry metadata is available, OR
|
|
730
|
+
* - The error is a hard quota (`kind === 'quota'`) — retries won't help,
|
|
731
|
+
* so showing a "retry after Xs" hint would contradict the quota message.
|
|
732
|
+
*/
|
|
733
|
+
function formatRateLimitDetail(err) {
|
|
734
|
+
if (err.kind === "quota") return "";
|
|
735
|
+
const parts = [];
|
|
736
|
+
if (typeof err.retryAfterMs === "number") parts.push(`retry after ${Math.round(err.retryAfterMs / 1e3)}s`);
|
|
737
|
+
else if (typeof err.resetAt === "number") {
|
|
738
|
+
const remainingMs = Math.max(err.resetAt - Date.now(), 0);
|
|
739
|
+
if (remainingMs > 0) parts.push(`resets in ${Math.round(remainingMs / 1e3)}s`);
|
|
740
|
+
}
|
|
741
|
+
return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Render the user-facing error string for a structured rate-limit / quota
|
|
745
|
+
* failure. Single-prefix format (no provider-specific wrapper duplication):
|
|
746
|
+
*
|
|
747
|
+
* "Rate limit exceeded: HTTP 429 Too Many Requests (code: rate_limit_exceeded) [retry after 7s]"
|
|
748
|
+
* "Quota exceeded: HTTP 429 Too Many Requests (code: insufficient_quota). Retries will not help — check your billing or daily quota."
|
|
749
|
+
*
|
|
750
|
+
* `details` (optional) is appended verbatim before the kind-specific
|
|
751
|
+
* trailer. Use it to surface upstream-provided context (e.g. an SDK
|
|
752
|
+
* error's full message that names the deployment / token count) that the
|
|
753
|
+
* structured error itself doesn't carry.
|
|
754
|
+
*/
|
|
755
|
+
function formatRateLimitErrorMessage(err, details) {
|
|
756
|
+
const codePart = err.code ? ` (code: ${err.code})` : "";
|
|
757
|
+
const trailingDetails = details ? ` ${details}` : "";
|
|
758
|
+
if (err.kind === "quota") return `Quota exceeded: HTTP ${err.status} ${err.statusText}${codePart}${trailingDetails}. Retries will not help — check your billing or daily quota.`;
|
|
759
|
+
const detail = formatRateLimitDetail(err);
|
|
760
|
+
return `Rate limit exceeded: HTTP ${err.status} ${err.statusText}${codePart}${trailingDetails}${detail}`;
|
|
761
|
+
}
|
|
762
|
+
function isHttpRateLimitError(err) {
|
|
763
|
+
return err instanceof HttpRateLimitError;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Best-effort extraction of an error code from a parsed response body across
|
|
767
|
+
* provider variants (OpenAI / Azure / Anthropic / generic JSON-RPC style).
|
|
768
|
+
*/
|
|
769
|
+
function extractRateLimitErrorCode(body) {
|
|
770
|
+
if (typeof body !== "object" || body === null) return;
|
|
771
|
+
const root = body;
|
|
772
|
+
if (typeof root.error === "object" && root.error !== null) {
|
|
773
|
+
const err = root.error;
|
|
774
|
+
const metadata = err.metadata;
|
|
775
|
+
const providerCode = typeof metadata === "object" && metadata !== null ? metadata.provider_code : void 0;
|
|
776
|
+
if (typeof providerCode === "string" && isDefinitiveBillingCode(providerCode.toLowerCase())) return providerCode.toLowerCase();
|
|
777
|
+
if (typeof err.code === "string" && err.code.length > 0) return err.code;
|
|
778
|
+
}
|
|
779
|
+
if (typeof root.code === "string" && root.code.length > 0) return root.code;
|
|
780
|
+
return extractRateLimitErrorType(body);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Best-effort extraction of the broad error class (`error.type` / top-level
|
|
784
|
+
* `type`) from a parsed response body. OpenAI pairs `type: "insufficient_quota"`
|
|
785
|
+
* with billing-specific codes such as `credit_balance_exhausted`, so the type
|
|
786
|
+
* still carries the quota classification when the code is not recognized.
|
|
787
|
+
* Returns `undefined` when the body has no separate `type` field.
|
|
788
|
+
*/
|
|
789
|
+
function extractRateLimitErrorType(body) {
|
|
790
|
+
if (typeof body !== "object" || body === null) return;
|
|
791
|
+
const root = body;
|
|
792
|
+
const nested = typeof root.error === "object" && root.error !== null ? root.error.type : void 0;
|
|
793
|
+
if (typeof nested === "string" && nested.length > 0) return nested;
|
|
794
|
+
return typeof root.type === "string" && root.type.length > 0 ? root.type : void 0;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Non-transient HTTP status codes that indicate the target is unavailable or misconfigured.
|
|
798
|
+
* These errors will not resolve on retry and should abort the scan immediately.
|
|
799
|
+
*
|
|
800
|
+
* - 401: Unauthorized - authentication required or invalid credentials
|
|
801
|
+
* - 403: Forbidden - valid credentials but access denied
|
|
802
|
+
* - 404: Not Found - target endpoint doesn't exist
|
|
803
|
+
* - 501: Not Implemented - server doesn't support the request method
|
|
804
|
+
*
|
|
805
|
+
* Excluded: 500 (often transient — server crashes, DB timeouts, deployment rollouts,
|
|
806
|
+
* or input-dependent bugs where one prompt triggers it but the next doesn't),
|
|
807
|
+
* 502/503/504 (typically transient gateway issues).
|
|
808
|
+
*/
|
|
809
|
+
const NON_TRANSIENT_HTTP_STATUSES = [
|
|
810
|
+
401,
|
|
811
|
+
403,
|
|
812
|
+
404,
|
|
813
|
+
501
|
|
814
|
+
];
|
|
815
|
+
function isNonTransientHttpStatus(status) {
|
|
816
|
+
return NON_TRANSIENT_HTTP_STATUSES.includes(status);
|
|
817
|
+
}
|
|
818
|
+
function isTransientConnectionError(error) {
|
|
819
|
+
if (!error) return false;
|
|
820
|
+
const code = error.code;
|
|
821
|
+
if (code === "ECONNRESET" || code === "EPIPE") return true;
|
|
822
|
+
const message = (error.message ?? "").toLowerCase();
|
|
823
|
+
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;
|
|
824
|
+
return message.includes("bad record mac") || message.includes("eproto") || message.includes("econnreset") || message.includes("socket hang up");
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* True when an error represents an aborted/cancelled operation (an AbortController
|
|
828
|
+
* signal firing). Callers use this to suppress expected cancellation rather than
|
|
829
|
+
* treating it as a real failure, so the abort's `.name` must be preserved upstream.
|
|
830
|
+
*/
|
|
831
|
+
function isAbortError(error) {
|
|
832
|
+
return error instanceof Error && (error.name === "AbortError" || error.name === "AbortException");
|
|
833
|
+
}
|
|
834
|
+
function getAbortError(signal) {
|
|
835
|
+
const reason = signal.reason;
|
|
836
|
+
if (reason instanceof Error && reason.name === "AbortError") return reason;
|
|
837
|
+
const error = new Error(reason instanceof Error ? reason.message : "Request was aborted");
|
|
838
|
+
error.name = "AbortError";
|
|
839
|
+
return error;
|
|
840
|
+
}
|
|
841
|
+
//#endregion
|
|
842
|
+
//#region src/util/fetch/monkeyPatchFetch.ts
|
|
843
|
+
const gzipAsync = promisify(gzip);
|
|
844
|
+
const PROMPTFOO_TEAM_ID_HEADER = "x-promptfoo-team-id";
|
|
845
|
+
function isConnectionError(error) {
|
|
846
|
+
return error instanceof TypeError && error.message === "fetch failed" && error.cause?.stack?.includes("internalConnectMultiple");
|
|
847
|
+
}
|
|
848
|
+
/** Extracts the request URL as a string. A `Request`'s `toString()` is "[object Request]", so read `.url`. */
|
|
849
|
+
function getRequestUrlString(url) {
|
|
850
|
+
return url instanceof Request ? url.url : url.toString();
|
|
851
|
+
}
|
|
852
|
+
function matchesNoLogUrl(url, noLogUrl) {
|
|
853
|
+
try {
|
|
854
|
+
const target = new URL(url);
|
|
855
|
+
const excluded = new URL(noLogUrl);
|
|
856
|
+
if (target.origin !== excluded.origin) return false;
|
|
857
|
+
const excludedPath = excluded.pathname.replace(/\/+$/, "");
|
|
858
|
+
return excludedPath === "" || target.pathname === excludedPath || target.pathname.startsWith(`${excludedPath}/`);
|
|
859
|
+
} catch {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function getSafeUrlForConnectionLog(url) {
|
|
864
|
+
return sanitizeUrl(getRequestUrlString(url));
|
|
865
|
+
}
|
|
866
|
+
function getSafeProxyForConnectionLog() {
|
|
867
|
+
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
|
|
868
|
+
return proxyUrl ? `or Proxy: ${sanitizeUrl(proxyUrl)}` : "";
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Returns true when `url` targets the configured Promptfoo Cloud origin: the public
|
|
872
|
+
* cloud host by default, or the on-prem host when one is configured via
|
|
873
|
+
* `cloudConfig.getApiHost()`. Matching is scheme+host+port exact (`URL.origin`), so
|
|
874
|
+
* look-alike hosts, HTTP downgrades, and different ports never match. Matching is
|
|
875
|
+
* origin-wide — every path on the configured cloud origin is treated as cloud, so the
|
|
876
|
+
* saved token may also reach sibling services hosted on that same origin. Fails closed
|
|
877
|
+
* (returns false) when either URL is unparseable, so a misconfigured host never leaks
|
|
878
|
+
* the token.
|
|
879
|
+
*/
|
|
880
|
+
function isPromptfooCloudApiHost(url) {
|
|
881
|
+
try {
|
|
882
|
+
const targetUrl = url instanceof Request ? url.url : url.toString();
|
|
883
|
+
return new URL(targetUrl).origin === new URL(cloudConfig.getApiHost()).origin;
|
|
884
|
+
} catch {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Resolves the header name used to carry the Cloud API credential (defaults to
|
|
890
|
+
* `Authorization`, but may be configured to a different name via
|
|
891
|
+
* `promptfoo auth login --auth-header-name` or `PROMPTFOO_CLOUD_AUTH_HEADER`).
|
|
892
|
+
*/
|
|
893
|
+
function getCloudAuthHeaderName() {
|
|
894
|
+
return cloudConfig.getAuthHeaderName();
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Resolves the auth header value for a request to the configured Promptfoo
|
|
898
|
+
* Cloud origin, or `undefined` when the request is not cloud-bound or no API key is
|
|
899
|
+
* saved. Centralizing this keeps the live request (`monkeyPatchFetch`) and the cache
|
|
900
|
+
* key (`getHeadersForCacheKey` in cache.ts) in lockstep.
|
|
901
|
+
*/
|
|
902
|
+
function getCloudBearerToken(url) {
|
|
903
|
+
if (!isPromptfooCloudApiHost(url)) return;
|
|
904
|
+
const token = cloudConfig.getApiKey();
|
|
905
|
+
return token ? `Bearer ${token}` : void 0;
|
|
906
|
+
}
|
|
907
|
+
function isPromptfooCloudTaskUrl(url) {
|
|
908
|
+
if (!isPromptfooCloudApiHost(url)) return false;
|
|
909
|
+
try {
|
|
910
|
+
const pathname = new URL(getRequestUrlString(url)).pathname.replace(/\/+$/, "");
|
|
911
|
+
return pathname.endsWith("/api/v1/task") || pathname.endsWith("/api/v1/task/harmful");
|
|
912
|
+
} catch {
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/** Returns the persisted CLI team for authenticated Cloud task requests. */
|
|
917
|
+
function getCloudTaskTeamId(url) {
|
|
918
|
+
if (!isPromptfooCloudTaskUrl(url)) return;
|
|
919
|
+
if (!getCloudBearerToken(url)) return;
|
|
920
|
+
const organizationId = cloudConfig.getCurrentOrganizationId();
|
|
921
|
+
return cloudConfig.getCurrentTeamId(organizationId);
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Resolves the caller-supplied headers for a request: the explicit `options.headers` when
|
|
925
|
+
* present, otherwise the headers carried by a `Request` URL.
|
|
926
|
+
*
|
|
927
|
+
* `monkeyPatchFetch`'s only in-repo caller (`fetchWithProxy`) always normalizes headers to
|
|
928
|
+
* a plain object, but the function is exported and typed to accept any `HeadersInit` /
|
|
929
|
+
* `Request`, so the helpers here preserve every shape rather than assuming a `Record`.
|
|
930
|
+
*/
|
|
931
|
+
function getEffectiveHeaders(url, headers) {
|
|
932
|
+
return headers ?? (url instanceof Request ? url.headers : void 0);
|
|
933
|
+
}
|
|
934
|
+
function hasHeader(headers, name) {
|
|
935
|
+
return new Headers(headers).has(name);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Returns `headers` with `name: value` set. Plain objects retain their shape, while a
|
|
939
|
+
* `Headers` instance or `[name, value][]` array is normalized through `Headers` so existing
|
|
940
|
+
* entries are never dropped (a naive object spread would discard them).
|
|
941
|
+
*/
|
|
942
|
+
function setHeader(headers, name, value) {
|
|
943
|
+
if (headers instanceof Headers || Array.isArray(headers)) {
|
|
944
|
+
const merged = new Headers(headers);
|
|
945
|
+
merged.set(name, value);
|
|
946
|
+
return merged;
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
...headers ?? {},
|
|
950
|
+
[name]: value
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function usesCustomCloudAuth(url, headers, explicitCloudAuth = false) {
|
|
954
|
+
const effectiveHeaders = new Headers(headers);
|
|
955
|
+
if (explicitCloudAuth) return Array.from(effectiveHeaders).some(([name, value]) => name !== "authorization" && /^Bearer\s/i.test(value));
|
|
956
|
+
const headerName = getCloudAuthHeaderName().toLowerCase();
|
|
957
|
+
if (headerName === "authorization") return false;
|
|
958
|
+
for (const [name, value] of effectiveHeaders) if (name === headerName) return /^Bearer\s/i.test(value) && (isPromptfooCloudApiHost(url) || value.replace(/^Bearer\s+/i, "") === cloudConfig.getApiKey());
|
|
959
|
+
return Boolean(getCloudBearerToken(url));
|
|
960
|
+
}
|
|
961
|
+
/** Capture the Cloud redirect policy before any asynchronous work or retry. */
|
|
962
|
+
function preserveCloudAuthRedirects(url, options = {}) {
|
|
963
|
+
if (options.restrictCloudAuthRedirects || !usesCustomCloudAuth(url, getEffectiveHeaders(url, options.headers), options.skipCloudAuthInjection)) return options;
|
|
964
|
+
return {
|
|
965
|
+
...options,
|
|
966
|
+
restrictCloudAuthRedirects: true
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Enhanced fetch wrapper that adds logging, authentication, error handling, and optional compression
|
|
971
|
+
*/
|
|
972
|
+
async function monkeyPatchFetch(url, options) {
|
|
973
|
+
const NO_LOG_URLS = [
|
|
974
|
+
R_ENDPOINT,
|
|
975
|
+
CONSENT_ENDPOINT,
|
|
976
|
+
EVENTS_ENDPOINT
|
|
977
|
+
];
|
|
978
|
+
const urlString = getRequestUrlString(url);
|
|
979
|
+
const callerHeaders = getEffectiveHeaders(url, options?.headers);
|
|
980
|
+
const isSilent = new Headers(callerHeaders).get("x-promptfoo-silent") === "true";
|
|
981
|
+
const logEnabled = !NO_LOG_URLS.some((logUrl) => matchesNoLogUrl(urlString, logUrl)) && !isSilent;
|
|
982
|
+
const { restrictCloudAuthRedirects: restrictRedirects, ...opts } = preserveCloudAuthRedirects(url, options);
|
|
983
|
+
const originalBody = opts.body;
|
|
984
|
+
if (options?.compress && opts.body && typeof opts.body === "string") try {
|
|
985
|
+
opts.body = await gzipAsync(opts.body);
|
|
986
|
+
opts.headers = setHeader(getEffectiveHeaders(url, opts.headers), "Content-Encoding", "gzip");
|
|
987
|
+
} catch (e) {
|
|
988
|
+
logger.warn(`Failed to compress request body: ${e}`);
|
|
989
|
+
}
|
|
990
|
+
if (!options?.skipCloudAuthInjection) {
|
|
991
|
+
const cloudAuth = getCloudBearerToken(url);
|
|
992
|
+
if (cloudAuth) {
|
|
993
|
+
const cloudAuthHeaderName = getCloudAuthHeaderName();
|
|
994
|
+
const effectiveHeaders = getEffectiveHeaders(url, opts.headers);
|
|
995
|
+
if (!hasHeader(effectiveHeaders, cloudAuthHeaderName)) opts.headers = setHeader(effectiveHeaders, cloudAuthHeaderName, cloudAuth);
|
|
996
|
+
}
|
|
997
|
+
const cloudTaskTeamId = getCloudTaskTeamId(url);
|
|
998
|
+
const headersWithAuth = getEffectiveHeaders(url, opts.headers);
|
|
999
|
+
if (cloudTaskTeamId && !hasHeader(headersWithAuth, "x-promptfoo-team-id")) opts.headers = setHeader(headersWithAuth, PROMPTFOO_TEAM_ID_HEADER, cloudTaskTeamId);
|
|
1000
|
+
}
|
|
1001
|
+
if (restrictRedirects || usesCustomCloudAuth(url, getEffectiveHeaders(url, opts.headers), options?.skipCloudAuthInjection)) opts.dispatcher = restrictCloudAuthRedirects(urlString, opts.dispatcher);
|
|
1002
|
+
try {
|
|
1003
|
+
const response = await fetch(url, opts);
|
|
1004
|
+
if (logEnabled) logRequestResponse({
|
|
1005
|
+
url: urlString,
|
|
1006
|
+
requestBody: originalBody,
|
|
1007
|
+
requestMethod: opts.method || "GET",
|
|
1008
|
+
response
|
|
1009
|
+
});
|
|
1010
|
+
return response;
|
|
1011
|
+
} catch (e) {
|
|
1012
|
+
if (logEnabled) {
|
|
1013
|
+
logRequestResponse({
|
|
1014
|
+
url: urlString,
|
|
1015
|
+
requestBody: opts.body,
|
|
1016
|
+
requestMethod: opts.method || "GET",
|
|
1017
|
+
response: null
|
|
1018
|
+
});
|
|
1019
|
+
if (isConnectionError(e)) {
|
|
1020
|
+
logger.debug(`Connection error, please check your network connectivity to the host: ${getSafeUrlForConnectionLog(url)} ${getSafeProxyForConnectionLog()}`);
|
|
1021
|
+
throw e;
|
|
1022
|
+
}
|
|
1023
|
+
const safeUrl = sanitizeUrlForLogging(urlString);
|
|
1024
|
+
const safeError = `${JSON.stringify(e, Object.getOwnPropertyNames(e), 2)} ${e instanceof Error ? e.stack : ""}`.split(urlString).join(safeUrl);
|
|
1025
|
+
logger.debug(`Error in fetch: ${safeError}`);
|
|
1026
|
+
}
|
|
1027
|
+
throw unwrapCloudAuthRedirectError(e);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
//#endregion
|
|
1031
|
+
//#region src/util/fetch/retryContext.ts
|
|
1032
|
+
const fetchRetryContext = new AsyncLocalStorage();
|
|
1033
|
+
/**
|
|
1034
|
+
* Run `fn` with a fetch retry context so nested `fetchWithRetries` /
|
|
1035
|
+
* `fetchWithProxy` calls inherit the provider's configured `maxRetries`.
|
|
1036
|
+
*
|
|
1037
|
+
* When `maxRetries` is `undefined`, the new scope deliberately shadows any
|
|
1038
|
+
* outer provider context so providers without an override fall back to defaults.
|
|
1039
|
+
*/
|
|
1040
|
+
function withFetchRetryContext(maxRetries, fn) {
|
|
1041
|
+
return fetchRetryContext.run({ maxRetries }, fn);
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Read the active context's `maxRetries`, or `undefined` when none is set.
|
|
1045
|
+
*/
|
|
1046
|
+
function getFetchRetryContextMaxRetries() {
|
|
1047
|
+
return fetchRetryContext.getStore()?.maxRetries;
|
|
1048
|
+
}
|
|
1049
|
+
//#endregion
|
|
1050
|
+
//#region src/util/fetch/index.ts
|
|
1051
|
+
var fetch_exports = /* @__PURE__ */ __exportAll({
|
|
1052
|
+
computeRateLimitWaitMs: () => computeRateLimitWaitMs,
|
|
1053
|
+
fetchWithProxy: () => fetchWithProxy,
|
|
1054
|
+
fetchWithRetries: () => fetchWithRetries,
|
|
1055
|
+
fetchWithTimeout: () => fetchWithTimeout,
|
|
1056
|
+
getFetchWithProxyHeaders: () => getFetchWithProxyHeaders,
|
|
1057
|
+
handleRateLimit: () => handleRateLimit,
|
|
1058
|
+
isRateLimited: () => isRateLimited,
|
|
1059
|
+
isTransientError: () => isTransientError,
|
|
1060
|
+
rateLimitTimingFromHeaders: () => rateLimitTimingFromHeaders,
|
|
1061
|
+
readBoundedText: () => readBoundedText
|
|
1062
|
+
});
|
|
1063
|
+
var RequestAuthenticationError = class extends Error {};
|
|
1064
|
+
async function resolveAuthenticationHeaders(getAuthHeaders, explicitHeaders, signal) {
|
|
1065
|
+
signal?.throwIfAborted();
|
|
1066
|
+
let headers;
|
|
1067
|
+
try {
|
|
1068
|
+
headers = new Headers(await getAuthHeaders(signal ?? void 0));
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
signal?.throwIfAborted();
|
|
1071
|
+
throw Object.assign(new RequestAuthenticationError(error instanceof Error ? error.message : "Request authentication failed"), { cause: error });
|
|
1072
|
+
}
|
|
1073
|
+
signal?.throwIfAborted();
|
|
1074
|
+
new Headers(explicitHeaders).forEach((value, name) => headers.set(name, value));
|
|
1075
|
+
return Object.fromEntries(headers);
|
|
1076
|
+
}
|
|
1077
|
+
const cachedAgents = /* @__PURE__ */ new Map();
|
|
1078
|
+
const cachedProxyAgents = /* @__PURE__ */ new Map();
|
|
1079
|
+
/**
|
|
1080
|
+
* Get the connection pool size for HTTP agents.
|
|
1081
|
+
* Priority: PROMPTFOO_FETCH_CONNECTIONS env var > CLI -j flag > DEFAULT_MAX_CONCURRENCY (4).
|
|
1082
|
+
* Set PROMPTFOO_FETCH_CONNECTIONS to override independently of eval concurrency
|
|
1083
|
+
* (e.g., server deployments that need more connections than the default 4).
|
|
1084
|
+
*/
|
|
1085
|
+
function getConnectionPoolSize() {
|
|
1086
|
+
const envConnections = getEnvString("PROMPTFOO_FETCH_CONNECTIONS");
|
|
1087
|
+
if (envConnections != null) {
|
|
1088
|
+
const normalized = envConnections.trim();
|
|
1089
|
+
const parsed = Number(normalized);
|
|
1090
|
+
if (/^\d+$/.test(normalized) && Number.isSafeInteger(parsed) && parsed > 0) return parsed;
|
|
1091
|
+
}
|
|
1092
|
+
return state.maxConcurrency || 4;
|
|
1093
|
+
}
|
|
1094
|
+
function getOrCreateAgent(tlsOptions) {
|
|
1095
|
+
const concurrency = getConnectionPoolSize();
|
|
1096
|
+
const existing = cachedAgents.get(concurrency);
|
|
1097
|
+
if (existing) return existing;
|
|
1098
|
+
const agent = new Agent({
|
|
1099
|
+
headersTimeout: getRequestTimeoutMs(),
|
|
1100
|
+
keepAliveTimeout: 3e4,
|
|
1101
|
+
keepAliveMaxTimeout: 6e4,
|
|
1102
|
+
connections: concurrency,
|
|
1103
|
+
connect: tlsOptions
|
|
1104
|
+
}).compose(createDecompressionInterceptor()).compose(stripDecompressionHeaders());
|
|
1105
|
+
cachedAgents.set(concurrency, agent);
|
|
1106
|
+
return agent;
|
|
1107
|
+
}
|
|
1108
|
+
function getProxyAgentCacheKey(proxyUrl, concurrency) {
|
|
1109
|
+
return `${proxyUrl}::${concurrency}`;
|
|
1110
|
+
}
|
|
1111
|
+
function getOrCreateProxyAgent(proxyUrl, tlsOptions) {
|
|
1112
|
+
const concurrency = getConnectionPoolSize();
|
|
1113
|
+
const cacheKey = getProxyAgentCacheKey(proxyUrl, concurrency);
|
|
1114
|
+
const existing = cachedProxyAgents.get(cacheKey);
|
|
1115
|
+
if (existing) return existing;
|
|
1116
|
+
const agent = new ProxyAgent({
|
|
1117
|
+
uri: proxyUrl,
|
|
1118
|
+
proxyTls: tlsOptions,
|
|
1119
|
+
requestTls: tlsOptions,
|
|
1120
|
+
headersTimeout: getRequestTimeoutMs(),
|
|
1121
|
+
keepAliveTimeout: 3e4,
|
|
1122
|
+
keepAliveMaxTimeout: 6e4,
|
|
1123
|
+
connections: concurrency
|
|
1124
|
+
}).compose(createDecompressionInterceptor()).compose(stripDecompressionHeaders());
|
|
1125
|
+
cachedProxyAgents.set(cacheKey, agent);
|
|
1126
|
+
return agent;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Resolve whether to disable transient-error retries. An explicit caller flag
|
|
1130
|
+
* always wins (a caller may opt back in even when the provider set
|
|
1131
|
+
* `maxRetries: 0`); otherwise we disable only when the retry context carries
|
|
1132
|
+
* `maxRetries === 0`.
|
|
1133
|
+
*/
|
|
1134
|
+
function resolveTransientRetryDisabled(explicit) {
|
|
1135
|
+
if (explicit !== void 0) return explicit;
|
|
1136
|
+
return getFetchRetryContextMaxRetries() === 0;
|
|
1137
|
+
}
|
|
1138
|
+
function headersInitToRecord(headers) {
|
|
1139
|
+
if (!headers) return {};
|
|
1140
|
+
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
|
|
1141
|
+
if (Array.isArray(headers)) return Object.fromEntries(new Headers(headers).entries());
|
|
1142
|
+
return { ...headers };
|
|
1143
|
+
}
|
|
1144
|
+
function getFetchWithProxyHeaders(url, options) {
|
|
1145
|
+
const requestHeaders = url instanceof Request && options.headers === void 0 ? headersInitToRecord(url.headers) : {};
|
|
1146
|
+
const optionHeaders = headersInitToRecord(options.headers);
|
|
1147
|
+
return {
|
|
1148
|
+
...requestHeaders,
|
|
1149
|
+
...optionHeaders,
|
|
1150
|
+
"x-promptfoo-version": VERSION
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
function getFetchUrlString(url) {
|
|
1154
|
+
if (typeof url === "string") return url;
|
|
1155
|
+
if (url instanceof URL) return url.toString();
|
|
1156
|
+
if (url instanceof Request) return url.url;
|
|
1157
|
+
}
|
|
1158
|
+
async function fetchWithProxy(url, options = {}, abortSignal) {
|
|
1159
|
+
options = preserveCloudAuthRedirects(url, options);
|
|
1160
|
+
let finalUrl = url;
|
|
1161
|
+
let finalUrlString = getFetchUrlString(url);
|
|
1162
|
+
if (!finalUrlString) throw new Error("Invalid URL");
|
|
1163
|
+
const combinedSignal = abortSignal ? options.signal ? AbortSignal.any([options.signal, abortSignal]) : abortSignal : options.signal;
|
|
1164
|
+
const { getAuthHeaders, ...requestOptions } = options;
|
|
1165
|
+
const finalOptions = {
|
|
1166
|
+
...requestOptions,
|
|
1167
|
+
headers: getFetchWithProxyHeaders(url, options),
|
|
1168
|
+
signal: combinedSignal
|
|
1169
|
+
};
|
|
1170
|
+
const logEnabled = new Headers(finalOptions.headers).get("x-promptfoo-silent") !== "true";
|
|
1171
|
+
if (typeof url === "string") try {
|
|
1172
|
+
const parsedUrl = new URL(url);
|
|
1173
|
+
if (parsedUrl.username || parsedUrl.password) {
|
|
1174
|
+
const headers = finalOptions.headers ?? {};
|
|
1175
|
+
if (Object.keys(headers).some((name) => name.toLowerCase() === "authorization")) {
|
|
1176
|
+
if (logEnabled) logger.warn("Both URL credentials and Authorization header present - URL credentials will be ignored");
|
|
1177
|
+
} else {
|
|
1178
|
+
const credentials = [parsedUrl.username, parsedUrl.password].map((part) => {
|
|
1179
|
+
try {
|
|
1180
|
+
return decodeURIComponent(part);
|
|
1181
|
+
} catch {
|
|
1182
|
+
return part;
|
|
1183
|
+
}
|
|
1184
|
+
}).join(":");
|
|
1185
|
+
finalOptions.headers = {
|
|
1186
|
+
...headers,
|
|
1187
|
+
Authorization: `Basic ${Buffer.from(credentials).toString("base64")}`
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
parsedUrl.username = "";
|
|
1191
|
+
parsedUrl.password = "";
|
|
1192
|
+
finalUrl = parsedUrl.toString();
|
|
1193
|
+
finalUrlString = finalUrl;
|
|
1194
|
+
}
|
|
1195
|
+
} catch (e) {
|
|
1196
|
+
if (logEnabled) logger.debug(`URL parsing failed in fetchWithProxy: ${e}`);
|
|
1197
|
+
}
|
|
1198
|
+
const tlsOptions = { rejectUnauthorized: !getEnvBool("PROMPTFOO_INSECURE_SSL", true) };
|
|
1199
|
+
const caCertPath = getEnvString("PROMPTFOO_CA_CERT_PATH");
|
|
1200
|
+
if (caCertPath) try {
|
|
1201
|
+
const resolvedPath = path.resolve(state.basePath || "", caCertPath);
|
|
1202
|
+
tlsOptions.ca = await fsPromises$1.readFile(resolvedPath, "utf8");
|
|
1203
|
+
if (logEnabled) logger.debug(`Using custom CA certificate from ${resolvedPath}`);
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
if (logEnabled) logger.warn(`Failed to read CA certificate from ${caCertPath}: ${e}`);
|
|
1206
|
+
}
|
|
1207
|
+
const proxyUrl = finalUrlString ? getProxyForUrl(finalUrlString) : "";
|
|
1208
|
+
if (!finalOptions.dispatcher) {
|
|
1209
|
+
if (proxyUrl) {
|
|
1210
|
+
if (logEnabled) logger.debug(`Using proxy: ${sanitizeUrl(proxyUrl)}`);
|
|
1211
|
+
finalOptions.dispatcher = getOrCreateProxyAgent(proxyUrl, tlsOptions);
|
|
1212
|
+
} else finalOptions.dispatcher = getOrCreateAgent(tlsOptions);
|
|
1213
|
+
}
|
|
1214
|
+
const disableTransientRetries = resolveTransientRetryDisabled(options.disableTransientRetries);
|
|
1215
|
+
const maxTransientRetries = disableTransientRetries ? 0 : 3;
|
|
1216
|
+
for (let attempt = 0; attempt <= maxTransientRetries; attempt++) {
|
|
1217
|
+
let attemptOptions = finalOptions;
|
|
1218
|
+
if (getAuthHeaders) attemptOptions = {
|
|
1219
|
+
...finalOptions,
|
|
1220
|
+
headers: await resolveAuthenticationHeaders(getAuthHeaders, finalOptions.headers, combinedSignal)
|
|
1221
|
+
};
|
|
1222
|
+
const response = await monkeyPatchFetch(finalUrl, attemptOptions);
|
|
1223
|
+
if (!disableTransientRetries && isTransientError(response) && attempt < maxTransientRetries) {
|
|
1224
|
+
const backoffMs = Math.pow(2, attempt) * 1e3;
|
|
1225
|
+
if (logEnabled) logger.debug(`Transient error (${response.status} ${response.statusText}), retry ${attempt + 1}/${maxTransientRetries} after ${backoffMs}ms`);
|
|
1226
|
+
await sleep(backoffMs);
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
return response;
|
|
1230
|
+
}
|
|
1231
|
+
throw new Error("Unexpected end of transient retry loop");
|
|
1232
|
+
}
|
|
1233
|
+
function fetchWithTimeout(url, options = {}, timeout) {
|
|
1234
|
+
return new Promise((resolve, reject) => {
|
|
1235
|
+
const timeoutController = new AbortController();
|
|
1236
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeoutController.signal]) : timeoutController.signal;
|
|
1237
|
+
const timeoutId = setTimeout(() => {
|
|
1238
|
+
timeoutController.abort();
|
|
1239
|
+
reject(/* @__PURE__ */ new Error(`Request timed out after ${timeout} ms`));
|
|
1240
|
+
}, timeout);
|
|
1241
|
+
fetchWithProxy(url, {
|
|
1242
|
+
...options,
|
|
1243
|
+
signal
|
|
1244
|
+
}).then((response) => {
|
|
1245
|
+
clearTimeout(timeoutId);
|
|
1246
|
+
resolve(response);
|
|
1247
|
+
}).catch((error) => {
|
|
1248
|
+
clearTimeout(timeoutId);
|
|
1249
|
+
reject(error);
|
|
1250
|
+
});
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Check if a response indicates rate limiting
|
|
1255
|
+
*/
|
|
1256
|
+
function isRateLimited(response) {
|
|
1257
|
+
invariant(response.headers, "Response headers are missing");
|
|
1258
|
+
invariant(response.status, "Response status is missing");
|
|
1259
|
+
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";
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Compute how long to wait after a rate-limited response.
|
|
1263
|
+
* Reads `retry-after-ms`, `Retry-After`, `X-RateLimit-Reset`, and OpenAI-style reset headers.
|
|
1264
|
+
* Default: 60s.
|
|
1265
|
+
*/
|
|
1266
|
+
function computeRateLimitWaitMs(response) {
|
|
1267
|
+
const parsedHeaders = parseRateLimitHeaders(Object.fromEntries(response.headers.entries()));
|
|
1268
|
+
const rateLimitReset = response.headers.get("X-RateLimit-Reset");
|
|
1269
|
+
if (response.headers.get("x-ratelimit-reset-requests") || response.headers.get("x-ratelimit-reset-tokens")) {
|
|
1270
|
+
if (parsedHeaders.resetAt !== void 0) return Math.max(parsedHeaders.resetAt - Date.now(), 0);
|
|
1271
|
+
}
|
|
1272
|
+
if (rateLimitReset) {
|
|
1273
|
+
const resetAt = Number.parseInt(rateLimitReset, 10) * 1e3;
|
|
1274
|
+
if (Number.isFinite(resetAt) && resetAt >= 0) return Math.max(resetAt - Date.now() + 1e3, 0);
|
|
1275
|
+
}
|
|
1276
|
+
return parsedHeaders.retryAfterMs ?? 6e4;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Maximum jitter (in ms) added to rate-limit waits. Spreads concurrent
|
|
1280
|
+
* waiters so they don't all retry in the same instant after Retry-After.
|
|
1281
|
+
*/
|
|
1282
|
+
const RATE_LIMIT_JITTER_MS = 1e3;
|
|
1283
|
+
async function sleepWithAbort(waitTime, signal) {
|
|
1284
|
+
if (!signal) {
|
|
1285
|
+
await sleep(waitTime);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (signal.aborted) throw getAbortError(signal);
|
|
1289
|
+
await new Promise((resolve, reject) => {
|
|
1290
|
+
const timeout = setTimeout(() => {
|
|
1291
|
+
signal.removeEventListener("abort", onAbort);
|
|
1292
|
+
resolve();
|
|
1293
|
+
}, waitTime);
|
|
1294
|
+
const onAbort = () => {
|
|
1295
|
+
clearTimeout(timeout);
|
|
1296
|
+
signal.removeEventListener("abort", onAbort);
|
|
1297
|
+
reject(getAbortError(signal));
|
|
1298
|
+
};
|
|
1299
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Handle rate limiting by waiting the appropriate amount of time, plus a
|
|
1304
|
+
* uniform random jitter to avoid synchronized retry storms when many
|
|
1305
|
+
* concurrent requests hit the same rate limit.
|
|
1306
|
+
*/
|
|
1307
|
+
async function handleRateLimit(response, signal, onRateLimitBackoff) {
|
|
1308
|
+
const waitTime = computeRateLimitWaitMs(response);
|
|
1309
|
+
if (!signal?.aborted) onRateLimitBackoff?.(Object.freeze({
|
|
1310
|
+
headers: Object.freeze(Object.fromEntries(response.headers.entries())),
|
|
1311
|
+
status: response.status,
|
|
1312
|
+
resetAt: Date.now() + waitTime
|
|
1313
|
+
}));
|
|
1314
|
+
const jitter = Math.floor(Math.random() * RATE_LIMIT_JITTER_MS);
|
|
1315
|
+
const totalWait = waitTime + jitter;
|
|
1316
|
+
logger.debug(`Rate limited, waiting ${totalWait}ms (base ${waitTime}ms + ${jitter}ms jitter) before retry`);
|
|
1317
|
+
await sleepWithAbort(totalWait, signal);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Hard cap on bytes read from a 429 response body during classification.
|
|
1321
|
+
* A hostile or buggy upstream could otherwise stream a multi-MB body on
|
|
1322
|
+
* every 429, amplifying memory pressure under concurrent eval load. 64 KB
|
|
1323
|
+
* is well above any well-formed provider error envelope (typical OpenAI
|
|
1324
|
+
* / Anthropic JSON errors are <1 KB).
|
|
1325
|
+
*/
|
|
1326
|
+
const RATE_LIMIT_BODY_PEEK_BYTES = 65536;
|
|
1327
|
+
/**
|
|
1328
|
+
* Read the body of a rate-limited response without consuming the original
|
|
1329
|
+
* stream. Returns the parsed body (JSON if parseable, else raw text) and
|
|
1330
|
+
* any extracted error code (e.g. `insufficient_quota`).
|
|
1331
|
+
*
|
|
1332
|
+
* Why clone: `Response.text()` consumes the underlying stream. The original
|
|
1333
|
+
* response may still be observed by upstream wrappers (logging middleware,
|
|
1334
|
+
* monkey-patched fetch); cloning preserves their ability to read the body.
|
|
1335
|
+
*
|
|
1336
|
+
* Clone/read failures degrade to `{ body: undefined, code: undefined }`
|
|
1337
|
+
* unless the caller cancelled the unfinished peek. Losing the body code only
|
|
1338
|
+
* widens classification from `quota` to `rate_limit`, which is the safer
|
|
1339
|
+
* (retryable) side of the misclassification.
|
|
1340
|
+
*/
|
|
1341
|
+
async function peekRateLimitBody(response, logEnabled, signal) {
|
|
1342
|
+
let cloned;
|
|
1343
|
+
try {
|
|
1344
|
+
cloned = response.clone();
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
if (signal?.aborted) throw getAbortError(signal);
|
|
1347
|
+
if (logEnabled) logger.debug(`[fetch] peekRateLimitBody: clone failed, skipping body code lookup: ${err}`);
|
|
1348
|
+
return {
|
|
1349
|
+
body: void 0,
|
|
1350
|
+
code: void 0,
|
|
1351
|
+
type: void 0
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
let text;
|
|
1355
|
+
try {
|
|
1356
|
+
text = await readBoundedText(cloned, RATE_LIMIT_BODY_PEEK_BYTES, { requireStream: true });
|
|
1357
|
+
} catch (err) {
|
|
1358
|
+
if (signal?.aborted) throw getAbortError(signal);
|
|
1359
|
+
if (logEnabled) logger.debug(`[fetch] peekRateLimitBody: body read failed: ${err}`);
|
|
1360
|
+
return {
|
|
1361
|
+
body: void 0,
|
|
1362
|
+
code: void 0,
|
|
1363
|
+
type: void 0
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
if (!text) return {
|
|
1367
|
+
body: void 0,
|
|
1368
|
+
code: void 0,
|
|
1369
|
+
type: void 0
|
|
1370
|
+
};
|
|
1371
|
+
try {
|
|
1372
|
+
const json = JSON.parse(text);
|
|
1373
|
+
return {
|
|
1374
|
+
body: json,
|
|
1375
|
+
code: extractRateLimitErrorCode(json),
|
|
1376
|
+
type: extractRateLimitErrorType(json)
|
|
1377
|
+
};
|
|
1378
|
+
} catch {
|
|
1379
|
+
if (logEnabled) logger.debug("[fetch] peekRateLimitBody: response body was not JSON");
|
|
1380
|
+
return {
|
|
1381
|
+
body: text,
|
|
1382
|
+
code: void 0,
|
|
1383
|
+
type: void 0
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Drain a Response's body into a string, but stop reading once `maxBytes`
|
|
1389
|
+
* have been collected. Each streamed chunk is bounded to the remaining
|
|
1390
|
+
* budget *before* it enters the in-memory buffer, so a single oversized
|
|
1391
|
+
* chunk cannot exceed `maxBytes` of retained memory. `requireStream` skips
|
|
1392
|
+
* streamless polyfills to guarantee bounded buffering for rate-limit peeking.
|
|
1393
|
+
* Other callers retain the legacy text fallback, which buffers before truncation.
|
|
1394
|
+
*/
|
|
1395
|
+
async function readBoundedText(response, maxBytes, options = {}) {
|
|
1396
|
+
if (!response.body) {
|
|
1397
|
+
const contentLength = Number.parseInt(response.headers?.get?.("content-length") ?? "", 10);
|
|
1398
|
+
if (options.requireStream || Number.isFinite(contentLength) && contentLength > maxBytes) return "";
|
|
1399
|
+
const text = await response.text();
|
|
1400
|
+
return text.length > maxBytes ? text.slice(0, maxBytes) : text;
|
|
1401
|
+
}
|
|
1402
|
+
const reader = response.body.getReader();
|
|
1403
|
+
const chunks = [];
|
|
1404
|
+
let total = 0;
|
|
1405
|
+
try {
|
|
1406
|
+
while (total < maxBytes) {
|
|
1407
|
+
const { done, value } = await reader.read();
|
|
1408
|
+
if (done) break;
|
|
1409
|
+
const room = maxBytes - total;
|
|
1410
|
+
const bounded = value.byteLength <= room ? value : value.slice(0, room);
|
|
1411
|
+
chunks.push(bounded);
|
|
1412
|
+
total += bounded.byteLength;
|
|
1413
|
+
}
|
|
1414
|
+
} finally {
|
|
1415
|
+
try {
|
|
1416
|
+
await reader.cancel();
|
|
1417
|
+
} catch {}
|
|
1418
|
+
}
|
|
1419
|
+
const merged = new Uint8Array(total);
|
|
1420
|
+
let offset = 0;
|
|
1421
|
+
for (const chunk of chunks) {
|
|
1422
|
+
merged.set(chunk, offset);
|
|
1423
|
+
offset += chunk.byteLength;
|
|
1424
|
+
}
|
|
1425
|
+
return new TextDecoder().decode(merged);
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Retry-After / reset timing from a rate-limit response's headers, for callers
|
|
1429
|
+
* that build an {@link HttpRateLimitError} from an SDK error rather than a
|
|
1430
|
+
* `Response`. Keeps the header parsing in one place.
|
|
1431
|
+
*/
|
|
1432
|
+
function rateLimitTimingFromHeaders(headers) {
|
|
1433
|
+
const parsed = parseRateLimitHeaders(headers);
|
|
1434
|
+
return {
|
|
1435
|
+
retryAfterMs: parsed.retryAfterMs,
|
|
1436
|
+
resetAt: parsed.resetAt
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function buildHttpRateLimitError(response, body, code, type) {
|
|
1440
|
+
const headers = Object.fromEntries(response.headers.entries());
|
|
1441
|
+
const parsed = parseRateLimitHeaders(headers);
|
|
1442
|
+
return new HttpRateLimitError({
|
|
1443
|
+
status: response.status,
|
|
1444
|
+
statusText: response.statusText,
|
|
1445
|
+
retryAfterMs: parsed.retryAfterMs,
|
|
1446
|
+
resetAt: parsed.resetAt,
|
|
1447
|
+
code,
|
|
1448
|
+
type,
|
|
1449
|
+
headers,
|
|
1450
|
+
body
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Check if a response indicates a transient server error that should be retried.
|
|
1455
|
+
* Matches specific status codes with their expected status text to avoid
|
|
1456
|
+
* retrying permanent failures (e.g., some APIs return 502 for auth errors).
|
|
1457
|
+
*/
|
|
1458
|
+
function isTransientError(response) {
|
|
1459
|
+
if (!response?.statusText) return false;
|
|
1460
|
+
const statusText = response.statusText.toLowerCase();
|
|
1461
|
+
switch (response.status) {
|
|
1462
|
+
case 502: return statusText.includes("bad gateway");
|
|
1463
|
+
case 503: return statusText.includes("service unavailable");
|
|
1464
|
+
case 504: return statusText.includes("gateway timeout");
|
|
1465
|
+
case 524: return statusText.includes("timeout");
|
|
1466
|
+
default: return false;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Decide what to do with a rate-limited response inside `fetchWithRetries`.
|
|
1471
|
+
*
|
|
1472
|
+
* Throws on hard-quota fail-fast or retry exhaustion (with a structured
|
|
1473
|
+
* {@link HttpRateLimitError} for status 429, or a plain `Error` for the soft
|
|
1474
|
+
* `X-RateLimit-Remaining=0` 200 case). Otherwise sleeps via
|
|
1475
|
+
* {@link handleRateLimit} and returns so the caller can `continue` the loop.
|
|
1476
|
+
*/
|
|
1477
|
+
/**
|
|
1478
|
+
* Returns a string form of a {@link RequestInfo} suitable for log output.
|
|
1479
|
+
* Strips basic-auth credentials and known sensitive query parameters (api_key,
|
|
1480
|
+
* token, password, signature, …) via {@link sanitizeUrl} so providers that
|
|
1481
|
+
* embed credentials in the URL (e.g. n8n webhooks, HTTP providers with
|
|
1482
|
+
* `?api_key=…`) do not leak them into retry diagnostics.
|
|
1483
|
+
*/
|
|
1484
|
+
function urlForLog(url) {
|
|
1485
|
+
const raw = typeof url === "string" ? url : url.url;
|
|
1486
|
+
return sanitizeUrlForLogging(raw);
|
|
1487
|
+
}
|
|
1488
|
+
async function handleRateLimitedResponse(response, url, attempt, maxRetries, logEnabled, signal, onRateLimitBackoff) {
|
|
1489
|
+
const isHardRateLimit = response.status === 429;
|
|
1490
|
+
const safeUrl = urlForLog(url);
|
|
1491
|
+
let rateLimitError;
|
|
1492
|
+
if (isHardRateLimit) {
|
|
1493
|
+
const { body, code, type } = await peekRateLimitBody(response, logEnabled, signal);
|
|
1494
|
+
rateLimitError = buildHttpRateLimitError(response, body, code, type);
|
|
1495
|
+
}
|
|
1496
|
+
if (rateLimitError?.kind === "quota") {
|
|
1497
|
+
if (logEnabled) logger.debug(`Quota exhausted on URL ${safeUrl}: HTTP ${response.status} (code: ${rateLimitError.code}), failing fast.`);
|
|
1498
|
+
throw rateLimitError;
|
|
1499
|
+
}
|
|
1500
|
+
if (attempt >= maxRetries) {
|
|
1501
|
+
if (rateLimitError) {
|
|
1502
|
+
if (logEnabled) logger.debug(`Rate limited on URL ${safeUrl}: HTTP ${response.status} ${response.statusText}, attempt ${attempt + 1}/${maxRetries + 1}, no retries remain.`);
|
|
1503
|
+
throw rateLimitError;
|
|
1504
|
+
}
|
|
1505
|
+
throw new Error(`Rate limited: ${response.status} ${response.statusText} after ${maxRetries + 1} attempts`);
|
|
1506
|
+
}
|
|
1507
|
+
if (logEnabled) logger.debug(`Rate limited on URL ${safeUrl}: HTTP ${response.status} ${response.statusText}, attempt ${attempt + 1}/${maxRetries + 1}, waiting before retry...`);
|
|
1508
|
+
await handleRateLimit(response, signal, onRateLimitBackoff);
|
|
1509
|
+
}
|
|
1510
|
+
function formatFetchErrorMessage(error, url) {
|
|
1511
|
+
if (!(error instanceof Error)) return String(error);
|
|
1512
|
+
const typedError = error;
|
|
1513
|
+
const rawUrl = typeof url === "string" ? url : url.url;
|
|
1514
|
+
const redactUrl = (value) => value.split(rawUrl).join(urlForLog(url));
|
|
1515
|
+
let message = `${typedError.name}: ${redactUrl(typedError.message)}`;
|
|
1516
|
+
if (typedError.cause) message += ` (Cause: ${redactUrl(String(typedError.cause))})`;
|
|
1517
|
+
if (typedError.code) message += ` (Code: ${typedError.code})`;
|
|
1518
|
+
return message;
|
|
1519
|
+
}
|
|
1520
|
+
async function fetchWithRetries(url, options = {}, timeout, maxRetries, onRateLimitBackoff) {
|
|
1521
|
+
options = preserveCloudAuthRedirects(url, options);
|
|
1522
|
+
const contextMaxRetries = getFetchRetryContextMaxRetries();
|
|
1523
|
+
maxRetries = Math.max(0, maxRetries ?? contextMaxRetries ?? 4);
|
|
1524
|
+
let lastErrorMessage;
|
|
1525
|
+
const backoff = getEnvInt("PROMPTFOO_REQUEST_BACKOFF_MS", 5e3);
|
|
1526
|
+
const signal = getEffectiveRequestSignal(url, options);
|
|
1527
|
+
const logEnabled = new Headers(getFetchWithProxyHeaders(url, options)).get("x-promptfoo-silent") !== "true";
|
|
1528
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
1529
|
+
let response;
|
|
1530
|
+
try {
|
|
1531
|
+
response = await fetchWithTimeout(url, {
|
|
1532
|
+
...options,
|
|
1533
|
+
disableTransientRetries: true
|
|
1534
|
+
}, timeout);
|
|
1535
|
+
if (getEnvBool("PROMPTFOO_RETRY_5XX") && response.status >= 500 && response.status < 600) throw new Error(`Internal Server Error: ${response.status} ${response.statusText}`);
|
|
1536
|
+
if (response && isRateLimited(response)) {
|
|
1537
|
+
await handleRateLimitedResponse(response, url, i, maxRetries, logEnabled, signal, onRateLimitBackoff);
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
return response;
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
1543
|
+
if (error instanceof CloudAuthRedirectError || error instanceof HttpRateLimitError || error instanceof RequestAuthenticationError) throw error;
|
|
1544
|
+
if (signal?.aborted) throw getAbortError(signal);
|
|
1545
|
+
const errorMessage = formatFetchErrorMessage(error, url);
|
|
1546
|
+
if (logEnabled) logger.debug(`Request to ${urlForLog(url)} failed (attempt #${i + 1}), retrying: ${errorMessage}`);
|
|
1547
|
+
if (i < maxRetries) await sleepWithAbort(Math.pow(2, i) * (backoff + 1e3 * Math.random()), signal);
|
|
1548
|
+
lastErrorMessage = errorMessage;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
throw new Error(`Request failed after ${maxRetries} retries: ${lastErrorMessage}`);
|
|
1552
|
+
}
|
|
1553
|
+
//#endregion
|
|
1554
|
+
export { isTransientConnectionError as A, CLOUD_PROVIDER_PREFIX as B, formatRateLimitErrorMessage as C, isHardQuotaCode as D, isDefinitiveBillingCode as E, CloudConfig as F, getDefaultShareViewBaseUrl as G, EVENTS_ENDPOINT as H, cloudConfig as I, POSTHOG_KEY as J, getShareApiBaseUrl as K, readGlobalConfig as L, stripDecompressionHeaders as M, parseRateLimitHeaders as N, isHttpRateLimitError as O, parseRetryAfter as P, writeGlobalConfig as R, extractRateLimitErrorType as S, isAbortError as T, R_ENDPOINT as U, CONSENT_ENDPOINT as V, TERMINAL_MAX_WIDTH as W, VERSION as Y, DEFINITIVE_BILLING_ERROR_CODES as _, getFetchWithProxyHeaders as a, NON_TRANSIENT_HTTP_STATUSES as b, getFetchRetryContextMaxRetries as c, getCloudAuthHeaderName as d, getCloudBearerToken as f, preserveCloudAuthRedirects as g, isPromptfooCloudApiHost as h, fetch_exports as i, createDecompressionInterceptor as j, isNonTransientHttpStatus as k, withFetchRetryContext as l, getRequestUrlString as m, fetchWithRetries as n, rateLimitTimingFromHeaders as o, getCloudTaskTeamId as p, getShareViewBaseUrl as q, fetchWithTimeout as r, readBoundedText as s, fetchWithProxy as t, PROMPTFOO_TEAM_ID_HEADER as u, HARD_QUOTA_ERROR_CODES as v, getAbortError as w, extractRateLimitErrorCode as x, HttpRateLimitError as y, writeGlobalConfigPartial as z };
|
|
1555
|
+
|
|
1556
|
+
//# sourceMappingURL=fetch-BG_Jl7ty.js.map
|