promptfoo 0.120.27 → 0.121.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/src/{ListApp-8WOe2nT6.js → ListApp-Du7YVwj5.js} +2 -4
- package/dist/src/accounts-B0pgC1oV.js +206 -0
- package/dist/src/{accounts-DVINui-2.js → accounts-Bm2D8Db9.js} +39 -34
- package/dist/src/{accounts-CPDRAMND.js → accounts-CiBLOnA7.js} +38 -33
- package/dist/src/{accounts-Fl2J3_Fu.cjs → accounts-gtkH-5KX.cjs} +77 -78
- package/dist/src/{agentic-utils-D922n6mm.js → agentic-utils-DS1g3GLF.js} +9 -10
- package/dist/src/{agents-BcsN_BgB.js → agents-9qiOy0ho.js} +16 -12
- package/dist/src/{agents-BXLmVsxR.js → agents-CBr9A01V.js} +37 -37
- package/dist/src/{agents-pMfppv9Z.js → agents-CmvBq8LV.js} +16 -18
- package/dist/src/{agents-hqgSV-3o.js → agents-D__IdAlg.js} +39 -40
- package/dist/src/{agents-BO2n8Z0d.cjs → agents-DbRtpYxR.cjs} +37 -40
- package/dist/src/{agents-BdUTAwi-.js → agents-DgF2zDag.js} +37 -42
- package/dist/src/{agents-DgJf2-ez.cjs → agents-Di9DKPzn.cjs} +16 -17
- package/dist/src/{agents-DNvSH78i.js → agents-cLXA8a_8.js} +17 -19
- package/dist/src/{aimlapi-DtgPI0nE.js → aimlapi-B4rcnZgv.js} +15 -17
- package/dist/src/{aimlapi-BE_Tg9Fl.cjs → aimlapi-BvlNH0gr.cjs} +15 -16
- package/dist/src/{aimlapi-DOib86oE.js → aimlapi-CnkC2HqE.js} +16 -18
- package/dist/src/{aimlapi-DTPACCB1.js → aimlapi-DHJU_kcV.js} +15 -4
- package/dist/src/app/assets/index-4LKxG2CG.js +439 -0
- package/dist/src/app/assets/{index-NCn4eVBv.css → index-C3zcsZFQ.css} +1 -1
- package/dist/src/app/assets/vendor-charts-BnDWwBlI.js +36 -0
- package/dist/src/app/index.html +3 -3
- package/dist/src/app/tsconfig.app.tsbuildinfo +1 -1
- package/dist/src/{audio-BnRUGAm_.js → audio-Bkv46et0.js} +6 -5
- package/dist/src/{audio-Cwo68yZS.cjs → audio-CGMyULza.cjs} +6 -7
- package/dist/src/{audio-MSRki4JU.js → audio-ClI_AFre.js} +6 -8
- package/dist/src/{audio-BRYU0BFo.js → audio-Dz3z7s3J.js} +7 -9
- package/dist/src/{base-pGVmXNl4.cjs → base-CGrhspbK.cjs} +36 -38
- package/dist/src/{base-h961VXYk.js → base-CpjcHe4e.js} +11 -13
- package/dist/src/base-DLKtKMFh.js +193 -0
- package/dist/src/{base-XB2tDJrB.js → base-Dy1V8--Z.js} +11 -13
- package/dist/src/blobs-BDbfYdrJ.js +236 -0
- package/dist/src/{blobs-CR5C4Ihh.js → blobs-CBO20krR.js} +9 -12
- package/dist/src/{blobs-BM_e6hCa.js → blobs-CMHN0Qcz.js} +9 -12
- package/dist/src/{blobs-B-KQAFhX.cjs → blobs-D23XLin-.cjs} +34 -37
- package/dist/src/{cache-jsiwsAJv.js → cache-BVeDlD87.js} +132 -117
- package/dist/src/{cache-CIpsoBZR.js → cache-C4Nxf52C.js} +132 -118
- package/dist/src/cache-CeUpFm3M.cjs +5 -0
- package/dist/src/{cache-BTVYfbka.cjs → cache-Dh5WtQps.cjs} +182 -168
- package/dist/src/cache-i1P6crbO.js +756 -0
- package/dist/src/cache-n-RCJ-hL.js +6 -0
- package/dist/src/{chat-BcPjZXIp.js → chat-BiKyneZl.js} +45 -46
- package/dist/src/{chat-D31K7C4u.cjs → chat-C1Qst7jL.cjs} +20 -21
- package/dist/src/{chat-B84t99NW.js → chat-C2jrdPMx.js} +20 -9
- package/dist/src/{chat-BE44YOc6.cjs → chat-CgF-J-Jj.cjs} +65 -66
- package/dist/src/{chat-DwWifjxi.js → chat-CzkrVDfz.js} +20 -22
- package/dist/src/chat-DJIw17u0.js +766 -0
- package/dist/src/{chat-CcUCysjU.js → chat-DqxYYtWA.js} +45 -46
- package/dist/src/{chat-DZM2GUHO.js → chat-qmatte1u.js} +21 -23
- package/dist/src/{chatkit-D67HS_0b.js → chatkit-65VXf5SR.js} +58 -58
- package/dist/src/{chatkit-DAB_qfzI.js → chatkit-Be-Q-a9F.js} +58 -60
- package/dist/src/{chatkit-Biqb_wsD.js → chatkit-BxFvW8KY.js} +58 -60
- package/dist/src/{chatkit-PGG4ZYIn.cjs → chatkit-DKyPi1Gs.cjs} +58 -60
- package/dist/src/chunk-DEq-mXcV.js +15 -0
- package/dist/src/chunk-DRamLcfz.js +16 -0
- package/dist/src/{claude-agent-sdk-SVM6AdBu.js → claude-agent-sdk-Apiy0iaz.js} +31 -31
- package/dist/src/{claude-agent-sdk-C-IOTPfo.js → claude-agent-sdk-D2bJee9S.js} +31 -29
- package/dist/src/{claude-agent-sdk-C9SiaQub.cjs → claude-agent-sdk-D9Z5Pr9X.cjs} +31 -28
- package/dist/src/{claude-agent-sdk-CiluSyW1.js → claude-agent-sdk-DfCoW0E6.js} +33 -20
- package/dist/src/cloud-BBh91EUK.js +4 -0
- package/dist/src/{cloud-CZ-q9Ier.js → cloud-C0dlstV_.js} +7 -9
- package/dist/src/{cloudflare-ai-BahKHyhh.js → cloudflare-ai-8TDxHR0x.js} +16 -18
- package/dist/src/{cloudflare-ai-v_qZD6_q.js → cloudflare-ai-BxAGvfju.js} +17 -19
- package/dist/src/{cloudflare-ai-Dfahv5SY.cjs → cloudflare-ai-CknbZ5LJ.cjs} +16 -17
- package/dist/src/{cloudflare-ai-Dxyt50Nl.js → cloudflare-ai-g7PB6VHR.js} +16 -4
- package/dist/src/{cloudflare-gateway-Bi_FpOFy.js → cloudflare-gateway-B9HWA5wf.js} +23 -23
- package/dist/src/{cloudflare-gateway-BPWoZIzJ.cjs → cloudflare-gateway-BSnDmHYo.cjs} +21 -22
- package/dist/src/{cloudflare-gateway-C0guUNwk.js → cloudflare-gateway-CKDb4dJ8.js} +26 -14
- package/dist/src/{cloudflare-gateway-btS7h1OZ.js → cloudflare-gateway-CP9QEWYS.js} +21 -25
- package/dist/src/{codex-sdk-DSxAnbfT.js → codex-sdk-C6UMlxwV.js} +28 -29
- package/dist/src/{codex-sdk-IYVi9fuM.js → codex-sdk-DUwKWezN.js} +28 -27
- package/dist/src/{codex-sdk-DulY0ZRq.js → codex-sdk-GGAw0qbD.js} +28 -29
- package/dist/src/{codex-sdk-DFKMtAyf.cjs → codex-sdk-fAO0c3yA.cjs} +28 -29
- package/dist/src/{cometapi-DzrR3SR_.js → cometapi-BL9yvj_f.js} +16 -4
- package/dist/src/{cometapi-DIO64tf4.cjs → cometapi-C4xSqeID.cjs} +21 -22
- package/dist/src/{cometapi-C9EEpJzT.js → cometapi-CUQq3H_a.js} +21 -24
- package/dist/src/{cometapi-DkNBMk0G.js → cometapi-DFNiKmSz.js} +17 -19
- package/dist/src/{completion-CG29bfKX.js → completion-5MzrpJxT.js} +11 -13
- package/dist/src/{completion-CCRT4kX1.cjs → completion-CM6oK8PS.cjs} +21 -23
- package/dist/src/{completion-Bgf1VJoq.js → completion-DZ083F31.js} +11 -13
- package/dist/src/completion-qRoZAYRB.js +120 -0
- package/dist/src/{createHash-Dw_iLu31.js → createHash-CTQmL3G2.js} +2 -3
- package/dist/src/{createHash-CYQy4YeL.cjs → createHash-CfZSc0b4.cjs} +13 -14
- package/dist/src/{createHash-CJcfskIZ.js → createHash-Da8fMwqB.js} +2 -3
- package/dist/src/createHash-DmPQkvBh.js +15 -0
- package/dist/src/{docker-D-ayp2FW.js → docker-Bb5dcxr8.js} +18 -20
- package/dist/src/{docker-B81N0t4e.js → docker-BvfL2BrW.js} +19 -21
- package/dist/src/{docker-DNcLR4Ig.cjs → docker-DcF2pRrj.cjs} +18 -19
- package/dist/src/{docker-egERKxCF.js → docker-ExVyLp0S.js} +18 -7
- package/dist/src/entrypoint.js +2 -3
- package/dist/src/{errors-DnGCbnx8.js → errors-P6ll7XSJ.js} +2 -2
- package/dist/src/{esm-B9dPm_BF.js → esm-C03C-mv3.js} +17 -20
- package/dist/src/{esm-D2pZ87fL.js → esm-CaIwzWR5.js} +18 -21
- package/dist/src/esm-Cd1AjG1D.js +379 -0
- package/dist/src/{esm-Ct-Joyue.cjs → esm-CnNt7sI4.cjs} +47 -49
- package/dist/src/eval-B3r2CVXr.js +15 -0
- package/dist/src/{eval-C-Nr6wX_.js → eval-Dg2nG4v2.js} +47 -54
- package/dist/src/evalResult-5xwYnECe.js +12 -0
- package/dist/src/evalResult-71lY93Kj.cjs +10 -0
- package/dist/src/{evalResult-DXMWJ3sx.js → evalResult-BBRNtX4I.js} +10 -11
- package/dist/src/{evalResult-4BzI2tmj.js → evalResult-BDMqrapS.js} +16 -12
- package/dist/src/evalResult-Dx5P5cIv.js +10 -0
- package/dist/src/{evalResult-CX8wQecI.cjs → evalResult-fuaI8HkH.cjs} +20 -21
- package/dist/src/{evaluator-8aGyV12L.js → evaluator-BhoWwp5b.js} +211 -235
- package/dist/src/evaluator-Jx6bRZV6.js +36 -0
- package/dist/src/{extractor-V5x_m1i0.js → extractor-C0EVHewb.js} +22 -24
- package/dist/src/extractor-D25qpmGX.js +374 -0
- package/dist/src/{extractor-CD5yKL-G.js → extractor-DReVID0K.js} +22 -24
- package/dist/src/{extractor-C031XmTA.cjs → extractor-pYLLi3wS.cjs} +37 -39
- package/dist/src/{fetch-BmbD-v1L.cjs → fetch-BPkYtG8K.cjs} +244 -277
- package/dist/src/fetch-BxNb_Lp3.js +5 -0
- package/dist/src/{fetch-D3OHf-lV.js → fetch-Cwxnd8zz.js} +36 -44
- package/dist/src/{fetch-CXZI9RRr.js → fetch-Dxpd4_sr.js} +23 -35
- package/dist/src/fetch-HaqdX7U1.js +780 -0
- package/dist/src/{fileExtensions-ePDqouxn.js → fileExtensions-DnqA1y9x.js} +2 -2
- package/dist/src/{fileExtensions-BpuMmaFL.js → fileExtensions-Ds-foDzt.js} +2 -2
- package/dist/src/fileExtensions-LcDYkU4v.js +85 -0
- package/dist/src/{fileExtensions-DkJYkWUy.cjs → fileExtensions-bYh77CN8.cjs} +27 -28
- package/dist/src/{formatDuration-CdevI3An.js → formatDuration-DgBVMN65.js} +2 -2
- package/dist/src/{genaiTracer-Ce19n68P.js → genaiTracer-70Z8BIuV.js} +2 -3
- package/dist/src/{genaiTracer-CqNnnXrE.js → genaiTracer-C1rxGO8Q.js} +2 -3
- package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
- package/dist/src/{genaiTracer-Dres3qrN.cjs → genaiTracer-DN4dQywX.cjs} +13 -14
- package/dist/src/{graders--1y2u9HO.js → graders-BTeBGqjJ.js} +349 -397
- package/dist/src/graders-B_pgMLS2.js +34 -0
- package/dist/src/{graders-DTeBrzWp.js → graders-Bj_Odv7c.js} +349 -397
- package/dist/src/graders-DErokPDO.cjs +32 -0
- package/dist/src/graders-DP7KFFo-.js +13466 -0
- package/dist/src/graders-DR_uNe54.js +32 -0
- package/dist/src/{graders-DohM2dir.cjs → graders-DU49_J8Y.cjs} +684 -732
- package/dist/src/graders-w3176Wz-.js +32 -0
- package/dist/src/{image-B0U4Hqll.js → image-B02ogr_b.js} +7 -9
- package/dist/src/{image-DmE-niFE.js → image-B0h9VEMc.js} +6 -5
- package/dist/src/{image-CuKHuccK.cjs → image-BLmROtN3.cjs} +29 -30
- package/dist/src/{image-DNEIf_aI.js → image-Bb4vWQLM.js} +6 -8
- package/dist/src/{image-DpKl2F15.cjs → image-C1madmKh.cjs} +6 -7
- package/dist/src/{image-C3wHC9_h.js → image-CHfWvljl.js} +9 -10
- package/dist/src/{image-O1u4bCFg.js → image-DS-o-0ph.js} +9 -10
- package/dist/src/image-Dpxa1Jt6.js +257 -0
- package/dist/src/index.cjs +615 -695
- package/dist/src/index.d.cts +271 -7
- package/dist/src/index.d.ts +271 -3
- package/dist/src/index.js +580 -664
- package/dist/src/{interactiveCheck-Bxj1Swex.js → interactiveCheck-BgLZUIt3.js} +7 -8
- package/dist/src/{invariant-DT20jrBd.js → invariant-BtWWVVhl.js} +2 -2
- package/dist/src/{invariant-1pAf2CD1.js → invariant-Ddh24eXh.js} +2 -2
- package/dist/src/{invariant-CKcJAQ6M.cjs → invariant-kfQ8Bu82.cjs} +7 -8
- package/dist/src/invariant-vgHWClmd.js +25 -0
- package/dist/src/{knowledgeBase-CEzQobWX.js → knowledgeBase-B3OoKIej.js} +14 -9
- package/dist/src/{knowledgeBase-Be_zyW4L.js → knowledgeBase-CYTLHOt1.js} +16 -16
- package/dist/src/{knowledgeBase-BZ41IFwq.js → knowledgeBase-D33Ty2l6.js} +14 -18
- package/dist/src/{knowledgeBase-D-5BMXlr.cjs → knowledgeBase-DOO_BM9b.cjs} +14 -15
- package/dist/src/{litellm-DnbRJ2if.js → litellm-AaeZcZQF.js} +18 -19
- package/dist/src/{litellm-hUSNM_M2.cjs → litellm-I_hbp_dc.cjs} +17 -17
- package/dist/src/{litellm-CRDqPhNI.js → litellm-NbjknEh6.js} +17 -18
- package/dist/src/{litellm-9vR8zpfU.js → litellm-TrljxD9G.js} +17 -5
- package/dist/src/{logger-CG1uZPbQ.js → logger-CT3IKMKA.js} +10 -29
- package/dist/src/{logger-B7sBeGa0.cjs → logger-Cp1GPUjj.cjs} +152 -180
- package/dist/src/logger-DLcq4dWf.js +713 -0
- package/dist/src/{logger-LSBxlt7a.js → logger-KkObSCzq.js} +13 -31
- package/dist/src/{luma-ray-4blv9iZ2.js → luma-ray-BS2_tY8L.js} +22 -21
- package/dist/src/{luma-ray-drvgdpP9.js → luma-ray-DDsjcgZZ.js} +20 -13
- package/dist/src/{luma-ray-Hm3d6VJE.cjs → luma-ray-Due0n7di.cjs} +20 -21
- package/dist/src/{luma-ray-B2__8lYH.js → luma-ray-f6I2fft-.js} +20 -23
- package/dist/src/main.js +1170 -1321
- package/dist/src/{messages-Uee41Mj5.js → messages-BS17jdMx.js} +22 -24
- package/dist/src/{messages-XhiwCbi4.cjs → messages-Bs1kC7P4.cjs} +32 -34
- package/dist/src/{messages-CGPPidQr.js → messages-D0lx5qK7.js} +22 -24
- package/dist/src/messages-ZJk778GH.js +240 -0
- package/dist/src/{meteor-BYykdXrV.js → meteor-44VjEACX.js} +3 -4
- package/dist/src/{meteor-CsopaHrH.js → meteor-D-SotUw9.js} +3 -4
- package/dist/src/{meteor-e-E-2vVl.cjs → meteor-DLZZ3osF.cjs} +3 -4
- package/dist/src/{meteor-C8lGP6P4.js → meteor-DUiCJRC-.js} +3 -4
- package/dist/src/{modelslab-yKz-ZNB4.js → modelslab-Bmni6skY.js} +17 -10
- package/dist/src/{modelslab-E9gO-bYd.js → modelslab-Bx9IrZfS.js} +18 -20
- package/dist/src/{modelslab-lUVW0cmB.cjs → modelslab-CoUX6Jc_.cjs} +17 -18
- package/dist/src/{modelslab-ClBkr8_9.js → modelslab-DRb74SP4.js} +17 -19
- package/dist/src/{nova-reel-Dk8jNpId.js → nova-reel-BfPq-0Yk.js} +20 -13
- package/dist/src/{nova-reel-D8CuO6QH.cjs → nova-reel-C_QM18Xn.cjs} +20 -21
- package/dist/src/{nova-reel-u2eF2Cxm.js → nova-reel-D_W1tjMH.js} +22 -21
- package/dist/src/{nova-reel-P9bwvtYX.js → nova-reel-bgjxilYW.js} +20 -23
- package/dist/src/{nova-sonic-CK2rAiKi.js → nova-sonic-CFb5GYhg.js} +30 -26
- package/dist/src/{nova-sonic-BaqWlkds.js → nova-sonic-DIGQNR07.js} +30 -31
- package/dist/src/{nova-sonic-yZapPLv7.js → nova-sonic-De1HW5fD.js} +31 -32
- package/dist/src/{nova-sonic-Ds1C-dpm.cjs → nova-sonic-zfcljeRp.cjs} +30 -31
- package/dist/src/{openai-DUFopMrH.cjs → openai-Cuif0GEt.cjs} +8 -9
- package/dist/src/{openai-PblZ3jUE.js → openai-DElQ-fPX.js} +3 -4
- package/dist/src/{openai-CcN1B8Sb.js → openai-DhbB7eWK.js} +3 -4
- package/dist/src/openai-j-sE2O7r.js +44 -0
- package/dist/src/{openclaw-B6qqDr_u.cjs → openclaw-CSugPYAr.cjs} +188 -130
- package/dist/src/{openclaw-A-3_loM7.js → openclaw-DiSz3I5L.js} +180 -109
- package/dist/src/{openclaw-a3lylB-V.js → openclaw-DuvJKEW5.js} +178 -124
- package/dist/src/{openclaw-COn6QzDi.js → openclaw-tiVYRtr-.js} +178 -122
- package/dist/src/opencode-sdk-0j6rTWNb.js +562 -0
- package/dist/src/opencode-sdk-B3CWY9h_.js +560 -0
- package/dist/src/opencode-sdk-BL764Jdi.cjs +564 -0
- package/dist/src/opencode-sdk-C2y6UkP2.js +560 -0
- package/dist/src/{otlpReceiver-oyf5wLGC.js → otlpReceiver-C99PPb48.js} +53 -51
- package/dist/src/{otlpReceiver-lXsYVbpj.cjs → otlpReceiver-CGq6LspY.cjs} +53 -55
- package/dist/src/{otlpReceiver-94URx7UW.js → otlpReceiver-CdNBdbsk.js} +53 -55
- package/dist/src/{otlpReceiver-BmmTiMjA.js → otlpReceiver-D89fR-rC.js} +53 -55
- package/dist/src/{providerRegistry-Cq_JK_CJ.js → providerRegistry-B0RUOLI_.js} +7 -8
- package/dist/src/{providerRegistry-DSSHjMKf.js → providerRegistry-CD8MEar9.js} +7 -8
- package/dist/src/{providerRegistry-CvHEVJad.cjs → providerRegistry-Civky8Ar.cjs} +12 -13
- package/dist/src/providerRegistry-DM8rZYol.js +45 -0
- package/dist/src/providers-B7V0njNs.js +32 -0
- package/dist/src/providers-BEwbhv0X.js +30 -0
- package/dist/src/{providers-Iil64vk9.js → providers-BlqUifFg.js} +1543 -1676
- package/dist/src/providers-CH3C7zf7.js +30 -0
- package/dist/src/{providers-DHbjzW2e.cjs → providers-CgKOSgTR.cjs} +1896 -2029
- package/dist/src/providers-D8lF1sqW.js +33246 -0
- package/dist/src/{providers-BnFpbY_s.js → providers-Dk_6ocUX.js} +1536 -1669
- package/dist/src/providers-zyB6k_38.cjs +31 -0
- package/dist/src/{pythonUtils-CcT5LH1M.js → pythonUtils-C3py6GC1.js} +18 -19
- package/dist/src/{pythonUtils-DBbuI3QJ.cjs → pythonUtils-CTU3Y3lw.cjs} +42 -43
- package/dist/src/{pythonUtils-hZ8LeQLv.js → pythonUtils-D5nxkQ0P.js} +18 -19
- package/dist/src/pythonUtils-D6fwaDSg.js +249 -0
- package/dist/src/{quiverai-BuI0tE39.js → quiverai-BbOUOn2L.js} +8 -7
- package/dist/src/{quiverai-DCGSZt4U.js → quiverai-CIaELU_m.js} +8 -10
- package/dist/src/{quiverai-DiMVJQDz.cjs → quiverai-PdShCPox.cjs} +8 -9
- package/dist/src/{quiverai-fQNkExW4.js → quiverai-uH-dcTIr.js} +9 -11
- package/dist/src/{render-Dj1smHEb.js → render-Drod8m7K.js} +4 -5
- package/dist/src/responses-CB2jwoAr.js +660 -0
- package/dist/src/{responses-ghR3IOfy.cjs → responses-D8SBTL64.cjs} +39 -42
- package/dist/src/{responses-DOAFFENS.js → responses-DIR9Ud3j.js} +24 -27
- package/dist/src/{responses-CxzoQoBe.js → responses-WNGNYe3K.js} +24 -27
- package/dist/src/rubyUtils-BUHu6PhO.js +5 -0
- package/dist/src/{rubyUtils-CwbGmgYN.js → rubyUtils-BUVePouc.js} +27 -20
- package/dist/src/rubyUtils-BcuGX77l.js +222 -0
- package/dist/src/{rubyUtils-DudlFZed.js → rubyUtils-Boc4HZzX.js} +18 -19
- package/dist/src/rubyUtils-CP42kMvq.cjs +4 -0
- package/dist/src/{rubyUtils-C8MhKGHb.cjs → rubyUtils-DhCAlxZr.cjs} +48 -50
- package/dist/src/{sagemaker-gmskuyre.js → sagemaker-CNBxx5CJ.js} +75 -70
- package/dist/src/{sagemaker-CcxhlOAR.js → sagemaker-CemTFp2h.js} +75 -79
- package/dist/src/{sagemaker-77zbJ2Q2.cjs → sagemaker-Cl28mZU2.cjs} +75 -76
- package/dist/src/{sagemaker-DuM71dVU.js → sagemaker-YSyBXQQh.js} +77 -77
- package/dist/src/{scanner-DJYiSXQj.js → scanner-BsBlNXNn.js} +100 -121
- package/dist/src/server/index.js +5520 -67427
- package/dist/src/{server-B5v33lvE.cjs → server-C_7Ax-hA.cjs} +57 -67
- package/dist/src/{server-BJ4m4f1D.js → server-CqzrVGpF.js} +26 -29
- package/dist/src/server-CuxBbeSY.js +229 -0
- package/dist/src/server-DA4Cyrrq.js +7 -0
- package/dist/src/server-Dulb-4-K.cjs +5 -0
- package/dist/src/{server-RV_i_YX5.js → server-VWgWb00X.js} +19 -24
- package/dist/src/{signal-BW33JuId.js → signal-4U3mfRvL.js} +9 -11
- package/dist/src/{slack-DEURelTy.cjs → slack-BmVAVGaK.cjs} +7 -8
- package/dist/src/{slack-BQYeW9L3.js → slack-DCUPTzS2.js} +8 -8
- package/dist/src/{slack-BB6yuZzp.js → slack-DOdy_kyv.js} +7 -8
- package/dist/src/{slack-2pRrhhgJ.js → slack-DXMKtA-f.js} +7 -9
- package/dist/src/store-CXGFv4aR.js +228 -0
- package/dist/src/store-CXS-Q_91.js +6 -0
- package/dist/src/{store-D7CgQzAR.cjs → store-DLlFCC4h.cjs} +44 -45
- package/dist/src/{store-DJNsD1iC.js → store-DXilxTl-.js} +40 -36
- package/dist/src/{store-s3SftUwF.js → store-Dim__MDd.js} +34 -35
- package/dist/src/store-eYkaKMwq.cjs +5 -0
- package/dist/src/{tables-DfTsNN7X.js → tables-6YKwjN9-.js} +19 -21
- package/dist/src/tables-DLJPUdUE.js +288 -0
- package/dist/src/{tables-BKTmd6u7.cjs → tables-DPi7wKeM.cjs} +89 -91
- package/dist/src/{tables-DMegD0Xf.js → tables-gftXzE9I.js} +21 -23
- package/dist/src/telemetry-BpMfhthR.cjs +5 -0
- package/dist/src/{telemetry--WAdAfVi.js → telemetry-CMrFgtPB.js} +11 -13
- package/dist/src/telemetry-Cps3mIU-.js +171 -0
- package/dist/src/{telemetry-DQgVBCAb.cjs → telemetry-DaX14Chu.cjs} +21 -24
- package/dist/src/{telemetry-BedSm-bZ.js → telemetry-Dthj_BbD.js} +17 -14
- package/dist/src/telemetry-Dw38hanS.js +7 -0
- package/dist/src/{text-oiSbwSOI.js → text-B_UCRPp2.js} +2 -2
- package/dist/src/{text-oKzCBnK6.cjs → text-CW1cyrwj.cjs} +12 -13
- package/dist/src/{text-B_IrO4GZ.js → text-Db-Wt2u2.js} +2 -2
- package/dist/src/text-TIv0QYnd.js +22 -0
- package/dist/src/{tokenUsageUtils-FZd5O_4A.js → tokenUsageUtils-BDGe-iyI.js} +2 -2
- package/dist/src/{tokenUsageUtils-DmZSD2eU.js → tokenUsageUtils-DflFMjS0.js} +2 -2
- package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
- package/dist/src/{tokenUsageUtils-CXhxVj72.cjs → tokenUsageUtils-bVa1ga6f.cjs} +32 -33
- package/dist/src/{transcription-mYS9vd5v.js → transcription-BNYURcXg.js} +14 -7
- package/dist/src/{transcription-X2-B4vkX.js → transcription-B_OdaHp7.js} +14 -16
- package/dist/src/{transcription-BO1AHegO.cjs → transcription-NLVG9MT1.cjs} +14 -15
- package/dist/src/{transcription-lzBLiTFJ.js → transcription-s6A-bNrZ.js} +15 -17
- package/dist/src/{transform-B1Hi5lWS.cjs → transform-CzK1Q0zl.cjs} +24 -26
- package/dist/src/{transform-DeGlxb0D.js → transform-D5HsjduX.js} +39 -47
- package/dist/src/{transform-CYDILYDe.js → transform-DECvGmzp.js} +15 -13
- package/dist/src/transform-DTGDnAzW.js +6 -0
- package/dist/src/{transform-BEgStbHK.js → transform-DilY9wbS.js} +10 -12
- package/dist/src/{transform-D5PjiWiZ.cjs → transform-DuHvhZpj.cjs} +179 -187
- package/dist/src/transform-aa6tmVpZ.js +216 -0
- package/dist/src/transform-m3qNw4KP.cjs +5 -0
- package/dist/src/transform-uAytVuyX.js +1506 -0
- package/dist/src/{transform-Dfl89yi4.js → transform-vNucnNr0.js} +39 -47
- package/dist/src/{transformersAvailability-SZnTS3pJ.js → transformersAvailability-CEVM2GNQ.js} +2 -2
- package/dist/src/{transformersAvailability-D-glmEy7.cjs → transformersAvailability-CwayUSlh.cjs} +2 -3
- package/dist/src/{transformersAvailability-CjeFXhuJ.js → transformersAvailability-D6c6ROpT.js} +2 -2
- package/dist/src/{types-DWNf48sT.cjs → types-C_7nyzr1.cjs} +538 -574
- package/dist/src/{types-CXQduE9o.js → types-Cbd8uOMq.js} +68 -100
- package/dist/src/types-CzW2QFyi.js +3288 -0
- package/dist/src/{types-C5hEkb-x.js → types-DmyIJ-sR.js} +63 -99
- package/dist/src/{util-CoQjmE3u.js → util-B3xGByQh.js} +4 -5
- package/dist/src/{util-aLhtl3fe.cjs → util-B9vlHIIh.cjs} +208 -223
- package/dist/src/{util-Du96oyYS.js → util-BHGHw5G1.js} +4 -5
- package/dist/src/{util-DQ984syk.js → util-BRYkYPTd.js} +36 -51
- package/dist/src/{util-D9eLdGfa.js → util-BV4XUC0n.js} +5 -6
- package/dist/src/util-Bv6uGDfH.js +293 -0
- package/dist/src/{util-1wWM599Z.cjs → util-BzMcevZc.cjs} +50 -51
- package/dist/src/{util-_h4pVqrz.js → util-C1CeHl-P.js} +36 -51
- package/dist/src/{util-Bm_-UMD_.js → util-CMy69ZgQ.js} +5 -6
- package/dist/src/{util-CyUdMzV0.cjs → util-DGNOS1db.cjs} +34 -35
- package/dist/src/util-Dnmk2mBQ.js +599 -0
- package/dist/src/util-ZzmqNPlg.js +1426 -0
- package/dist/src/{utils-BjLy-Q72.cjs → utils-Cz9qXqII.cjs} +29 -32
- package/dist/src/{utils-CFMn2yHW.js → utils-XiOAgly5.js} +4 -7
- package/dist/src/utils-dLokC-eR.js +94 -0
- package/dist/src/{utils-DvWMzuMx.js → utils-f2-Moju7.js} +4 -7
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -38
- package/dist/src/app/assets/index-B2D0bCSI.js +0 -439
- package/dist/src/app/assets/vendor-charts-CCl15Imd.js +0 -36
- package/dist/src/cache-ChPcurj7.js +0 -6
- package/dist/src/cache-VVu_W-yg.js +0 -8
- package/dist/src/cache-YLNCFEM2.cjs +0 -6
- package/dist/src/chunk-DHDDz29n.js +0 -22
- package/dist/src/chunk-FhC4c-0y.js +0 -21
- package/dist/src/cloud-BndfXy4H.js +0 -5
- package/dist/src/eval-BhHvMY82.js +0 -17
- package/dist/src/evalResult-Dq2gFNQY.js +0 -12
- package/dist/src/evalResult-nmcP5VKH.cjs +0 -12
- package/dist/src/evalResult-trqZjVYh.js +0 -14
- package/dist/src/evaluator-CnfPstzT.js +0 -39
- package/dist/src/fetch-IDPDue6F.cjs +0 -4
- package/dist/src/fetch-hKJ-It8q.js +0 -6
- package/dist/src/fetch-ouKnrWK-.js +0 -4
- package/dist/src/graders-CQn7WUsd.cjs +0 -34
- package/dist/src/graders-DC6QAbpW.js +0 -35
- package/dist/src/graders-DUWz3Y7j.js +0 -37
- package/dist/src/opencode-sdk-4bL9n-Gk.js +0 -382
- package/dist/src/opencode-sdk-BfC2zWcR.js +0 -376
- package/dist/src/opencode-sdk-DMJyuwMg.js +0 -380
- package/dist/src/opencode-sdk-Da-9adza.cjs +0 -383
- package/dist/src/providers-CsXB2Ix-.js +0 -35
- package/dist/src/providers-DO8ltjLC.js +0 -33
- package/dist/src/providers-Dtq-xnXd.cjs +0 -33
- package/dist/src/rubyUtils-BUbcND2f.js +0 -6
- package/dist/src/rubyUtils-Cr55X_KE.js +0 -5
- package/dist/src/rubyUtils-DlIiqoYo.cjs +0 -5
- package/dist/src/server-C2eQH4Gu.js +0 -6
- package/dist/src/server-CXWycu7H.cjs +0 -6
- package/dist/src/server-Q6OGlxxT.js +0 -8
- package/dist/src/store-B3EDO9Q3.js +0 -7
- package/dist/src/store-Dl9F8aw5.js +0 -6
- package/dist/src/store-SnrGrlt9.cjs +0 -6
- package/dist/src/telemetry-BGhiPZtl.js +0 -8
- package/dist/src/telemetry-CFfiYan6.cjs +0 -6
- package/dist/src/telemetry-DHzEduxX.js +0 -6
- package/dist/src/transform-C1x1ZlMQ.cjs +0 -6
- package/dist/src/transform-DYHjFmQu.js +0 -8
- package/dist/src/transform-rmwJT5JQ.js +0 -7
- package/dist/src/transformersAvailability-eJooj0gX.js +0 -35
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-DEq-mXcV.js";
|
|
2
|
+
import { _ as getEnvBool, b as getEnvString, c as sanitizeUrl, g as getConfigDirectoryPath, i as logger, r as logRequestResponse, w as state, y as getEnvInt } from "./logger-DLcq4dWf.js";
|
|
3
|
+
import { t as invariant } from "./invariant-vgHWClmd.js";
|
|
4
|
+
import * as fs$1 from "fs";
|
|
5
|
+
import * as path$1 from "path";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import yaml from "js-yaml";
|
|
8
|
+
import * as fsPromises from "fs/promises";
|
|
9
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
10
|
+
import { Agent, ProxyAgent } from "undici";
|
|
11
|
+
import { promisify } from "util";
|
|
12
|
+
import { gzip } from "zlib";
|
|
13
|
+
//#region src/providers/constants.ts
|
|
14
|
+
const FILE_METADATA_KEY = "_promptfooFileMetadata";
|
|
15
|
+
/**
|
|
16
|
+
* Identifier for manual user ratings in componentResults.
|
|
17
|
+
* Used to distinguish human ratings from automated assertions.
|
|
18
|
+
*/
|
|
19
|
+
const HUMAN_ASSERTION_TYPE = "human";
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/version.ts
|
|
22
|
+
/**
|
|
23
|
+
* Application version from package.json.
|
|
24
|
+
* Injected at build time, or read from npm environment in development.
|
|
25
|
+
*/
|
|
26
|
+
const VERSION = "0.121.2";
|
|
27
|
+
/**
|
|
28
|
+
* PostHog analytics key.
|
|
29
|
+
* Only populated during production builds via PROMPTFOO_POSTHOG_KEY env var.
|
|
30
|
+
* Empty string in development/test.
|
|
31
|
+
*/
|
|
32
|
+
const POSTHOG_KEY = "phc_E5n5uHnDo2eREJL1uqX1cIlbkoRby4yFWt3V94HqRRg";
|
|
33
|
+
function getShareApiBaseUrl() {
|
|
34
|
+
return getEnvString("PROMPTFOO_REMOTE_API_BASE_URL") || "https://api.promptfoo.app";
|
|
35
|
+
}
|
|
36
|
+
function getDefaultShareViewBaseUrl() {
|
|
37
|
+
return getEnvString("PROMPTFOO_SHARING_APP_BASE_URL", `https://promptfoo.app`);
|
|
38
|
+
}
|
|
39
|
+
function getShareViewBaseUrl() {
|
|
40
|
+
return getEnvString("PROMPTFOO_REMOTE_APP_BASE_URL") || getDefaultShareViewBaseUrl();
|
|
41
|
+
}
|
|
42
|
+
function getDefaultPort() {
|
|
43
|
+
return getEnvInt("API_PORT", 15500);
|
|
44
|
+
}
|
|
45
|
+
const TERMINAL_MAX_WIDTH = process?.stdout?.isTTY && process?.stdout?.columns && process?.stdout?.columns > 10 ? process?.stdout?.columns - 10 : 120;
|
|
46
|
+
const CLOUD_PROVIDER_PREFIX = "promptfoo://provider/";
|
|
47
|
+
const CONSENT_ENDPOINT = "https://api.promptfoo.dev/consent";
|
|
48
|
+
const EVENTS_ENDPOINT = "https://a.promptfoo.app";
|
|
49
|
+
const R_ENDPOINT = "https://r.promptfoo.app/";
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/providers/shared.ts
|
|
52
|
+
/**
|
|
53
|
+
* The default timeout for API requests in milliseconds.
|
|
54
|
+
*/
|
|
55
|
+
const REQUEST_TIMEOUT_MS = getEnvInt("REQUEST_TIMEOUT_MS", 3e5);
|
|
56
|
+
/**
|
|
57
|
+
* Extended timeout for long-running models (deep research, gpt-5-pro, etc.) in milliseconds.
|
|
58
|
+
* These models can take significantly longer to respond due to their complex reasoning.
|
|
59
|
+
*/
|
|
60
|
+
const LONG_RUNNING_MODEL_TIMEOUT_MS = 6e5;
|
|
61
|
+
/**
|
|
62
|
+
* Calculates the cost of an API call based on the model and token usage.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} modelName The name of the model used.
|
|
65
|
+
* @param {ProviderConfig} config The provider configuration.
|
|
66
|
+
* @param {number | undefined} promptTokens The number of tokens in the prompt.
|
|
67
|
+
* @param {number | undefined} completionTokens The number of tokens in the completion.
|
|
68
|
+
* @param {ProviderModel[]} models An array of available models with their costs.
|
|
69
|
+
* @returns {number | undefined} The calculated cost, or undefined if it can't be calculated.
|
|
70
|
+
*/
|
|
71
|
+
function calculateCost(modelName, config, promptTokens, completionTokens, models) {
|
|
72
|
+
if (!Number.isFinite(promptTokens) || !Number.isFinite(completionTokens) || typeof promptTokens === "undefined" || typeof completionTokens === "undefined") return;
|
|
73
|
+
const model = models.find((m) => m.id === modelName);
|
|
74
|
+
if (!model || !model.cost) return;
|
|
75
|
+
const inputCost = config.cost ?? model.cost.input;
|
|
76
|
+
const outputCost = config.cost ?? model.cost.output;
|
|
77
|
+
return inputCost * promptTokens + outputCost * completionTokens;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Checks if a string looks like it's attempting to be JSON.
|
|
81
|
+
* This helps distinguish between actual JSON attempts and plain text that happens to start/end with brackets.
|
|
82
|
+
*/
|
|
83
|
+
function looksLikeJson(prompt) {
|
|
84
|
+
const trimmed = prompt.trim();
|
|
85
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return true;
|
|
86
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
87
|
+
const afterBracket = trimmed.slice(1).trimStart();
|
|
88
|
+
if (afterBracket.startsWith("\"") || afterBracket.startsWith("{") || afterBracket.startsWith("[") || /^[\d-]/.test(afterBracket) || /^(true|false|null)/.test(afterBracket)) return true;
|
|
89
|
+
if (afterBracket.length === 0 || /^\s+$/.test(afterBracket)) return true;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parses a chat prompt string into a structured format.
|
|
96
|
+
*
|
|
97
|
+
* @template T The expected return type of the parsed prompt.
|
|
98
|
+
* @param {string} prompt The input prompt string to parse.
|
|
99
|
+
* @param {T} defaultValue The default value to return if parsing fails.
|
|
100
|
+
* @returns {T} The parsed prompt or the default value.
|
|
101
|
+
* @throws {Error} If the prompt is invalid YAML or JSON (when required).
|
|
102
|
+
*/
|
|
103
|
+
function parseChatPrompt(prompt, defaultValue) {
|
|
104
|
+
const trimmedPrompt = prompt.trim();
|
|
105
|
+
if (trimmedPrompt.startsWith("- role:")) try {
|
|
106
|
+
return yaml.load(prompt);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
throw new Error(`Chat Completion prompt is not a valid YAML string: ${err}\n\n${prompt}`);
|
|
109
|
+
}
|
|
110
|
+
else try {
|
|
111
|
+
return JSON.parse(prompt);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (getEnvBool("PROMPTFOO_REQUIRE_JSON_PROMPTS") || looksLikeJson(trimmedPrompt)) throw new Error(`Chat Completion prompt is not a valid JSON string: ${err}\n\n${prompt}`);
|
|
114
|
+
return defaultValue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Converts a string to title case.
|
|
119
|
+
*
|
|
120
|
+
* @param {string} str The input string to convert.
|
|
121
|
+
* @returns {string} The input string converted to title case.
|
|
122
|
+
*/
|
|
123
|
+
function toTitleCase(str) {
|
|
124
|
+
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
125
|
+
}
|
|
126
|
+
function isPromptfooSampleTarget(provider) {
|
|
127
|
+
const url = provider.config?.url;
|
|
128
|
+
return url?.includes("promptfoo.app") || url?.includes("promptfoo.dev");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Checks if the given value is an OpenAI tool choice format.
|
|
132
|
+
* Detects string values ('auto', 'none', 'required') and
|
|
133
|
+
* the object form ({ type: 'function', function: { name } }).
|
|
134
|
+
*/
|
|
135
|
+
function isOpenAIToolChoice(obj) {
|
|
136
|
+
if (typeof obj === "string") return [
|
|
137
|
+
"auto",
|
|
138
|
+
"none",
|
|
139
|
+
"required"
|
|
140
|
+
].includes(obj);
|
|
141
|
+
if (typeof obj === "object" && obj !== null) {
|
|
142
|
+
const candidate = obj;
|
|
143
|
+
if (candidate.type === "function" && typeof candidate.function === "object" && candidate.function !== null) return typeof candidate.function.name === "string";
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Transforms an OpenAI tool choice to Anthropic format.
|
|
149
|
+
*/
|
|
150
|
+
function openaiToolChoiceToAnthropic(choice) {
|
|
151
|
+
if (typeof choice === "string") switch (choice) {
|
|
152
|
+
case "auto": return { type: "auto" };
|
|
153
|
+
case "none": return { type: "auto" };
|
|
154
|
+
case "required": return { type: "any" };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
type: "tool",
|
|
158
|
+
name: choice.function.name
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Transforms an OpenAI tool choice to Bedrock Converse format.
|
|
163
|
+
*/
|
|
164
|
+
function openaiToolChoiceToBedrock(choice) {
|
|
165
|
+
if (typeof choice === "string") switch (choice) {
|
|
166
|
+
case "auto": return { auto: {} };
|
|
167
|
+
case "none": return;
|
|
168
|
+
case "required": return { any: {} };
|
|
169
|
+
}
|
|
170
|
+
return { tool: { name: choice.function.name } };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Transforms an OpenAI tool choice to Google (Gemini) format.
|
|
174
|
+
*/
|
|
175
|
+
function openaiToolChoiceToGoogle(choice) {
|
|
176
|
+
if (typeof choice === "string") switch (choice) {
|
|
177
|
+
case "auto": return { functionCallingConfig: { mode: "AUTO" } };
|
|
178
|
+
case "none": return { functionCallingConfig: { mode: "NONE" } };
|
|
179
|
+
case "required": return { functionCallingConfig: { mode: "ANY" } };
|
|
180
|
+
}
|
|
181
|
+
return { functionCallingConfig: {
|
|
182
|
+
mode: "ANY",
|
|
183
|
+
allowedFunctionNames: [choice.function.name]
|
|
184
|
+
} };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Transforms an OpenAI tool choice to the specified provider format.
|
|
188
|
+
* If the input is not in OpenAI format, it's returned as-is (native passthrough).
|
|
189
|
+
*/
|
|
190
|
+
function transformToolChoice(toolChoice, format) {
|
|
191
|
+
if (!isOpenAIToolChoice(toolChoice)) return toolChoice;
|
|
192
|
+
switch (format) {
|
|
193
|
+
case "openai": return toolChoice;
|
|
194
|
+
case "anthropic": return openaiToolChoiceToAnthropic(toolChoice);
|
|
195
|
+
case "bedrock": return openaiToolChoiceToBedrock(toolChoice);
|
|
196
|
+
case "google": return openaiToolChoiceToGoogle(toolChoice);
|
|
197
|
+
default: return toolChoice;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Checks if an array contains OpenAI-format tools.
|
|
202
|
+
* Returns true if the first tool has `type: 'function'` and `function.name`.
|
|
203
|
+
*/
|
|
204
|
+
function isOpenAIToolArray(tools) {
|
|
205
|
+
if (!Array.isArray(tools) || tools.length === 0) return false;
|
|
206
|
+
const first = tools[0];
|
|
207
|
+
if (typeof first !== "object" || first === null) return false;
|
|
208
|
+
const candidate = first;
|
|
209
|
+
return candidate.type === "function" && typeof candidate.function === "object" && candidate.function !== null && typeof candidate.function.name === "string";
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Transforms OpenAI-format tools to Anthropic format.
|
|
213
|
+
*/
|
|
214
|
+
function openaiToolsToAnthropic(tools) {
|
|
215
|
+
return tools.map((tool) => ({
|
|
216
|
+
name: tool.function.name,
|
|
217
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
218
|
+
input_schema: tool.function.parameters || {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {}
|
|
221
|
+
}
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Transforms OpenAI-format tools to Bedrock Converse format.
|
|
226
|
+
*/
|
|
227
|
+
function openaiToolsToBedrock(tools) {
|
|
228
|
+
return tools.map((tool) => ({ toolSpec: {
|
|
229
|
+
name: tool.function.name,
|
|
230
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
231
|
+
inputSchema: { json: tool.function.parameters || {
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: {}
|
|
234
|
+
} }
|
|
235
|
+
} }));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Sanitizes a schema for Google/Gemini compatibility.
|
|
239
|
+
* - Converts type strings to uppercase (string → STRING)
|
|
240
|
+
* - Removes unsupported properties (additionalProperties, $schema, default)
|
|
241
|
+
* - Recursively processes nested schemas
|
|
242
|
+
*/
|
|
243
|
+
function sanitizeSchemaForGoogle(schema) {
|
|
244
|
+
const result = {};
|
|
245
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
246
|
+
if ([
|
|
247
|
+
"additionalProperties",
|
|
248
|
+
"$schema",
|
|
249
|
+
"default",
|
|
250
|
+
"$id",
|
|
251
|
+
"$ref"
|
|
252
|
+
].includes(key)) continue;
|
|
253
|
+
if (key === "type" && typeof value === "string") result[key] = value.toUpperCase();
|
|
254
|
+
else if (key === "properties" && typeof value === "object" && value !== null) {
|
|
255
|
+
const sanitizedProps = {};
|
|
256
|
+
for (const [propKey, propValue] of Object.entries(value)) if (typeof propValue === "object" && propValue !== null) sanitizedProps[propKey] = sanitizeSchemaForGoogle(propValue);
|
|
257
|
+
else sanitizedProps[propKey] = propValue;
|
|
258
|
+
result[key] = sanitizedProps;
|
|
259
|
+
} else if (key === "items" && typeof value === "object" && value !== null) result[key] = sanitizeSchemaForGoogle(value);
|
|
260
|
+
else result[key] = value;
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Transforms OpenAI-format tools to Google/Gemini format.
|
|
266
|
+
*/
|
|
267
|
+
function openaiToolsToGoogle(tools) {
|
|
268
|
+
return [{ functionDeclarations: tools.map((tool) => ({
|
|
269
|
+
name: tool.function.name,
|
|
270
|
+
...tool.function.description ? { description: tool.function.description } : {},
|
|
271
|
+
...tool.function.parameters ? { parameters: sanitizeSchemaForGoogle(tool.function.parameters) } : {}
|
|
272
|
+
})) }];
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Transforms tools from OpenAI format to the specified provider format.
|
|
276
|
+
* If the input is not in OpenAI format, it's returned as-is.
|
|
277
|
+
*/
|
|
278
|
+
function transformTools(tools, format) {
|
|
279
|
+
if (!isOpenAIToolArray(tools)) return tools;
|
|
280
|
+
switch (format) {
|
|
281
|
+
case "openai": return tools;
|
|
282
|
+
case "anthropic": return openaiToolsToAnthropic(tools);
|
|
283
|
+
case "bedrock": return openaiToolsToBedrock(tools);
|
|
284
|
+
case "google": return openaiToolsToGoogle(tools);
|
|
285
|
+
default: return tools;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/util/time.ts
|
|
290
|
+
function getCurrentTimestamp() {
|
|
291
|
+
return Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
292
|
+
}
|
|
293
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/globalConfig/globalConfig.ts
|
|
296
|
+
/**
|
|
297
|
+
* Functions for manipulating the global configuration file, which lives at
|
|
298
|
+
* ~/.promptfoo/promptfoo.yaml by default.
|
|
299
|
+
*/
|
|
300
|
+
function writeGlobalConfig(config) {
|
|
301
|
+
fs$1.writeFileSync(path$1.join(getConfigDirectoryPath(true), "promptfoo.yaml"), yaml.dump(config));
|
|
302
|
+
}
|
|
303
|
+
function readGlobalConfig() {
|
|
304
|
+
const configDir = getConfigDirectoryPath();
|
|
305
|
+
const configFilePath = path$1.join(configDir, "promptfoo.yaml");
|
|
306
|
+
let globalConfig = { id: crypto.randomUUID() };
|
|
307
|
+
if (fs$1.existsSync(configFilePath)) {
|
|
308
|
+
globalConfig = yaml.load(fs$1.readFileSync(configFilePath, "utf-8")) || {};
|
|
309
|
+
if (!globalConfig?.id) {
|
|
310
|
+
globalConfig = {
|
|
311
|
+
...globalConfig,
|
|
312
|
+
id: crypto.randomUUID()
|
|
313
|
+
};
|
|
314
|
+
writeGlobalConfig(globalConfig);
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
if (!fs$1.existsSync(configDir)) fs$1.mkdirSync(configDir, { recursive: true });
|
|
318
|
+
fs$1.writeFileSync(configFilePath, yaml.dump(globalConfig));
|
|
319
|
+
}
|
|
320
|
+
return globalConfig;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Merges the top-level keys into existing config.
|
|
324
|
+
* @param partialConfig New keys to merge into the existing config.
|
|
325
|
+
*/
|
|
326
|
+
function writeGlobalConfigPartial(partialConfig) {
|
|
327
|
+
const updatedConfig = { ...readGlobalConfig() };
|
|
328
|
+
Object.entries(partialConfig).forEach(([key, value]) => {
|
|
329
|
+
if (value !== void 0 && value !== null) updatedConfig[key] = value;
|
|
330
|
+
else delete updatedConfig[key];
|
|
331
|
+
});
|
|
332
|
+
writeGlobalConfig(updatedConfig);
|
|
333
|
+
}
|
|
334
|
+
const API_HOST = getEnvString("API_HOST", "https://api.promptfoo.app");
|
|
335
|
+
const SHARING_CUTOFF_DATE = /* @__PURE__ */ new Date("2026-03-09T00:00:00Z");
|
|
336
|
+
var CloudConfig = class {
|
|
337
|
+
config;
|
|
338
|
+
constructor() {
|
|
339
|
+
const savedConfig = readGlobalConfig()?.cloud || {};
|
|
340
|
+
this.config = {
|
|
341
|
+
appUrl: savedConfig.appUrl || "https://www.promptfoo.app",
|
|
342
|
+
apiHost: savedConfig.apiHost,
|
|
343
|
+
apiKey: savedConfig.apiKey,
|
|
344
|
+
sharing: savedConfig.sharing,
|
|
345
|
+
currentOrganizationId: savedConfig.currentOrganizationId,
|
|
346
|
+
currentTeamId: savedConfig.currentTeamId,
|
|
347
|
+
teams: savedConfig.teams
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Returns the API key from config file or PROMPTFOO_API_KEY environment variable.
|
|
352
|
+
* Config file takes precedence over environment variable.
|
|
353
|
+
*/
|
|
354
|
+
resolveApiKey() {
|
|
355
|
+
return this.config.apiKey || process.env.PROMPTFOO_API_KEY;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Returns the API host from config file, PROMPTFOO_CLOUD_API_URL environment variable,
|
|
359
|
+
* or defaults to the standard cloud API host.
|
|
360
|
+
* Config file takes precedence over environment variable.
|
|
361
|
+
*/
|
|
362
|
+
resolveApiHost() {
|
|
363
|
+
return this.config.apiHost || process.env.PROMPTFOO_CLOUD_API_URL || API_HOST;
|
|
364
|
+
}
|
|
365
|
+
isEnabled() {
|
|
366
|
+
return !!this.resolveApiKey();
|
|
367
|
+
}
|
|
368
|
+
setApiHost(apiHost) {
|
|
369
|
+
this.config.apiHost = apiHost;
|
|
370
|
+
this.saveConfig();
|
|
371
|
+
}
|
|
372
|
+
setApiKey(apiKey) {
|
|
373
|
+
this.config.apiKey = apiKey;
|
|
374
|
+
this.saveConfig();
|
|
375
|
+
}
|
|
376
|
+
getApiKey() {
|
|
377
|
+
return this.resolveApiKey();
|
|
378
|
+
}
|
|
379
|
+
getApiHost() {
|
|
380
|
+
return this.resolveApiHost();
|
|
381
|
+
}
|
|
382
|
+
setAppUrl(appUrl) {
|
|
383
|
+
this.config.appUrl = appUrl;
|
|
384
|
+
this.saveConfig();
|
|
385
|
+
}
|
|
386
|
+
getAppUrl() {
|
|
387
|
+
return this.config.appUrl;
|
|
388
|
+
}
|
|
389
|
+
getSharing() {
|
|
390
|
+
return this.config.sharing;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Sets the sharing preference. Note: this value is only updated at authentication time
|
|
394
|
+
* (via `validateAndSetApiToken`) and may become stale if the user's license status
|
|
395
|
+
* changes between re-authentications.
|
|
396
|
+
*/
|
|
397
|
+
setSharing(sharing) {
|
|
398
|
+
this.config.sharing = sharing;
|
|
399
|
+
this.saveConfig();
|
|
400
|
+
}
|
|
401
|
+
delete() {
|
|
402
|
+
writeGlobalConfigPartial({ cloud: {} });
|
|
403
|
+
this.reload();
|
|
404
|
+
}
|
|
405
|
+
saveConfig() {
|
|
406
|
+
writeGlobalConfigPartial({ cloud: this.config });
|
|
407
|
+
this.reload();
|
|
408
|
+
}
|
|
409
|
+
reload() {
|
|
410
|
+
const savedConfig = readGlobalConfig()?.cloud || {};
|
|
411
|
+
this.config = {
|
|
412
|
+
appUrl: savedConfig.appUrl || "https://www.promptfoo.app",
|
|
413
|
+
apiHost: savedConfig.apiHost,
|
|
414
|
+
apiKey: savedConfig.apiKey,
|
|
415
|
+
sharing: savedConfig.sharing,
|
|
416
|
+
currentOrganizationId: savedConfig.currentOrganizationId,
|
|
417
|
+
currentTeamId: savedConfig.currentTeamId,
|
|
418
|
+
teams: savedConfig.teams
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async validateAndSetApiToken(token, apiHost) {
|
|
422
|
+
try {
|
|
423
|
+
const { fetchWithProxy } = await Promise.resolve().then(() => fetch_exports);
|
|
424
|
+
const response = await fetchWithProxy(`${apiHost}/api/v1/users/me`, { headers: { Authorization: `Bearer ${token}` } });
|
|
425
|
+
if (!response.ok) {
|
|
426
|
+
const errorMessage = await response.text();
|
|
427
|
+
logger.error(`[Cloud] Failed to validate API token: ${errorMessage}. HTTP Status: ${response.status} - ${response.statusText}.`);
|
|
428
|
+
throw new Error("Failed to validate API token: " + response.statusText);
|
|
429
|
+
}
|
|
430
|
+
const { user, organization, app, hasActiveLicense } = await response.json();
|
|
431
|
+
this.setApiKey(token);
|
|
432
|
+
this.setApiHost(apiHost);
|
|
433
|
+
this.setAppUrl(app.url);
|
|
434
|
+
if (typeof hasActiveLicense === "boolean") {
|
|
435
|
+
const createdAt = user?.createdAt ? new Date(user.createdAt) : null;
|
|
436
|
+
const isGrandfathered = createdAt != null && createdAt < SHARING_CUTOFF_DATE;
|
|
437
|
+
this.setSharing(hasActiveLicense || isGrandfathered);
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
user,
|
|
441
|
+
organization,
|
|
442
|
+
app,
|
|
443
|
+
hasActiveLicense: typeof hasActiveLicense === "boolean" ? hasActiveLicense : false
|
|
444
|
+
};
|
|
445
|
+
} catch (err) {
|
|
446
|
+
const error = err;
|
|
447
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
448
|
+
logger.error(`[Cloud] Failed to validate API token with host ${apiHost}: ${errorMessage}`);
|
|
449
|
+
if (error.cause) logger.error(`Cause: ${error.cause}`);
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
getCurrentOrganizationId() {
|
|
454
|
+
return this.config.currentOrganizationId;
|
|
455
|
+
}
|
|
456
|
+
setCurrentOrganization(organizationId) {
|
|
457
|
+
this.config.currentOrganizationId = organizationId;
|
|
458
|
+
this.saveConfig();
|
|
459
|
+
}
|
|
460
|
+
getCurrentTeamId(organizationId) {
|
|
461
|
+
if (organizationId) return this.config.teams?.[organizationId]?.currentTeamId;
|
|
462
|
+
return this.config.currentTeamId;
|
|
463
|
+
}
|
|
464
|
+
setCurrentTeamId(teamId, organizationId) {
|
|
465
|
+
if (organizationId) {
|
|
466
|
+
if (!this.config.teams) this.config.teams = {};
|
|
467
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
468
|
+
this.config.teams[organizationId].currentTeamId = teamId;
|
|
469
|
+
} else this.config.currentTeamId = teamId;
|
|
470
|
+
this.saveConfig();
|
|
471
|
+
}
|
|
472
|
+
clearCurrentTeamId(organizationId) {
|
|
473
|
+
if (organizationId) {
|
|
474
|
+
if (this.config.teams?.[organizationId]) delete this.config.teams[organizationId].currentTeamId;
|
|
475
|
+
} else delete this.config.currentTeamId;
|
|
476
|
+
this.saveConfig();
|
|
477
|
+
}
|
|
478
|
+
cacheTeams(teams, organizationId) {
|
|
479
|
+
if (organizationId) {
|
|
480
|
+
if (!this.config.teams) this.config.teams = {};
|
|
481
|
+
if (!this.config.teams[organizationId]) this.config.teams[organizationId] = {};
|
|
482
|
+
this.config.teams[organizationId].cache = teams.map((t) => ({
|
|
483
|
+
id: t.id,
|
|
484
|
+
name: t.name,
|
|
485
|
+
slug: t.slug,
|
|
486
|
+
lastFetched: (/* @__PURE__ */ new Date()).toISOString()
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
this.saveConfig();
|
|
490
|
+
}
|
|
491
|
+
getCachedTeams(organizationId) {
|
|
492
|
+
if (organizationId) return this.config.teams?.[organizationId]?.cache;
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const cloudConfig = new CloudConfig();
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/util/fetch/monkeyPatchFetch.ts
|
|
498
|
+
const gzipAsync = promisify(gzip);
|
|
499
|
+
function isConnectionError(error) {
|
|
500
|
+
return error instanceof TypeError && error.message === "fetch failed" && error.cause?.stack?.includes("internalConnectMultiple");
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Enhanced fetch wrapper that adds logging, authentication, error handling, and optional compression
|
|
504
|
+
*/
|
|
505
|
+
async function monkeyPatchFetch(url, options) {
|
|
506
|
+
const NO_LOG_URLS = [
|
|
507
|
+
R_ENDPOINT,
|
|
508
|
+
CONSENT_ENDPOINT,
|
|
509
|
+
EVENTS_ENDPOINT
|
|
510
|
+
];
|
|
511
|
+
const isSilent = (options?.headers || {})["x-promptfoo-silent"] === "true";
|
|
512
|
+
const logEnabled = !NO_LOG_URLS.some((logUrl) => url.toString().startsWith(logUrl)) && !isSilent;
|
|
513
|
+
const opts = { ...options };
|
|
514
|
+
const originalBody = opts.body;
|
|
515
|
+
if (options?.compress && opts.body && typeof opts.body === "string") try {
|
|
516
|
+
opts.body = await gzipAsync(opts.body);
|
|
517
|
+
opts.headers = {
|
|
518
|
+
...opts.headers || {},
|
|
519
|
+
"Content-Encoding": "gzip"
|
|
520
|
+
};
|
|
521
|
+
} catch (e) {
|
|
522
|
+
logger.warn(`Failed to compress request body: ${e}`);
|
|
523
|
+
}
|
|
524
|
+
if (typeof url === "string" && url.startsWith("https://api.promptfoo.app") || url instanceof URL && url.host === "https://api.promptfoo.app".replace(/^https?:\/\//, "")) {
|
|
525
|
+
const token = cloudConfig.getApiKey();
|
|
526
|
+
opts.headers = {
|
|
527
|
+
...opts.headers || {},
|
|
528
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const response = await fetch(url, opts);
|
|
533
|
+
if (logEnabled) logRequestResponse({
|
|
534
|
+
url: url.toString(),
|
|
535
|
+
requestBody: originalBody,
|
|
536
|
+
requestMethod: opts.method || "GET",
|
|
537
|
+
response
|
|
538
|
+
});
|
|
539
|
+
return response;
|
|
540
|
+
} catch (e) {
|
|
541
|
+
if (logEnabled) {
|
|
542
|
+
logRequestResponse({
|
|
543
|
+
url: url.toString(),
|
|
544
|
+
requestBody: opts.body,
|
|
545
|
+
requestMethod: opts.method || "GET",
|
|
546
|
+
response: null
|
|
547
|
+
});
|
|
548
|
+
if (isConnectionError(e)) {
|
|
549
|
+
logger.debug(`Connection error, please check your network connectivity to the host: ${url} ${process.env.HTTP_PROXY || process.env.HTTPS_PROXY ? `or Proxy: ${process.env.HTTP_PROXY || process.env.HTTPS_PROXY}` : ""}`);
|
|
550
|
+
throw e;
|
|
551
|
+
}
|
|
552
|
+
logger.debug(`Error in fetch: ${JSON.stringify(e, Object.getOwnPropertyNames(e), 2)} ${e instanceof Error ? e.stack : ""}`);
|
|
553
|
+
}
|
|
554
|
+
throw e;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
//#endregion
|
|
558
|
+
//#region src/util/fetch/index.ts
|
|
559
|
+
var fetch_exports = /* @__PURE__ */ __exportAll({
|
|
560
|
+
fetchWithProxy: () => fetchWithProxy,
|
|
561
|
+
fetchWithRetries: () => fetchWithRetries,
|
|
562
|
+
fetchWithTimeout: () => fetchWithTimeout,
|
|
563
|
+
handleRateLimit: () => handleRateLimit,
|
|
564
|
+
isRateLimited: () => isRateLimited,
|
|
565
|
+
isTransientError: () => isTransientError
|
|
566
|
+
});
|
|
567
|
+
let cachedAgent = null;
|
|
568
|
+
let cachedAgentConcurrency;
|
|
569
|
+
let cachedProxyAgents = /* @__PURE__ */ new Map();
|
|
570
|
+
/**
|
|
571
|
+
* Get the connection pool size for HTTP agents.
|
|
572
|
+
* Priority: PROMPTFOO_FETCH_CONNECTIONS env var > CLI -j flag > DEFAULT_MAX_CONCURRENCY (4).
|
|
573
|
+
* Set PROMPTFOO_FETCH_CONNECTIONS to override independently of eval concurrency
|
|
574
|
+
* (e.g., server deployments that need more connections than the default 4).
|
|
575
|
+
*/
|
|
576
|
+
function getConnectionPoolSize() {
|
|
577
|
+
const envConnections = getEnvString("PROMPTFOO_FETCH_CONNECTIONS");
|
|
578
|
+
if (envConnections != null) {
|
|
579
|
+
const parsed = parseInt(envConnections, 10);
|
|
580
|
+
if (!isNaN(parsed)) return parsed;
|
|
581
|
+
}
|
|
582
|
+
return state.maxConcurrency || 4;
|
|
583
|
+
}
|
|
584
|
+
function getOrCreateAgent(tlsOptions) {
|
|
585
|
+
const concurrency = getConnectionPoolSize();
|
|
586
|
+
if (cachedAgent && cachedAgentConcurrency !== concurrency) {
|
|
587
|
+
if (typeof cachedAgent.close === "function") cachedAgent.close();
|
|
588
|
+
cachedAgent = null;
|
|
589
|
+
}
|
|
590
|
+
if (!cachedAgent) {
|
|
591
|
+
cachedAgent = new Agent({
|
|
592
|
+
headersTimeout: REQUEST_TIMEOUT_MS,
|
|
593
|
+
keepAliveTimeout: 3e4,
|
|
594
|
+
keepAliveMaxTimeout: 6e4,
|
|
595
|
+
connections: concurrency,
|
|
596
|
+
connect: tlsOptions
|
|
597
|
+
});
|
|
598
|
+
cachedAgentConcurrency = concurrency;
|
|
599
|
+
}
|
|
600
|
+
return cachedAgent;
|
|
601
|
+
}
|
|
602
|
+
function getOrCreateProxyAgent(proxyUrl, tlsOptions) {
|
|
603
|
+
if (!cachedProxyAgents.has(proxyUrl)) {
|
|
604
|
+
const agent = new ProxyAgent({
|
|
605
|
+
uri: proxyUrl,
|
|
606
|
+
proxyTls: tlsOptions,
|
|
607
|
+
requestTls: tlsOptions,
|
|
608
|
+
headersTimeout: REQUEST_TIMEOUT_MS,
|
|
609
|
+
keepAliveTimeout: 3e4,
|
|
610
|
+
keepAliveMaxTimeout: 6e4,
|
|
611
|
+
connections: getConnectionPoolSize()
|
|
612
|
+
});
|
|
613
|
+
cachedProxyAgents.set(proxyUrl, agent);
|
|
614
|
+
}
|
|
615
|
+
return cachedProxyAgents.get(proxyUrl);
|
|
616
|
+
}
|
|
617
|
+
async function fetchWithProxy(url, options = {}, abortSignal) {
|
|
618
|
+
let finalUrl = url;
|
|
619
|
+
let finalUrlString;
|
|
620
|
+
if (typeof url === "string") finalUrlString = url;
|
|
621
|
+
else if (url instanceof URL) finalUrlString = url.toString();
|
|
622
|
+
else if (url instanceof Request) finalUrlString = url.url;
|
|
623
|
+
if (!finalUrlString) throw new Error("Invalid URL");
|
|
624
|
+
const combinedSignal = abortSignal ? options.signal ? AbortSignal.any([options.signal, abortSignal]) : abortSignal : options.signal;
|
|
625
|
+
const finalOptions = {
|
|
626
|
+
...options,
|
|
627
|
+
headers: {
|
|
628
|
+
...options.headers,
|
|
629
|
+
"x-promptfoo-version": VERSION
|
|
630
|
+
},
|
|
631
|
+
signal: combinedSignal
|
|
632
|
+
};
|
|
633
|
+
if (typeof url === "string") try {
|
|
634
|
+
const parsedUrl = new URL(url);
|
|
635
|
+
if (parsedUrl.username || parsedUrl.password) {
|
|
636
|
+
if (finalOptions.headers && "Authorization" in finalOptions.headers) logger.warn("Both URL credentials and Authorization header present - URL credentials will be ignored");
|
|
637
|
+
else {
|
|
638
|
+
const username = parsedUrl.username || "";
|
|
639
|
+
const password = parsedUrl.password || "";
|
|
640
|
+
const credentials = Buffer.from(`${username}:${password}`).toString("base64");
|
|
641
|
+
finalOptions.headers = {
|
|
642
|
+
...finalOptions.headers,
|
|
643
|
+
Authorization: `Basic ${credentials}`
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
parsedUrl.username = "";
|
|
647
|
+
parsedUrl.password = "";
|
|
648
|
+
finalUrl = parsedUrl.toString();
|
|
649
|
+
finalUrlString = finalUrl.toString();
|
|
650
|
+
}
|
|
651
|
+
} catch (e) {
|
|
652
|
+
logger.debug(`URL parsing failed in fetchWithProxy: ${e}`);
|
|
653
|
+
}
|
|
654
|
+
const tlsOptions = { rejectUnauthorized: !getEnvBool("PROMPTFOO_INSECURE_SSL", true) };
|
|
655
|
+
const caCertPath = getEnvString("PROMPTFOO_CA_CERT_PATH");
|
|
656
|
+
if (caCertPath) try {
|
|
657
|
+
const resolvedPath = path.resolve(state.basePath || "", caCertPath);
|
|
658
|
+
tlsOptions.ca = await fsPromises.readFile(resolvedPath, "utf8");
|
|
659
|
+
logger.debug(`Using custom CA certificate from ${resolvedPath}`);
|
|
660
|
+
} catch (e) {
|
|
661
|
+
logger.warn(`Failed to read CA certificate from ${caCertPath}: ${e}`);
|
|
662
|
+
}
|
|
663
|
+
const proxyUrl = finalUrlString ? getProxyForUrl(finalUrlString) : "";
|
|
664
|
+
if (!finalOptions.dispatcher) if (proxyUrl) {
|
|
665
|
+
logger.debug(`Using proxy: ${sanitizeUrl(proxyUrl)}`);
|
|
666
|
+
finalOptions.dispatcher = getOrCreateProxyAgent(proxyUrl, tlsOptions);
|
|
667
|
+
} else finalOptions.dispatcher = getOrCreateAgent(tlsOptions);
|
|
668
|
+
const maxTransientRetries = options.disableTransientRetries ? 0 : 3;
|
|
669
|
+
for (let attempt = 0; attempt <= maxTransientRetries; attempt++) {
|
|
670
|
+
const response = await monkeyPatchFetch(finalUrl, finalOptions);
|
|
671
|
+
if (!options.disableTransientRetries && isTransientError(response) && attempt < maxTransientRetries) {
|
|
672
|
+
const backoffMs = Math.pow(2, attempt) * 1e3;
|
|
673
|
+
logger.debug(`Transient error (${response.status} ${response.statusText}), retry ${attempt + 1}/${maxTransientRetries} after ${backoffMs}ms`);
|
|
674
|
+
await sleep(backoffMs);
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
return response;
|
|
678
|
+
}
|
|
679
|
+
throw new Error("Unexpected end of transient retry loop");
|
|
680
|
+
}
|
|
681
|
+
function fetchWithTimeout(url, options = {}, timeout) {
|
|
682
|
+
return new Promise((resolve, reject) => {
|
|
683
|
+
const timeoutController = new AbortController();
|
|
684
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeoutController.signal]) : timeoutController.signal;
|
|
685
|
+
const timeoutId = setTimeout(() => {
|
|
686
|
+
timeoutController.abort();
|
|
687
|
+
reject(/* @__PURE__ */ new Error(`Request timed out after ${timeout} ms`));
|
|
688
|
+
}, timeout);
|
|
689
|
+
fetchWithProxy(url, {
|
|
690
|
+
...options,
|
|
691
|
+
signal
|
|
692
|
+
}).then((response) => {
|
|
693
|
+
clearTimeout(timeoutId);
|
|
694
|
+
resolve(response);
|
|
695
|
+
}).catch((error) => {
|
|
696
|
+
clearTimeout(timeoutId);
|
|
697
|
+
reject(error);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Check if a response indicates rate limiting
|
|
703
|
+
*/
|
|
704
|
+
function isRateLimited(response) {
|
|
705
|
+
invariant(response.headers, "Response headers are missing");
|
|
706
|
+
invariant(response.status, "Response status is missing");
|
|
707
|
+
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";
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Handle rate limiting by waiting the appropriate amount of time
|
|
711
|
+
*/
|
|
712
|
+
async function handleRateLimit(response) {
|
|
713
|
+
const rateLimitReset = response.headers.get("X-RateLimit-Reset");
|
|
714
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
715
|
+
const openaiReset = response.headers.get("x-ratelimit-reset-requests") || response.headers.get("x-ratelimit-reset-tokens");
|
|
716
|
+
let waitTime = 6e4;
|
|
717
|
+
if (openaiReset) waitTime = Math.max(Number.parseInt(openaiReset) * 1e3, 0);
|
|
718
|
+
else if (rateLimitReset) {
|
|
719
|
+
const resetTime = /* @__PURE__ */ new Date(Number.parseInt(rateLimitReset) * 1e3);
|
|
720
|
+
const now = /* @__PURE__ */ new Date();
|
|
721
|
+
waitTime = Math.max(resetTime.getTime() - now.getTime() + 1e3, 0);
|
|
722
|
+
} else if (retryAfter) waitTime = Number.parseInt(retryAfter) * 1e3;
|
|
723
|
+
logger.debug(`Rate limited, waiting ${waitTime}ms before retry`);
|
|
724
|
+
await sleep(waitTime);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Check if a response indicates a transient server error that should be retried.
|
|
728
|
+
* Matches specific status codes with their expected status text to avoid
|
|
729
|
+
* retrying permanent failures (e.g., some APIs return 502 for auth errors).
|
|
730
|
+
*/
|
|
731
|
+
function isTransientError(response) {
|
|
732
|
+
if (!response?.statusText) return false;
|
|
733
|
+
const statusText = response.statusText.toLowerCase();
|
|
734
|
+
switch (response.status) {
|
|
735
|
+
case 502: return statusText.includes("bad gateway");
|
|
736
|
+
case 503: return statusText.includes("service unavailable");
|
|
737
|
+
case 504: return statusText.includes("gateway timeout");
|
|
738
|
+
case 524: return statusText.includes("timeout");
|
|
739
|
+
default: return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async function fetchWithRetries(url, options = {}, timeout, maxRetries) {
|
|
743
|
+
maxRetries = Math.max(0, maxRetries ?? 4);
|
|
744
|
+
let lastErrorMessage;
|
|
745
|
+
const backoff = getEnvInt("PROMPTFOO_REQUEST_BACKOFF_MS", 5e3);
|
|
746
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
747
|
+
let response;
|
|
748
|
+
try {
|
|
749
|
+
response = await fetchWithTimeout(url, {
|
|
750
|
+
...options,
|
|
751
|
+
disableTransientRetries: true
|
|
752
|
+
}, timeout);
|
|
753
|
+
if (getEnvBool("PROMPTFOO_RETRY_5XX") && response.status >= 500 && response.status < 600) throw new Error(`Internal Server Error: ${response.status} ${response.statusText}`);
|
|
754
|
+
if (response && isRateLimited(response)) {
|
|
755
|
+
logger.debug(`Rate limited on URL ${url}: ${response.status} ${response.statusText}, attempt ${i + 1}/${maxRetries + 1}, waiting before retry...`);
|
|
756
|
+
lastErrorMessage = `Rate limited: ${response.status} ${response.statusText}`;
|
|
757
|
+
await handleRateLimit(response);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
return response;
|
|
761
|
+
} catch (error) {
|
|
762
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
763
|
+
let errorMessage;
|
|
764
|
+
if (error instanceof Error) {
|
|
765
|
+
const typedError = error;
|
|
766
|
+
errorMessage = `${typedError.name}: ${typedError.message}`;
|
|
767
|
+
if (typedError.cause) errorMessage += ` (Cause: ${typedError.cause})`;
|
|
768
|
+
if (typedError.code) errorMessage += ` (Code: ${typedError.code})`;
|
|
769
|
+
} else errorMessage = String(error);
|
|
770
|
+
logger.debug(`Request to ${url} failed (attempt #${i + 1}), retrying: ${errorMessage}`);
|
|
771
|
+
if (i < maxRetries) await sleep(Math.pow(2, i) * (backoff + 1e3 * Math.random()));
|
|
772
|
+
lastErrorMessage = errorMessage;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
throw new Error(`Request failed after ${maxRetries} retries: ${lastErrorMessage}`);
|
|
776
|
+
}
|
|
777
|
+
//#endregion
|
|
778
|
+
export { getDefaultShareViewBaseUrl as A, transformTools as C, R_ENDPOINT as D, EVENTS_ENDPOINT as E, FILE_METADATA_KEY as F, HUMAN_ASSERTION_TYPE as I, getShareViewBaseUrl as M, POSTHOG_KEY as N, TERMINAL_MAX_WIDTH as O, VERSION as P, transformToolChoice as S, CONSENT_ENDPOINT as T, isPromptfooSampleTarget as _, CloudConfig as a, parseChatPrompt as b, writeGlobalConfig as c, sleep as d, LONG_RUNNING_MODEL_TIMEOUT_MS as f, isOpenAIToolChoice as g, isOpenAIToolArray as h, fetch_exports as i, getShareApiBaseUrl as j, getDefaultPort as k, writeGlobalConfigPartial as l, calculateCost as m, fetchWithRetries as n, cloudConfig as o, REQUEST_TIMEOUT_MS as p, fetchWithTimeout as r, readGlobalConfig as s, fetchWithProxy as t, getCurrentTimestamp as u, openaiToolChoiceToBedrock as v, CLOUD_PROVIDER_PREFIX as w, toTitleCase as x, openaiToolsToBedrock as y };
|
|
779
|
+
|
|
780
|
+
//# sourceMappingURL=fetch-HaqdX7U1.js.map
|