promptfoo 0.121.3 → 0.121.5
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-Du7YVwj5.js → ListApp-BRUsT43Y.js} +1 -1
- package/dist/src/{accounts-BPyfpSeU.cjs → accounts-BIFntVWB.cjs} +5 -5
- package/dist/src/{accounts-CFLK3mnD.js → accounts-CLJHCDDb.js} +6 -6
- package/dist/src/{accounts-B2XmGjty.js → accounts-CaLNYnf7.js} +5 -5
- package/dist/src/{accounts-Xatc0RYb.js → accounts-bnyHT7Ju.js} +5 -5
- package/dist/src/{agentic-utils-36epdqwB.js → agentic-utils-B5krlibj.js} +3 -3
- package/dist/src/{agentic-utils-DIYAAYE7.js → agentic-utils-Ba67xmgs.js} +3 -3
- package/dist/src/{agentic-utils-D8yXo5Lm.js → agentic-utils-BclbiXiq.js} +4 -4
- package/dist/src/{agentic-utils-DAVsChuB.cjs → agentic-utils-D2x0wGhB.cjs} +3 -3
- package/dist/src/{agents-CLQ-P15P.js → agents-BGqaTDnr.js} +5 -7
- package/dist/src/{agents-wg3ohknq.js → agents-BV9yFpXX.js} +6 -7
- package/dist/src/{agents-CgBniSlI.js → agents-BYdMl1UE.js} +5 -9
- package/dist/src/{agents-Bqgfdokm.js → agents-DhxWMCtH.js} +35 -14
- package/dist/src/{agents-BBWxKSM0.cjs → agents-DiWmQYH9.cjs} +5 -7
- package/dist/src/{agents-CAYbM7qD.cjs → agents-WULPVjbH.cjs} +34 -12
- package/dist/src/{agents-DSSTV4bv.js → agents-emVcx3yh.js} +35 -13
- package/dist/src/{agents-BBVJCIYr.js → agents-n6vPqV3i.js} +35 -13
- package/dist/src/{aimlapi-BwGC1TtS.js → aimlapi-BxqK9HF_.js} +8 -14
- package/dist/src/{aimlapi-Bv8Fmc-b.cjs → aimlapi-BzLjZI_m.cjs} +8 -15
- package/dist/src/{aimlapi-MgSLdvy7.js → aimlapi-DR4pgeiC.js} +7 -14
- package/dist/src/{aimlapi-DaC3qZ-o.js → aimlapi-uPGp0Zdo.js} +7 -16
- package/dist/src/app/app/tsconfig.app.tsbuildinfo +1 -0
- package/dist/src/app/assets/Report-vjzrbgce.js +1 -0
- package/dist/src/app/assets/index-B3NQ8HTd.js +385 -0
- package/dist/src/app/assets/index-Cli2yAXv.css +1 -0
- package/dist/src/app/assets/rolldown-runtime-COnpUsM8.js +1 -0
- package/dist/src/app/assets/scroll-timeline-D9IT_e8Z.js +1 -0
- package/dist/src/app/assets/sync-IjzpWrOE.js +4 -0
- package/dist/src/app/assets/vendor-charts-BNdH8TCw.js +36 -0
- package/dist/src/app/assets/vendor-markdown-Ch00wnNI.js +29 -0
- package/dist/src/app/assets/vendor-react-CVvmk1UB.js +9 -0
- package/dist/src/app/assets/vendor-utils-BnEYbx2Q.js +37 -0
- package/dist/src/app/index.html +32 -7
- package/dist/src/{audio-Bn44pQxv.js → audio-BvpTOArF.js} +4 -4
- package/dist/src/{audio-DVFjQ67_.cjs → audio-C0vDeS0j.cjs} +4 -4
- package/dist/src/{audio-DjU9GswO.js → audio-CScmnmEB.js} +4 -5
- package/dist/src/{audio-DDA5WHdx.js → audio-Da8U9IS5.js} +4 -4
- package/dist/src/{base-CKjwebIH.js → base-BOMaNEes.js} +3 -3
- package/dist/src/{base-CqzQ4K8j.js → base-BTux96b1.js} +3 -3
- package/dist/src/{base-BboXIF_0.cjs → base-Tw6uhH8K.cjs} +3 -3
- package/dist/src/{base-Cz2ZC_iA.js → base-dYsl2hmL.js} +3 -3
- package/dist/src/{blobs-C6j0bvFz.cjs → blobs-B95F_7vE.cjs} +3 -3
- package/dist/src/{blobs-DXTl6J3H.js → blobs-BW4U31ue.js} +3 -3
- package/dist/src/{blobs-BUWmKWzo.js → blobs-D_gg8nbm.js} +4 -4
- package/dist/src/{blobs-B1JriOyi.js → blobs-DjLby-uP.js} +4 -4
- package/dist/src/cache-BI5BY7ey.js +280 -0
- package/dist/src/cache-BRkhlH3k.cjs +3 -0
- package/dist/src/cache-BlC6aeJ0.js +3 -0
- package/dist/src/cache-Bzttsk0X.js +310 -0
- package/dist/src/cache-Cr-qWIbP.js +310 -0
- package/dist/src/cache-DGg-yTZG.cjs +376 -0
- package/dist/src/{chat-BEwdgGEg.js → chat-BLOdH60v.js} +63 -37
- package/dist/src/{chat-B0iaWhoh.js → chat-Cx_LkwvZ.js} +63 -37
- package/dist/src/{chat-DK1U-eZ-.js → chat-D9nudO9b.js} +5 -14
- package/dist/src/{chat-pxmiVpWe.js → chat-DChSH_Es.js} +63 -37
- package/dist/src/{chat-B-52XYI1.js → chat-DG2LkwLq.js} +3 -13
- package/dist/src/{chat-BtIKkLKx.cjs → chat-DH97tVV9.cjs} +3 -13
- package/dist/src/{chat-BE0qTA8e.js → chat-aMQZw6R7.js} +4 -16
- package/dist/src/{chat-CM8qWR3_.cjs → chat-vYqqv1gP.cjs} +64 -38
- package/dist/src/{chatkit-_8eJqKcD.js → chatkit-B8X34dQc.js} +4 -4
- package/dist/src/{chatkit-BYGQlHlV.js → chatkit-BXu42Qwt.js} +4 -4
- package/dist/src/{chatkit-a2D6mY6s.js → chatkit-CbMRoeYw.js} +4 -4
- package/dist/src/{chatkit-Cx174XI3.cjs → chatkit-D44VyUyB.cjs} +4 -4
- package/dist/src/{claude-agent-sdk-8ddRp1L2.cjs → claude-agent-sdk-BRq0bbIK.cjs} +23 -18
- package/dist/src/{claude-agent-sdk-CMjh4LFH.js → claude-agent-sdk-BjriSVRZ.js} +20 -15
- package/dist/src/{claude-agent-sdk-HgbFioFw.js → claude-agent-sdk-BzNZeZ0N.js} +20 -15
- package/dist/src/{claude-agent-sdk-Bq5EArsX.js → claude-agent-sdk-DYv_AJ8u.js} +21 -17
- package/dist/src/cloud-CoD5OacT.js +3 -0
- package/dist/src/{cloud-z8KZpUoa.js → cloud-Da0bofJd.js} +25 -13
- package/dist/src/{cloudflare-ai-Bbp26N0L.js → cloudflare-ai-CXC4b1EU.js} +5 -14
- package/dist/src/{cloudflare-ai-BGyXlpXJ.js → cloudflare-ai-CyBoIs1Q.js} +7 -15
- package/dist/src/{cloudflare-ai-DdKP9TKT.js → cloudflare-ai-DGOwgexC.js} +6 -17
- package/dist/src/{cloudflare-ai-C62x6MQG.cjs → cloudflare-ai-DJv5qnyb.cjs} +6 -15
- package/dist/src/{cloudflare-gateway-DXhtXDRb.js → cloudflare-gateway-1sAoOyft.js} +6 -16
- package/dist/src/{cloudflare-gateway-D-e9i1Sn.js → cloudflare-gateway-D-dnkzCF.js} +5 -18
- package/dist/src/{cloudflare-gateway-Dx36ftqF.cjs → cloudflare-gateway-DKVjkDav.cjs} +4 -15
- package/dist/src/{cloudflare-gateway-BwAaUgeW.js → cloudflare-gateway-TJkVrZlB.js} +4 -15
- package/dist/src/codex-app-server-CCLjqCh9.js +1915 -0
- package/dist/src/codex-app-server-CCe0TiDc.js +1915 -0
- package/dist/src/codex-app-server-CPW1LFwh.js +1916 -0
- package/dist/src/codex-app-server-VMRnjZ68.cjs +1920 -0
- package/dist/src/codex-sdk-1jm_qPHf.js +3 -0
- package/dist/src/codex-sdk-Bd8UbO9q.cjs +1172 -0
- package/dist/src/codex-sdk-BgEFQ70r.js +1164 -0
- package/dist/src/codex-sdk-Bzb_TqX9.js +1165 -0
- package/dist/src/codex-sdk-Danroptg.cjs +2 -0
- package/dist/src/codex-sdk-DfvDTN33.js +1165 -0
- package/dist/src/{cometapi-BDyV-NNm.js → cometapi-B5ImDlSm.js} +9 -15
- package/dist/src/{cometapi-C3hOlM7-.cjs → cometapi-BgAkuYCw.cjs} +9 -16
- package/dist/src/{cometapi-hhL4TAh3.js → cometapi-CC7hWxmX.js} +8 -15
- package/dist/src/{cometapi-sp7sJpBD.js → cometapi-CCbpHkuF.js} +8 -17
- package/dist/src/{completion-DoYy49ti.js → completion-2iuYVxwi.js} +8 -57
- package/dist/src/{completion-BCimtq-h.js → completion-CrD6MQ93.js} +8 -57
- package/dist/src/{completion-DlXUhj5c.cjs → completion-DtQ72Bm3.cjs} +7 -62
- package/dist/src/{completion-DCjv7RZ3.js → completion-Vq_ad618.js} +8 -57
- package/dist/src/{createHash-CTQmL3G2.js → createHash-4gFQpDDv.js} +3 -3
- package/dist/src/{createHash-Da8fMwqB.js → createHash-DPpsZgFF.js} +3 -3
- package/dist/src/{createHash-DmPQkvBh.js → createHash-Un4Q_huE.js} +3 -3
- package/dist/src/{createHash-BYwImsYv.cjs → createHash-VvBIc-AW.cjs} +4 -4
- package/dist/src/{docker-CxCkwMzc.js → docker--3qzPa-6.js} +6 -14
- package/dist/src/{docker-Cqj2-QVi.cjs → docker-D3AY-5F5.cjs} +7 -15
- package/dist/src/{docker-FeBni2dw.js → docker-DCsCDvwM.js} +7 -14
- package/dist/src/{docker-DpguQj-w.js → docker-Dorv4_Dg.js} +6 -16
- package/dist/src/embedding-BXhN5lCH.cjs +63 -0
- package/dist/src/embedding-ChS1ivFS.js +58 -0
- package/dist/src/embedding-DNRvZwRN.js +59 -0
- package/dist/src/embedding-D_bI4NDq.js +58 -0
- package/dist/src/entrypoint.js +69 -6
- package/dist/src/{errors-P6ll7XSJ.js → errors-DFHe4L-n.js} +1 -1
- package/dist/src/{esm-SUNIX1x3.js → esm-B6whoAcf.js} +15 -6
- package/dist/src/{esm-CKWP3u_P.js → esm-BRkfNsYs.js} +16 -7
- package/dist/src/{esm-7UIl0pPM.js → esm-BX8fwlAO.js} +27 -18
- package/dist/src/{esm-CipptfDu.cjs → esm-B_rGuPTo.cjs} +15 -6
- package/dist/src/eval-BQPLBJbw.js +3 -0
- package/dist/src/{eval-BTqTn7lb.js → eval-DJ_4A-tr.js} +50 -21
- package/dist/src/evalResult-BBJAHAtw.cjs +2 -0
- package/dist/src/evalResult-BBK58h2B.js +3 -0
- package/dist/src/{evalResult-DpARzUCb.cjs → evalResult-Cx-8OWkb.cjs} +29 -11
- package/dist/src/{evalResult-DUDShQrm.js → evalResult-D6P5I5il.js} +29 -11
- package/dist/src/{evalResult-BkIhRdTe.js → evalResult-pSvGWFMo.js} +29 -11
- package/dist/src/evalResult-spPqh1G_.js +2 -0
- package/dist/src/{evaluator-BcvOGaam.js → evaluator-D-UIbbYq.js} +3975 -2152
- package/dist/src/evaluator-DgLKaZk8.js +3 -0
- package/dist/src/{extractor-D_wd8jxt.js → extractor-BM3jRERL.js} +6 -6
- package/dist/src/{extractor-DG3sSfXE.cjs → extractor-Dxr2J_wK.cjs} +6 -6
- package/dist/src/{extractor-CAZ2G3Kh.js → extractor-DxyiFhPk.js} +6 -6
- package/dist/src/{extractor-C8XwivI9.js → extractor-YlZbUMsL.js} +6 -6
- package/dist/src/fetch-8viavNv8.js +3 -0
- package/dist/src/{fetch-DoVRJZhJ.js → fetch-B6ch2nU2.js} +199 -60
- package/dist/src/{fetch-CVAtKnI3.js → fetch-D9xxyC1p.js} +404 -252
- package/dist/src/{fetch-BnR9wSnm.cjs → fetch-NuqXW1Xb.cjs} +415 -263
- package/dist/src/{fetch-BiYv2BZc.js → fetch-Y5qX_kST.js} +222 -70
- package/dist/src/{fileExtensions-LcDYkU4v.js → fileExtensions-8CjoL7vB.js} +1 -1
- package/dist/src/{fileExtensions-DnqA1y9x.js → fileExtensions-BGh-W-HT.js} +1 -1
- package/dist/src/{fileExtensions-bYh77CN8.cjs → fileExtensions-D9h-8Wxg.cjs} +1 -1
- package/dist/src/{fileExtensions-Ds-foDzt.js → fileExtensions-DysCsxNG.js} +1 -1
- package/dist/src/{formatDuration-DgBVMN65.js → formatDuration-Ch4A7G3o.js} +1 -1
- package/dist/src/{genaiTracer-BfxrvSUb.cjs → genaiTracer-BokHC-MW.cjs} +7 -3
- package/dist/src/{genaiTracer-C1rxGO8Q.js → genaiTracer-C3ZPQU60.js} +6 -2
- package/dist/src/{genaiTracer-70Z8BIuV.js → genaiTracer-CFny3gOy.js} +6 -2
- package/dist/src/{genaiTracer-D3fD9dNV.js → genaiTracer-DxODqT9e.js} +6 -2
- package/dist/src/golang/wrapper.go +1 -1
- package/dist/src/{graders-DG7mhg-b.js → graders-BoUqsCEm.js} +7402 -5699
- package/dist/src/{graders-BElhu9ZY.cjs → graders-Bw1wk_21.cjs} +5220 -3437
- package/dist/src/graders-C84JI-m5.js +2 -0
- package/dist/src/graders-CBbd0K0Q.cjs +2 -0
- package/dist/src/graders-CbQqpHSN.js +3 -0
- package/dist/src/{graders-RjHF8VfG.js → graders-CgPn32yp.js} +7400 -5697
- package/dist/src/{graders-BXAJ0sbS.js → graders-CwrbifOo.js} +6136 -4433
- package/dist/src/graders-DS42d3ZG.js +2 -0
- package/dist/src/{image-6WQXK8m8.js → image-BeWaInPF.js} +4 -4
- package/dist/src/{image-PoF6DN3x.js → image-BmilRNqO.js} +8 -8
- package/dist/src/{image--F58eEIn.cjs → image-CxJoa3aW.cjs} +8 -8
- package/dist/src/{image-fza3zuKs.cjs → image-D10dNAav.cjs} +4 -4
- package/dist/src/{image-DO0RYnjH.js → image-Dr_3I3nK.js} +4 -5
- package/dist/src/{image-B8b6f36E.js → image-DsGRlkh7.js} +8 -8
- package/dist/src/{image-CoxZp9PZ.js → image-a_SGUobh.js} +8 -8
- package/dist/src/{image-xNbw5ph2.js → image-qjO6FWPs.js} +4 -4
- package/dist/src/index.cjs +4835 -2582
- package/dist/src/index.d.cts +2782 -31
- package/dist/src/index.d.ts +2783 -32
- package/dist/src/index.js +4817 -2564
- package/dist/src/{interactiveCheck-BnMYOjMu.js → interactiveCheck-CCICw2cy.js} +2 -2
- package/dist/src/{invariant-BtWWVVhl.js → invariant-B2Rf6avk.js} +1 -1
- package/dist/src/{invariant-vgHWClmd.js → invariant-DIYf9sP1.js} +1 -1
- package/dist/src/{knowledgeBase-Bi7CmDbx.js → knowledgeBase-BBETc5-S.js} +6 -8
- package/dist/src/{knowledgeBase-DqrLX8fy.cjs → knowledgeBase-C8qOo26M.cjs} +6 -8
- package/dist/src/{knowledgeBase-DFRXPZl_.js → knowledgeBase-CzAi2rUI.js} +7 -8
- package/dist/src/{knowledgeBase-Ce3ofVan.js → knowledgeBase-Dr3Kib7F.js} +6 -10
- package/dist/src/{litellm-CKiAxnoM.js → litellm-BLSiANhk.js} +6 -14
- package/dist/src/{litellm-CnHI69aj.cjs → litellm-CaUmV7Mk.cjs} +6 -15
- package/dist/src/{litellm-Tc294Jhj.js → litellm-DQGo_juI.js} +5 -14
- package/dist/src/{litellm-Bo2gQXpo.js → litellm-DRc4qWfc.js} +5 -16
- package/dist/src/{logger-BcJBzSSA.js → logger-BbY6ypFL.js} +41 -12
- package/dist/src/{logger-D5iKBpu_.cjs → logger-COuQb2xB.cjs} +51 -10
- package/dist/src/{logger-DO8_zM18.js → logger-Ct2S6Yx-.js} +40 -11
- package/dist/src/{logger-BnkjG2jt.js → logger-KD8JjCRJ.js} +41 -12
- package/dist/src/{luma-ray-C9q8rdQe.js → luma-ray-B-tNZzqW.js} +6 -10
- package/dist/src/{luma-ray-DP0QA9qn.js → luma-ray-CtS3OlGq.js} +6 -10
- package/dist/src/{luma-ray-0ehMPt5N.js → luma-ray-PJJgUjOc.js} +6 -11
- package/dist/src/{luma-ray-m9Ku2meV.cjs → luma-ray-if-Ml4R9.cjs} +6 -10
- package/dist/src/main.d.ts +1 -26
- package/dist/src/main.js +1188 -679
- package/dist/src/messages-B9dSjrNf.js +544 -0
- package/dist/src/messages-BnsVHUnm.cjs +558 -0
- package/dist/src/messages-CI69Lasb.js +543 -0
- package/dist/src/messages-CewuNcNS.js +543 -0
- package/dist/src/{meteor-DLZZ3osF.cjs → meteor-BBGcGeCa.cjs} +1 -1
- package/dist/src/{meteor-DUiCJRC-.js → meteor-BKTM-7KS.js} +1 -1
- package/dist/src/{meteor-44VjEACX.js → meteor-CeGo0Lu2.js} +2 -2
- package/dist/src/{meteor-D-SotUw9.js → meteor-Wc_aUVvu.js} +2 -2
- package/dist/src/{modelslab-B5J-ZM5c.js → modelslab-BCLOtfek.js} +8 -10
- package/dist/src/{modelslab-IQbNg-r7.cjs → modelslab-BkapYJhh.cjs} +7 -10
- package/dist/src/{modelslab-BTOT8FUO.js → modelslab-D73OnKSx.js} +7 -10
- package/dist/src/{modelslab-BI458moT.js → modelslab-zpz9JcK0.js} +7 -12
- package/dist/src/{nova-reel-BZ9y-Y5s.js → nova-reel-B8F_TK5w.js} +7 -10
- package/dist/src/{nova-reel-Xw1SXLpg.js → nova-reel-Bx0NFV2f.js} +6 -10
- package/dist/src/{nova-reel-DEeQlnOJ.js → nova-reel-CNGJTLtG.js} +6 -12
- package/dist/src/{nova-reel-CE5etkv9.cjs → nova-reel-DkT7tnoB.cjs} +6 -10
- package/dist/src/{nova-sonic-Ogqf-csn.js → nova-sonic-BaXRN1cr.js} +5 -7
- package/dist/src/{nova-sonic-DXTLpi-r.js → nova-sonic-BeTRaFOh.js} +4 -7
- package/dist/src/{nova-sonic-DWswpN1E.js → nova-sonic-CL7Zqv0G.js} +4 -9
- package/dist/src/{nova-sonic-N0yCm0vb.cjs → nova-sonic-YT426juD.cjs} +4 -7
- package/dist/src/{openai-BcB5KlTk.js → openai-BMHD2Huo.js} +6 -3
- package/dist/src/{openai-BMcwgD5C.js → openai-BT-JvDse.js} +6 -3
- package/dist/src/{openai-CoxGAQwn.cjs → openai-Cy1XLs0c.cjs} +6 -3
- package/dist/src/{openai-D6wITiVn.js → openai-D4fxGvRx.js} +6 -3
- package/dist/src/openclaw-Bq7RVR3k.js +1200 -0
- package/dist/src/openclaw-DA8U4DsD.js +1201 -0
- package/dist/src/openclaw-DObVgpjC.js +1200 -0
- package/dist/src/openclaw-DUBZP3GL.cjs +1206 -0
- package/dist/src/{opencode-sdk-CHCs7dEb.js → opencode-sdk-BB40Wir1.js} +6 -8
- package/dist/src/{opencode-sdk-DDxj4QqH.js → opencode-sdk-BM1UAIv1.js} +6 -8
- package/dist/src/{opencode-sdk-WWJhnbKr.cjs → opencode-sdk-CeqiOcOU.cjs} +7 -9
- package/dist/src/{opencode-sdk-C71Z0ehR.js → opencode-sdk-ChdK7F7z.js} +6 -9
- package/dist/src/{otlpReceiver-CZL48YfC.js → otlpReceiver-C6thJRXi.js} +154 -98
- package/dist/src/{otlpReceiver-C9KlUtxh.js → otlpReceiver-CcdIikOu.js} +154 -98
- package/dist/src/{otlpReceiver-DHKqJlsz.cjs → otlpReceiver-DNSQj6bf.cjs} +154 -98
- package/dist/src/{otlpReceiver-CavGAA6k.js → otlpReceiver-UYMQx3sy.js} +154 -98
- package/dist/src/{providerRegistry-BkzVH5Ba.js → providerRegistry-1gB5vtzQ.js} +2 -2
- package/dist/src/{providerRegistry-BTDgfV5h.cjs → providerRegistry-BESeALrr.cjs} +2 -2
- package/dist/src/{providerRegistry-CUWki5mQ.js → providerRegistry-DoACwqhD.js} +2 -2
- package/dist/src/{providerRegistry-B9lh-_tx.js → providerRegistry-PMsleEzs.js} +2 -2
- package/dist/src/providers-BuyzKt7C.js +2 -0
- package/dist/src/providers-C7lNVBjX.cjs +3 -0
- package/dist/src/providers-CCE2COJi2.js +2 -0
- package/dist/src/{providers-Cn73d5sr.js → providers-CJh7iriU.js} +17180 -16823
- package/dist/src/providers-Ctcc592x.js +3 -0
- package/dist/src/{providers-DvddrgxL.js → providers-DRrerKra.js} +1052 -695
- package/dist/src/{providers-Ch6Mr0gn.js → providers-DT-GtF2t.js} +16716 -16359
- package/dist/src/{providers-CScd1wN6.cjs → providers-eDShy16E.cjs} +19893 -19506
- package/dist/src/python/persistent_wrapper.py +0 -5
- package/dist/src/{pythonUtils-Cpo0Ez1p.js → pythonUtils-C4tltmIn.js} +4 -4
- package/dist/src/{pythonUtils-dAVigVK-.cjs → pythonUtils-CoLaCwNY.cjs} +4 -4
- package/dist/src/{pythonUtils-Bzwbgpbg.js → pythonUtils-DMO68Jg7.js} +3 -3
- package/dist/src/{pythonUtils-wIqk7zAf.js → pythonUtils-DNqbnRdx.js} +3 -3
- package/dist/src/{quiverai-BeofbLVc.js → quiverai-BSS9a7wV.js} +4 -4
- package/dist/src/{quiverai-DVSEqJiq.js → quiverai-Bk1KrvL6.js} +4 -4
- package/dist/src/{quiverai-CcUhPIBg.cjs → quiverai-Bpx6MZ7T.cjs} +4 -4
- package/dist/src/{quiverai-CCQn73lq.js → quiverai-CPKhWgaT.js} +4 -5
- package/dist/src/render-7uNJ2V14.js +135 -0
- package/dist/src/render-DlscvAUJ.js +135 -0
- package/dist/src/render-eui5p5mL.js +136 -0
- package/dist/src/{render-BHl6QVq9.js → render-nj-UaPdn.js} +2 -3
- package/dist/src/render-tG6ir9_g.cjs +165 -0
- package/dist/src/{responses-CgNyTPsY.js → responses-1ztiVYsx.js} +56 -17
- package/dist/src/{responses-BKP_WYis.js → responses-B8haB-mD.js} +56 -17
- package/dist/src/{responses-CQb1Tj69.js → responses-BiaBguAu.js} +56 -17
- package/dist/src/{responses-mo0KQDbu.cjs → responses-CF-ayauu.cjs} +56 -17
- package/dist/src/rubyUtils-4hjGxvju.js +3 -0
- package/dist/src/{rubyUtils-DECSbsfY.js → rubyUtils-BI0p46eZ.js} +3 -3
- package/dist/src/{rubyUtils-CiVfln3g.js → rubyUtils-CIQFnVz4.js} +3 -3
- package/dist/src/rubyUtils-CO-tuszQ.cjs +2 -0
- package/dist/src/{rubyUtils-PgU-gHmx.js → rubyUtils-DGnoCYL2.js} +4 -4
- package/dist/src/{rubyUtils-CGeUtCfW.cjs → rubyUtils-DoifqkiA.cjs} +5 -4
- package/dist/src/{sagemaker-CqeASYE5.js → sagemaker-BDLeW29y.js} +14 -18
- package/dist/src/{sagemaker-MUbD5V3v.js → sagemaker-C5T60MKf.js} +14 -19
- package/dist/src/{sagemaker-CVv8W7so.js → sagemaker-ClS_NB07.js} +14 -18
- package/dist/src/{sagemaker-jiw1wQa-.cjs → sagemaker-ljtY12VM.cjs} +14 -18
- package/dist/src/{scanner-DVDeUz1r.js → scanner-nOCWNIXa.js} +130 -35
- package/dist/src/server/golang/wrapper.go +1 -1
- package/dist/src/server/index.js +4829 -2529
- package/dist/src/server/python/persistent_wrapper.py +0 -5
- package/dist/src/{server-BtoCXeXI.cjs → server-BEECpeGG.cjs} +140 -6
- package/dist/src/{server-DZ9MtCn0.js → server-ByiF3qlg.js} +129 -9
- package/dist/src/{server-Cns05F1j.js → server-ByxbqAcQ.js} +128 -8
- package/dist/src/server-C0XKRNB_.cjs +2 -0
- package/dist/src/server-C_15p79-.js +3 -0
- package/dist/src/{server-CP9qKM40.js → server-gyd6d4Hc.js} +126 -7
- package/dist/src/{signal-C3ZTsUgi.js → signal-DTtUuU3l.js} +3 -3
- package/dist/src/{slack-DCEV-vWP.js → slack-4zZX1OKP.js} +2 -2
- package/dist/src/{slack-94iG3T0s.cjs → slack-BLlsDpfG.cjs} +2 -2
- package/dist/src/{slack-BR0HtO3K.js → slack-BPYLQLgb.js} +2 -2
- package/dist/src/{slack-2sdpGzbt.js → slack-Bamy_7te.js} +2 -2
- package/dist/src/{store-CLyU7AtI.cjs → store-2K0kDi80.cjs} +3 -3
- package/dist/src/{store-VB0GP46K.js → store-2OXm_eBY.js} +3 -3
- package/dist/src/store-BELqNwvz.js +3 -0
- package/dist/src/{store-Cj258DgL.js → store-BPkzEyFM.js} +3 -3
- package/dist/src/{store-P8OKm19S.js → store-CPh25336.js} +3 -3
- package/dist/src/store-uQZ4AjPe.cjs +2 -0
- package/dist/src/{tables-BEIFz2tM.js → tables-BMSOS2Gg.js} +3 -3
- package/dist/src/{tables-BdZQEpRz.cjs → tables-CXbaZ9y1.cjs} +3 -3
- package/dist/src/{tables-DmzvLbeZ.js → tables-NlvH23ky.js} +3 -3
- package/dist/src/{tables-kC7R5kiK.js → tables-WgdUZ8Ck.js} +3 -3
- package/dist/src/{telemetry-DPXLd7UE.js → telemetry--iqaGyaS.js} +5 -4
- package/dist/src/{telemetry-re627Lre.cjs → telemetry-CEQxGnMZ.cjs} +8 -7
- package/dist/src/{telemetry-BugWqKiu.js → telemetry-CgdVGV8N.js} +5 -4
- package/dist/src/{telemetry-BnH5VJAU.js → telemetry-DWdGHvEf.js} +5 -4
- package/dist/src/telemetry-DjNoC_n3.cjs +2 -0
- package/dist/src/telemetry-ZdPZc0fm.js +3 -0
- package/dist/src/{text-CW1cyrwj.cjs → text-BiNME7QG.cjs} +1 -1
- package/dist/src/{text-Db-Wt2u2.js → text-D4lz-Jg_.js} +1 -1
- package/dist/src/{text-TIv0QYnd.js → text-DDQP0tuQ.js} +1 -1
- package/dist/src/{text-B_UCRPp2.js → text-NWvfMfkF.js} +1 -1
- package/dist/src/{tokenUsageUtils-DflFMjS0.js → tokenUsageUtils-2wIvAhB3.js} +7 -3
- package/dist/src/{tokenUsageUtils-BDGe-iyI.js → tokenUsageUtils-4c780gFd.js} +7 -3
- package/dist/src/{tokenUsageUtils-NYT-WKS6.js → tokenUsageUtils-BjVkdk18.js} +7 -3
- package/dist/src/{tokenUsageUtils-bVa1ga6f.cjs → tokenUsageUtils-C9odhsbW.cjs} +7 -3
- package/dist/src/{transcription-CaMivnjG.js → transcription-84t4ALo2.js} +7 -11
- package/dist/src/{transcription-Hb3VnC4M.js → transcription-Bm2emLmJ.js} +8 -11
- package/dist/src/{transcription-BvtsrzRG.cjs → transcription-CZ4LG5hQ.cjs} +9 -13
- package/dist/src/{transcription-DOMMTu01.js → transcription-D7Q0vJsh.js} +7 -13
- package/dist/src/{transform-DrleutM3.js → transform-B-b6Cq-q.js} +8 -6
- package/dist/src/transform-BQt0BeAW.js +3 -0
- package/dist/src/transform-Bq5oqC0s.cjs +2 -0
- package/dist/src/{transform-ZrG2dvlo.cjs → transform-C9izGX54.cjs} +5 -5
- package/dist/src/{transform-BzK09Q_9.js → transform-CwbAZ84V.js} +5 -5
- package/dist/src/{transform-0BwoBsvO.cjs → transform-Dg4LcO1Y.cjs} +18 -10
- package/dist/src/{transform-B2-jIv68.js → transform-DtooZqYY.js} +8 -6
- package/dist/src/{transform-ljLYHEPh.js → transform-DzCF-wqV.js} +5 -5
- package/dist/src/{transform-DyDAwEpE.js → transform-_DpNB4qp.js} +9 -7
- package/dist/src/{transform-BqPkNPYm.js → transform-eGiUAv86.js} +5 -5
- package/dist/src/{transformersAvailability-DKoRtQLy.cjs → transformersAvailability-B22swDxr.cjs} +1 -1
- package/dist/src/{transformersAvailability-BGkzavwb.js → transformersAvailability-lvCCvuPT.js} +1 -1
- package/dist/src/{transformersAvailability-D6c6ROpT.js → transformersAvailability-rJGPccjr.js} +1 -1
- package/dist/src/{types-Cd3ygw8W.js → types-BDjGOq4E.js} +354 -24
- package/dist/src/{types-CIhFeUC4.js → types-BVH9hjgW.js} +364 -23
- package/dist/src/{types-D8cGDZbL.cjs → types-CgG2rKiW.cjs} +534 -167
- package/dist/src/{types-q8GXGF65.js → types-DNRZVOue.js} +498 -167
- package/dist/src/{util-BLvy9qfE.js → util-3pBZZb_H.js} +151 -149
- package/dist/src/{util-CFj4YKIn.cjs → util-A5_ZsQUn.cjs} +66 -44
- package/dist/src/{util-BtoGs5Cb.js → util-B9CNhyac.js} +66 -44
- package/dist/src/{util-Bm3E9jpK.js → util-BQOCAHQC.js} +692 -690
- package/dist/src/{util-vNmDL5DT.js → util-BVXcTwXu.js} +138 -36
- package/dist/src/{util-CgDCK4KI.js → util-BlFVL0UF.js} +66 -44
- package/dist/src/{util-DM2rTn_6.js → util-C-kmRosx.js} +66 -44
- package/dist/src/{util-DbVG-yZU.js → util-DFPeFkiV.js} +138 -36
- package/dist/src/{util-DMFeUvLz.js → util-DN0-b81k.js} +138 -36
- package/dist/src/{util--9u9UVCt.cjs → util-Dpmm_dAI.cjs} +143 -35
- package/dist/src/{util-CMMkIxfU.js → util-Dub0f_ej.js} +693 -691
- package/dist/src/{util-CuLo2pMR.cjs → util-DvpHnLt0.cjs} +714 -719
- package/dist/src/{utils-DOjD4dTC.js → utils-BUMN8orw.js} +6 -4
- package/dist/src/{utils-DKw8mrgr.cjs → utils-DkVeShIB.cjs} +6 -4
- package/dist/src/{utils-DEuL4VNB.js → utils-kt7lv30R.js} +6 -4
- package/dist/src/{utils-CFxO9KGo.js → utils-o8S5huU2.js} +6 -4
- package/dist/src/version-0frU0UTr.js +16 -0
- package/dist/src/version-CbpiUINz.js +17 -0
- package/dist/src/version-CbuBKu2U.js +16 -0
- package/dist/src/version-D9zu9FWB.cjs +27 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +57 -46
- package/dist/src/app/assets/index-B6l9CVVb.js +0 -439
- package/dist/src/app/assets/index-DyZ0Ep37.css +0 -1
- package/dist/src/app/assets/scroll-timeline-BdJZVXlz.js +0 -1
- package/dist/src/app/assets/sync-CStkzc6u.js +0 -4
- package/dist/src/app/assets/vendor-charts-BnDWwBlI.js +0 -36
- package/dist/src/app/assets/vendor-markdown-Bz7N-ca6.js +0 -29
- package/dist/src/app/assets/vendor-react-AtKqiNEf.js +0 -4
- package/dist/src/app/assets/vendor-syntax-D06x6TQF.js +0 -2
- package/dist/src/app/assets/vendor-utils-BvMHZmO7.js +0 -37
- package/dist/src/cache-C5yFZ4gC.cjs +0 -816
- package/dist/src/cache-CaT5tPgo.js +0 -756
- package/dist/src/cache-CyCanoMu.js +0 -6
- package/dist/src/cache-DSqR6ezl.js +0 -726
- package/dist/src/cache-Df_QFDNu.cjs +0 -5
- package/dist/src/cache-HP0NP4k3.js +0 -756
- package/dist/src/cloud-DE3t1-ZI.js +0 -4
- package/dist/src/codex-sdk-BQEw16R_.js +0 -834
- package/dist/src/codex-sdk-C_07GuVS.js +0 -834
- package/dist/src/codex-sdk-DE5G18dx.js +0 -835
- package/dist/src/codex-sdk-ZLKfDjqP.cjs +0 -838
- package/dist/src/eval-7aEqoMs3.js +0 -15
- package/dist/src/evalResult-CYNHkk5A.js +0 -12
- package/dist/src/evalResult-CuvJeNiM.js +0 -10
- package/dist/src/evalResult-tGdilrWt.cjs +0 -10
- package/dist/src/evaluator-BBUqRhz1.js +0 -36
- package/dist/src/fetch-UWU706qb.js +0 -5
- package/dist/src/graders-BxfEguVY.js +0 -32
- package/dist/src/graders-CzVMbEnv.js +0 -34
- package/dist/src/graders-DjCXfj0l.cjs +0 -32
- package/dist/src/graders-kHzIWOKu.js +0 -32
- package/dist/src/messages-DJNo37Ko.js +0 -246
- package/dist/src/messages-Dy9QecMs.js +0 -245
- package/dist/src/messages-HJsyEh4o.cjs +0 -257
- package/dist/src/messages-biC_ex-p.js +0 -245
- package/dist/src/openclaw-0Sv7AK3O.js +0 -580
- package/dist/src/openclaw-CXxbKgDH.cjs +0 -586
- package/dist/src/openclaw-D1FSCps-.js +0 -580
- package/dist/src/openclaw-D2ENvu7a.js +0 -582
- package/dist/src/providers-BSLEaIQG.js +0 -32
- package/dist/src/providers-D-FnDg8k.cjs +0 -31
- package/dist/src/providers-DEYiFVAo.js +0 -30
- package/dist/src/providers-sS2WI8YD.js +0 -30
- package/dist/src/rubyUtils-B1HXG4ej.cjs +0 -4
- package/dist/src/rubyUtils-Rt6pKA96.js +0 -5
- package/dist/src/server-B0Xh1Gx-.js +0 -7
- package/dist/src/server-DJTKu9IR.cjs +0 -5
- package/dist/src/store-C5u6MgC8.js +0 -6
- package/dist/src/store-CNHk-De4.cjs +0 -5
- package/dist/src/telemetry-Yig0Tino.js +0 -7
- package/dist/src/telemetry-p8Pwqm1i.cjs +0 -5
- package/dist/src/transform-ChNIpHz7.js +0 -6
- package/dist/src/transform-PtQ6rAE3.cjs +0 -5
|
@@ -1,813 +1,815 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { o as safeResolve, r as importModule, t as getDirectory } from "./esm-
|
|
8
|
-
import {
|
|
2
|
+
import { N as state, T as getEnvBool, m as sanitizeObject, s as logger } from "./logger-BbY6ypFL.js";
|
|
3
|
+
import { k as TERMINAL_MAX_WIDTH, n as fetchWithProxy } from "./fetch-B6ch2nU2.js";
|
|
4
|
+
import { n as VERSION } from "./version-CbpiUINz.js";
|
|
5
|
+
import { t as invariant } from "./invariant-B2Rf6avk.js";
|
|
6
|
+
import { m as isProviderOptions, o as OutputFileExtension, p as isApiProvider, s as ResultFailureReason } from "./types-BVH9hjgW.js";
|
|
7
|
+
import { o as safeResolve, r as importModule, t as getDirectory } from "./esm-BX8fwlAO.js";
|
|
8
|
+
import { a as getNunjucksEngine, n as renderVarsInObject } from "./render-eui5p5mL.js";
|
|
9
|
+
import { i as isJavascriptFile, t as JAVASCRIPT_EXTENSIONS } from "./fileExtensions-DysCsxNG.js";
|
|
10
|
+
import { r as runPython } from "./pythonUtils-DNqbnRdx.js";
|
|
9
11
|
import dotenv from "dotenv";
|
|
10
12
|
import * as fs$2 from "fs";
|
|
13
|
+
import fs from "fs";
|
|
11
14
|
import * as path$1 from "path";
|
|
12
15
|
import path from "path";
|
|
13
16
|
import * as os$1 from "os";
|
|
14
17
|
import yaml from "js-yaml";
|
|
15
18
|
import * as fsPromises from "fs/promises";
|
|
16
19
|
import dedent from "dedent";
|
|
20
|
+
import nunjucks from "nunjucks";
|
|
17
21
|
import deepEqual from "fast-deep-equal";
|
|
18
22
|
import { parse as parse$1 } from "csv-parse/sync";
|
|
19
23
|
import { globSync, hasMagic } from "glob";
|
|
20
|
-
import nunjucks from "nunjucks";
|
|
21
24
|
import { XMLBuilder } from "fast-xml-parser";
|
|
22
25
|
import { stringify } from "csv-stringify/sync";
|
|
23
|
-
//#region src/util/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
//#region src/util/functions/loadFunction.ts
|
|
27
|
+
const functionCache = {};
|
|
28
|
+
/**
|
|
29
|
+
* Loads a function from a JavaScript or Python file
|
|
30
|
+
* @param options Options for loading the function
|
|
31
|
+
* @returns The loaded function
|
|
32
|
+
*/
|
|
33
|
+
async function loadFunction({ filePath, functionName, defaultFunctionName = "func", basePath = state.basePath, useCache = true }) {
|
|
34
|
+
const cacheKey = `${filePath}${functionName ? `:${functionName}` : ""}`;
|
|
35
|
+
if (useCache && functionCache[cacheKey]) return functionCache[cacheKey];
|
|
36
|
+
const resolvedPath = basePath ? path.resolve(basePath, filePath) : filePath;
|
|
37
|
+
if (!isJavascriptFile(resolvedPath) && !resolvedPath.endsWith(".py")) throw new Error(`File must be a JavaScript (${JAVASCRIPT_EXTENSIONS.join(", ")}) or Python (.py) file`);
|
|
38
|
+
try {
|
|
39
|
+
let func;
|
|
40
|
+
if (isJavascriptFile(resolvedPath)) {
|
|
41
|
+
const module = await importModule(resolvedPath, functionName);
|
|
42
|
+
let moduleFunc;
|
|
43
|
+
if (functionName) moduleFunc = module;
|
|
44
|
+
else moduleFunc = typeof module === "function" ? module : module?.default?.default || module?.default || module?.[defaultFunctionName] || module;
|
|
45
|
+
if (typeof moduleFunc !== "function") throw new Error(functionName ? `JavaScript file must export a "${functionName}" function` : `JavaScript file must export a function (as default export or named export "${defaultFunctionName}")`);
|
|
46
|
+
func = moduleFunc;
|
|
47
|
+
} else {
|
|
48
|
+
const result = (...args) => runPython(resolvedPath, functionName || defaultFunctionName, args);
|
|
49
|
+
func = result;
|
|
50
|
+
}
|
|
51
|
+
if (useCache) functionCache[cacheKey] = func;
|
|
52
|
+
return func;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
logger.error(`Failed to load function: ${err.message}`);
|
|
55
|
+
throw err;
|
|
37
56
|
}
|
|
38
|
-
if ((id.endsWith(".js") || id.endsWith(".ts") || id.endsWith(".mjs")) && (id.includes("/") || id.includes("\\"))) return `file://${path$1.resolve(id)}`;
|
|
39
|
-
return id;
|
|
40
|
-
}
|
|
41
|
-
function getProviderLabel(provider) {
|
|
42
|
-
return provider?.label && typeof provider.label === "string" ? provider.label : void 0;
|
|
43
57
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Extracts the file path and function name from a file:// URL
|
|
60
|
+
* @param fileUrl The file:// URL (e.g., "file://path/to/file.js:functionName")
|
|
61
|
+
* @returns The file path and optional function name
|
|
62
|
+
*/
|
|
63
|
+
function parseFileUrl(fileUrl) {
|
|
64
|
+
if (!fileUrl.startsWith("file://")) throw new Error("URL must start with file://");
|
|
65
|
+
const urlWithoutProtocol = fileUrl.slice(7);
|
|
66
|
+
const lastColonIndex = urlWithoutProtocol.lastIndexOf(":");
|
|
67
|
+
if (lastColonIndex > 1) return {
|
|
68
|
+
filePath: urlWithoutProtocol.slice(0, lastColonIndex),
|
|
69
|
+
functionName: urlWithoutProtocol.slice(lastColonIndex + 1)
|
|
70
|
+
};
|
|
71
|
+
return { filePath: urlWithoutProtocol };
|
|
55
72
|
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/util/file.ts
|
|
56
75
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
76
|
+
* Simple Nunjucks engine specifically for file paths
|
|
77
|
+
* This function is separate from the main getNunjucksEngine to avoid circular dependencies
|
|
59
78
|
*/
|
|
60
|
-
function
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
function getNunjucksEngineForFilePath() {
|
|
80
|
+
const env = nunjucks.configure({ autoescape: false });
|
|
81
|
+
env.addGlobal("env", {
|
|
82
|
+
...process.env,
|
|
83
|
+
...state.config?.env
|
|
84
|
+
});
|
|
85
|
+
return env;
|
|
65
86
|
}
|
|
66
87
|
/**
|
|
67
|
-
*
|
|
68
|
-
* Supports
|
|
88
|
+
* Loads content from an external file if the input is a file path, otherwise
|
|
89
|
+
* returns the input as-is. Supports Nunjucks templating for file paths.
|
|
90
|
+
*
|
|
91
|
+
* @param filePath - The input to process. Can be a file path string starting with "file://",
|
|
92
|
+
* an array of file paths, or any other type of data.
|
|
93
|
+
* @param context - Optional context to control file loading behavior. 'assertion' context
|
|
94
|
+
* preserves Python/JS file references instead of loading their content.
|
|
95
|
+
* @returns The loaded content if the input was a file path, otherwise the original input.
|
|
96
|
+
* For JSON and YAML files, the content is parsed into an object.
|
|
97
|
+
* For other file types, the raw file content is returned as a string.
|
|
98
|
+
*
|
|
99
|
+
* @throws {Error} If the specified file does not exist.
|
|
69
100
|
*/
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
function maybeLoadFromExternalFile(filePath, context) {
|
|
102
|
+
if (Array.isArray(filePath)) return filePath.map((path) => {
|
|
103
|
+
return maybeLoadFromExternalFile(path, context);
|
|
104
|
+
});
|
|
105
|
+
if (typeof filePath !== "string") return filePath;
|
|
106
|
+
if (!filePath.startsWith("file://")) return filePath;
|
|
107
|
+
const renderedFilePath = getNunjucksEngineForFilePath().renderString(filePath, {});
|
|
108
|
+
const { filePath: cleanPath, functionName } = parseFileUrl(renderedFilePath);
|
|
109
|
+
if (context === "assertion" && (cleanPath.endsWith(".py") || isJavascriptFile(cleanPath))) {
|
|
110
|
+
logger.debug(`Preserving Python/JS file reference in assertion context: ${renderedFilePath}`);
|
|
111
|
+
return renderedFilePath;
|
|
80
112
|
}
|
|
81
|
-
if (
|
|
82
|
-
|
|
113
|
+
if (context === "vars") {
|
|
114
|
+
logger.debug(`Preserving file reference in vars context: ${renderedFilePath}`);
|
|
115
|
+
return renderedFilePath;
|
|
116
|
+
}
|
|
117
|
+
if (functionName && (cleanPath.endsWith(".py") || isJavascriptFile(cleanPath))) return renderedFilePath;
|
|
118
|
+
const pathToUse = functionName && !(cleanPath.endsWith(".py") || isJavascriptFile(cleanPath)) ? renderedFilePath.slice(7) : cleanPath;
|
|
119
|
+
const resolvedPath = path$1.resolve(state.basePath || "", pathToUse);
|
|
120
|
+
if (hasMagic(pathToUse)) {
|
|
121
|
+
const matchedFiles = globSync(resolvedPath, { windowsPathsNoEscape: true });
|
|
122
|
+
if (matchedFiles.length === 0) throw new Error(`No files found matching pattern: ${resolvedPath}`);
|
|
123
|
+
const allContents = [];
|
|
124
|
+
for (const matchedFile of matchedFiles) {
|
|
125
|
+
let contents;
|
|
126
|
+
try {
|
|
127
|
+
contents = fs$2.readFileSync(matchedFile, "utf8");
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error.code === "ENOENT") {
|
|
130
|
+
logger.debug(`File disappeared during glob expansion: ${matchedFile}`);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
if (matchedFile.endsWith(".json")) {
|
|
136
|
+
const parsed = JSON.parse(contents);
|
|
137
|
+
if (Array.isArray(parsed)) allContents.push(...parsed);
|
|
138
|
+
else allContents.push(parsed);
|
|
139
|
+
} else if (matchedFile.endsWith(".yaml") || matchedFile.endsWith(".yml")) {
|
|
140
|
+
const parsed = yaml.load(contents);
|
|
141
|
+
if (parsed === null || parsed === void 0) continue;
|
|
142
|
+
if (Array.isArray(parsed)) allContents.push(...parsed);
|
|
143
|
+
else allContents.push(parsed);
|
|
144
|
+
} else if (matchedFile.endsWith(".csv")) {
|
|
145
|
+
const records = parse$1(contents, { columns: true });
|
|
146
|
+
if (records.length > 0 && Object.keys(records[0]).length === 1) allContents.push(...records.map((record) => Object.values(record)[0]));
|
|
147
|
+
else allContents.push(...records);
|
|
148
|
+
} else allContents.push(contents);
|
|
149
|
+
}
|
|
150
|
+
return allContents;
|
|
151
|
+
}
|
|
152
|
+
const finalPath = resolvedPath;
|
|
153
|
+
let contents;
|
|
154
|
+
try {
|
|
155
|
+
contents = fs$2.readFileSync(finalPath, "utf8");
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error.code === "ENOENT") throw new Error(`File does not exist: ${finalPath}`);
|
|
158
|
+
throw new Error(`Failed to read file ${finalPath}: ${error}`);
|
|
159
|
+
}
|
|
160
|
+
if (finalPath.endsWith(".json")) try {
|
|
161
|
+
return JSON.parse(contents);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new Error(`Failed to parse JSON file ${finalPath}: ${error}`);
|
|
164
|
+
}
|
|
165
|
+
if (finalPath.endsWith(".yaml") || finalPath.endsWith(".yml")) try {
|
|
166
|
+
return yaml.load(contents);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new Error(`Failed to parse YAML file ${finalPath}: ${error}`);
|
|
169
|
+
}
|
|
170
|
+
if (finalPath.endsWith(".csv")) {
|
|
171
|
+
const records = parse$1(contents, { columns: true });
|
|
172
|
+
if (records.length > 0 && Object.keys(records[0]).length === 1) return records.map((record) => Object.values(record)[0]);
|
|
173
|
+
return records;
|
|
174
|
+
}
|
|
175
|
+
return contents;
|
|
83
176
|
}
|
|
84
177
|
/**
|
|
85
|
-
*
|
|
178
|
+
* Resolves a relative file path with respect to a base path, handling cloud configuration appropriately.
|
|
179
|
+
* When using a cloud configuration, the current working directory is always used instead of the context's base path.
|
|
180
|
+
*
|
|
181
|
+
* @param filePath - The relative or absolute file path to resolve.
|
|
182
|
+
* @param isCloudConfig - Whether this is a cloud configuration.
|
|
183
|
+
* @returns The resolved absolute file path.
|
|
86
184
|
*/
|
|
87
|
-
function
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
return allowedProviders.some((ref) => doesProviderRefMatch(ref, provider));
|
|
185
|
+
function getResolvedRelativePath(filePath, isCloudConfig) {
|
|
186
|
+
if (path$1.isAbsolute(filePath) || !isCloudConfig) return filePath;
|
|
187
|
+
return path$1.join(process.cwd(), filePath);
|
|
91
188
|
}
|
|
92
189
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
190
|
+
* Recursively loads external file references from a configuration object.
|
|
191
|
+
*
|
|
192
|
+
* @param config - The configuration object to process
|
|
193
|
+
* @param context - Optional context to control file loading behavior
|
|
194
|
+
* @returns The configuration with external file references resolved
|
|
95
195
|
*/
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"curie",
|
|
106
|
-
"babbage",
|
|
107
|
-
"ada",
|
|
108
|
-
"text-embedding",
|
|
109
|
-
"whisper",
|
|
110
|
-
"dall-e",
|
|
111
|
-
"tts"
|
|
112
|
-
].some((indicator) => lowerProviderId.includes(indicator))) return true;
|
|
196
|
+
function maybeLoadConfigFromExternalFile(config, context) {
|
|
197
|
+
if (Array.isArray(config)) return config.map((item) => maybeLoadConfigFromExternalFile(item, context));
|
|
198
|
+
if (typeof config === "object" && config !== null) {
|
|
199
|
+
const result = {};
|
|
200
|
+
for (const key of Object.keys(config)) {
|
|
201
|
+
const childContext = key === "value" && "type" in config && typeof config.type === "string" && (config.type === "python" || config.type === "javascript") ? "assertion" : key === "vars" ? "vars" : context;
|
|
202
|
+
result[key] = maybeLoadConfigFromExternalFile(config[key], childContext);
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
113
205
|
}
|
|
114
|
-
return
|
|
206
|
+
return maybeLoadFromExternalFile(config, context);
|
|
115
207
|
}
|
|
116
208
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
209
|
+
* Parses a file path or glob pattern to extract function names and file extensions.
|
|
210
|
+
* Function names can be specified in the filename like this:
|
|
211
|
+
* prompt.py:myFunction or prompts.js:myFunction.
|
|
212
|
+
* @param basePath - The base path for file resolution.
|
|
213
|
+
* @param promptPath - The path or glob pattern.
|
|
214
|
+
* @returns Parsed details including function name, file extension, and directory status.
|
|
119
215
|
*/
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
134
|
-
google: "GOOGLE_API_KEY",
|
|
135
|
-
mistral: "MISTRAL_API_KEY",
|
|
136
|
-
cohere: "COHERE_API_KEY",
|
|
137
|
-
replicate: "REPLICATE_API_TOKEN",
|
|
138
|
-
voyage: "VOYAGE_API_KEY",
|
|
139
|
-
ai21: "AI21_API_KEY",
|
|
140
|
-
xai: "XAI_API_KEY",
|
|
141
|
-
groq: "GROQ_API_KEY",
|
|
142
|
-
deepseek: "DEEPSEEK_API_KEY",
|
|
143
|
-
perplexity: "PERPLEXITY_API_KEY",
|
|
144
|
-
hyperbolic: "HYPERBOLIC_API_KEY",
|
|
145
|
-
cerebras: "CEREBRAS_API_KEY",
|
|
146
|
-
togetherai: "TOGETHER_API_KEY",
|
|
147
|
-
fal: "FAL_KEY",
|
|
148
|
-
huggingface: "HF_TOKEN",
|
|
149
|
-
"cloudflare-ai": "CLOUDFLARE_API_KEY"
|
|
150
|
-
};
|
|
151
|
-
function getDefaultEnvVar(providerId) {
|
|
152
|
-
const prefix = providerId.split(":")[0];
|
|
153
|
-
return KNOWN_ENV_VARS[prefix] || `${prefix.toUpperCase()}_API_KEY`;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Pre-checks providers for missing API keys before evaluation starts.
|
|
157
|
-
* Assumes getApiKey() is side-effect free (no network calls or token refresh).
|
|
158
|
-
*/
|
|
159
|
-
function checkProviderApiKeys(providers) {
|
|
160
|
-
const missingApiKeys = /* @__PURE__ */ new Map();
|
|
161
|
-
for (const provider of providers) {
|
|
162
|
-
const p = provider;
|
|
163
|
-
if (typeof p.getApiKey !== "function") continue;
|
|
164
|
-
if (provider.id().startsWith("azure:")) continue;
|
|
165
|
-
const requiresKey = typeof p.requiresApiKey === "function" ? p.requiresApiKey() : p.config?.apiKeyRequired !== false;
|
|
166
|
-
let apiKey;
|
|
167
|
-
try {
|
|
168
|
-
apiKey = p.getApiKey();
|
|
169
|
-
} catch {
|
|
170
|
-
apiKey = void 0;
|
|
171
|
-
}
|
|
172
|
-
if (requiresKey && !apiKey) {
|
|
173
|
-
const envVar = p.config?.apiKeyEnvar || getDefaultEnvVar(provider.id());
|
|
174
|
-
if (!missingApiKeys.has(envVar)) missingApiKeys.set(envVar, []);
|
|
175
|
-
missingApiKeys.get(envVar).push(provider.id());
|
|
216
|
+
function parsePathOrGlob(basePath, promptPath) {
|
|
217
|
+
if (promptPath.startsWith("file://")) promptPath = promptPath.slice(7);
|
|
218
|
+
const filePath = path$1.resolve(basePath, promptPath);
|
|
219
|
+
let filename = path$1.relative(basePath, filePath);
|
|
220
|
+
let functionName;
|
|
221
|
+
if (filename.includes(":")) {
|
|
222
|
+
const lastColonIndex = filename.lastIndexOf(":");
|
|
223
|
+
if (lastColonIndex > 1) {
|
|
224
|
+
const pathWithoutFunction = filename.slice(0, lastColonIndex);
|
|
225
|
+
if (isJavascriptFile(pathWithoutFunction) || pathWithoutFunction.endsWith(".py") || pathWithoutFunction.endsWith(".go") || pathWithoutFunction.endsWith(".rb")) {
|
|
226
|
+
functionName = filename.slice(lastColonIndex + 1);
|
|
227
|
+
filename = pathWithoutFunction;
|
|
228
|
+
}
|
|
176
229
|
}
|
|
177
230
|
}
|
|
178
|
-
|
|
231
|
+
let stats;
|
|
232
|
+
try {
|
|
233
|
+
stats = fs$2.statSync(path$1.join(basePath, filename));
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (getEnvBool("PROMPTFOO_STRICT_FILES")) throw err;
|
|
236
|
+
}
|
|
237
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
238
|
+
const isPathPattern = stats?.isDirectory() || hasMagic(promptPath) || hasMagic(normalizedFilePath);
|
|
239
|
+
const safeFilename = path$1.relative(basePath, safeResolve(basePath, filename));
|
|
240
|
+
return {
|
|
241
|
+
extension: isPathPattern ? void 0 : path$1.parse(safeFilename).ext,
|
|
242
|
+
filePath: path$1.join(basePath, safeFilename),
|
|
243
|
+
functionName,
|
|
244
|
+
isPathPattern
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function readOutput(outputPath) {
|
|
248
|
+
const ext = path$1.parse(outputPath).ext.slice(1);
|
|
249
|
+
switch (ext) {
|
|
250
|
+
case "json": return JSON.parse(fs$2.readFileSync(outputPath, "utf-8"));
|
|
251
|
+
default: throw new Error(`Unsupported output file format: ${ext} currently only supports json`);
|
|
252
|
+
}
|
|
179
253
|
}
|
|
180
254
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
255
|
+
* Load custom Nunjucks filters from external files.
|
|
256
|
+
* Note: If a glob pattern matches multiple files, only the last file's export is used.
|
|
257
|
+
* Each filter name should typically resolve to a single file.
|
|
184
258
|
*/
|
|
185
|
-
function
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
259
|
+
async function readFilters(filters, basePath = "") {
|
|
260
|
+
const ret = {};
|
|
261
|
+
for (const [name, filterPath] of Object.entries(filters)) {
|
|
262
|
+
const filePaths = globSync(path$1.join(basePath, filterPath), { windowsPathsNoEscape: true });
|
|
263
|
+
for (const filePath of filePaths) ret[name] = await importModule(path$1.resolve(filePath));
|
|
190
264
|
}
|
|
191
|
-
return
|
|
265
|
+
return ret;
|
|
192
266
|
}
|
|
193
|
-
//#endregion
|
|
194
|
-
//#region src/util/comparison.ts
|
|
195
267
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
268
|
+
* Loads configuration from an external file with variable rendering.
|
|
269
|
+
* This is a convenience wrapper that combines renderVarsInObject and maybeLoadFromExternalFile.
|
|
198
270
|
*
|
|
199
|
-
*
|
|
271
|
+
* Use this for simple config fields that:
|
|
272
|
+
* - Need variable rendering ({{ vars.x }}, {{ env.X }})
|
|
273
|
+
* - May reference external files (file://path.json)
|
|
274
|
+
* - Don't have nested file references that need loading
|
|
200
275
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*/
|
|
204
|
-
const EXPLICIT_RUNTIME_VAR_KEYS = ["sessionId"];
|
|
205
|
-
/**
|
|
206
|
-
* Checks if a variable key is a runtime-only variable that should be filtered
|
|
207
|
-
* when comparing test cases.
|
|
276
|
+
* For fields with nested file references (like response_format.schema),
|
|
277
|
+
* use maybeLoadResponseFormatFromExternalFile instead.
|
|
208
278
|
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
279
|
+
* @param config - The configuration to process
|
|
280
|
+
* @param vars - Variables for template rendering
|
|
281
|
+
* @returns The processed configuration with variables rendered and files loaded
|
|
212
282
|
*/
|
|
213
|
-
function
|
|
214
|
-
return
|
|
283
|
+
function maybeLoadFromExternalFileWithVars(config, vars) {
|
|
284
|
+
return maybeLoadFromExternalFile(renderVarsInObject(config, vars));
|
|
215
285
|
}
|
|
216
286
|
/**
|
|
217
|
-
*
|
|
218
|
-
* but aren't part of the original test definition.
|
|
287
|
+
* Loads response_format configuration from an external file with variable rendering.
|
|
219
288
|
*
|
|
220
|
-
* This
|
|
221
|
-
*
|
|
289
|
+
* This function handles the special case where response_format may contain:
|
|
290
|
+
* 1. A top-level file reference (file://format.json)
|
|
291
|
+
* 2. A nested schema reference for json_schema type (schema: file://schema.json)
|
|
222
292
|
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
293
|
+
* Both levels need variable rendering and file loading.
|
|
294
|
+
*
|
|
295
|
+
* @param responseFormat - The response_format configuration
|
|
296
|
+
* @param vars - Variables for template rendering
|
|
297
|
+
* @returns The processed response_format with all files loaded
|
|
226
298
|
*/
|
|
227
|
-
function
|
|
228
|
-
if (
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
299
|
+
function maybeLoadResponseFormatFromExternalFile(responseFormat, vars) {
|
|
300
|
+
if (responseFormat === void 0 || responseFormat === null) return responseFormat;
|
|
301
|
+
const loaded = maybeLoadFromExternalFile(renderVarsInObject(responseFormat, vars));
|
|
302
|
+
if (!loaded || typeof loaded !== "object") return loaded;
|
|
303
|
+
if (loaded.type === "json_schema") {
|
|
304
|
+
const nestedSchema = loaded.schema || loaded.json_schema?.schema;
|
|
305
|
+
if (nestedSchema) {
|
|
306
|
+
const loadedSchema = maybeLoadFromExternalFile(renderVarsInObject(nestedSchema, vars));
|
|
307
|
+
if (loaded.schema !== void 0) return {
|
|
308
|
+
...loaded,
|
|
309
|
+
schema: loadedSchema
|
|
310
|
+
};
|
|
311
|
+
else if (loaded.json_schema?.schema !== void 0) return {
|
|
312
|
+
...loaded,
|
|
313
|
+
json_schema: {
|
|
314
|
+
...loaded.json_schema,
|
|
315
|
+
schema: loadedSchema
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return loaded;
|
|
232
321
|
}
|
|
233
322
|
/**
|
|
234
|
-
*
|
|
235
|
-
* This
|
|
323
|
+
* Renders variables in a tools object and loads from external file if applicable.
|
|
324
|
+
* This function combines renderVarsInObject and maybeLoadFromExternalFile into a single step
|
|
325
|
+
* specifically for handling tools configurations.
|
|
236
326
|
*
|
|
237
|
-
*
|
|
327
|
+
* Supports loading from JSON, YAML, Python, and JavaScript files.
|
|
328
|
+
*
|
|
329
|
+
* @param tools - The tools configuration object or array to process.
|
|
330
|
+
* @param vars - Variables to use for rendering.
|
|
331
|
+
* @returns The processed tools configuration with variables rendered and content loaded from files if needed.
|
|
332
|
+
* @throws {Error} If the loaded tools are in an invalid format
|
|
238
333
|
*/
|
|
239
|
-
function
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
334
|
+
async function maybeLoadToolsFromExternalFile(tools, vars) {
|
|
335
|
+
const rendered = renderVarsInObject(tools, vars);
|
|
336
|
+
if (typeof rendered === "string" && rendered.startsWith("file://")) {
|
|
337
|
+
const { filePath, functionName } = parseFileUrl(rendered);
|
|
338
|
+
if (functionName && (filePath.endsWith(".py") || isJavascriptFile(filePath))) {
|
|
339
|
+
const fileType = filePath.endsWith(".py") ? "Python" : "JavaScript";
|
|
340
|
+
logger.debug(`[maybeLoadToolsFromExternalFile] Loading tools from ${fileType} file: ${filePath}:${functionName}`);
|
|
341
|
+
try {
|
|
342
|
+
let toolDefinitions;
|
|
343
|
+
if (filePath.endsWith(".py")) {
|
|
344
|
+
const absPath = safeResolve(state.basePath || process.cwd(), filePath);
|
|
345
|
+
logger.debug(`[maybeLoadToolsFromExternalFile] Resolved Python path: ${absPath}`);
|
|
346
|
+
toolDefinitions = await runPython(absPath, functionName, []);
|
|
347
|
+
} else {
|
|
348
|
+
const absPath = safeResolve(state.basePath || process.cwd(), filePath);
|
|
349
|
+
logger.debug(`[maybeLoadToolsFromExternalFile] Resolved JavaScript path: ${absPath}`);
|
|
350
|
+
const module = await importModule(absPath);
|
|
351
|
+
const fn = module[functionName] || module.default?.[functionName];
|
|
352
|
+
if (typeof fn !== "function") {
|
|
353
|
+
const availableExports = Object.keys(module).filter((k) => k !== "default");
|
|
354
|
+
const basePath = state.basePath || process.cwd();
|
|
355
|
+
throw new Error(`Function "${functionName}" not found in ${filePath}. Available exports: ${availableExports.length > 0 ? availableExports.join(", ") : "(none)"}\nResolved from: ${basePath}`);
|
|
356
|
+
}
|
|
357
|
+
toolDefinitions = await Promise.resolve(fn());
|
|
358
|
+
}
|
|
359
|
+
if (!toolDefinitions || typeof toolDefinitions === "string" || typeof toolDefinitions === "number" || typeof toolDefinitions === "boolean") throw new Error(`Function "${functionName}" must return an array or object of tool definitions, but returned: ${toolDefinitions === null ? "null" : typeof toolDefinitions}`);
|
|
360
|
+
logger.debug(`[maybeLoadToolsFromExternalFile] Successfully loaded ${Array.isArray(toolDefinitions) ? toolDefinitions.length : "object"} tools`);
|
|
361
|
+
return toolDefinitions;
|
|
362
|
+
} catch (err) {
|
|
363
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
364
|
+
const basePath = state.basePath || process.cwd();
|
|
365
|
+
throw new Error(`Failed to load tools from ${rendered}:\n${errorMessage}\n\nMake sure the function "${functionName}" exists and returns a valid tool definition array.\nResolved from: ${basePath}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (filePath.endsWith(".py") || isJavascriptFile(filePath)) {
|
|
369
|
+
const ext = filePath.endsWith(".py") ? "Python" : "JavaScript";
|
|
370
|
+
const basePath = state.basePath || process.cwd();
|
|
371
|
+
throw new Error(`Cannot load tools from ${rendered}\n${ext} files require a function name. Use this format:\n tools: file://${filePath}:get_tools\n\nYour ${ext} file should export a function that returns tool definitions:\n` + (filePath.endsWith(".py") ? ` def get_tools():\n return [{"type": "function", "function": {...}}]` : ` module.exports.get_tools = () => [{ type: "function", function: {...} }];`) + `\n\nResolved from: ${basePath}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (Array.isArray(rendered)) {
|
|
375
|
+
const results = await Promise.all(rendered.map((item) => maybeLoadToolsFromExternalFile(item, vars)));
|
|
376
|
+
if (results.every((r) => Array.isArray(r))) return results.flat();
|
|
377
|
+
return results;
|
|
378
|
+
}
|
|
379
|
+
if (typeof rendered !== "string") return rendered;
|
|
380
|
+
const loaded = maybeLoadFromExternalFile(rendered);
|
|
381
|
+
if (loaded !== void 0 && loaded !== null && typeof loaded === "string") {
|
|
382
|
+
if (loaded.startsWith("file://")) throw new Error(`Failed to load tools from ${loaded}\nEnsure the file exists and contains valid JSON or YAML tool definitions.`);
|
|
383
|
+
if (loaded.includes("def ") || loaded.includes("import ")) throw new Error("Invalid tools configuration: file appears to contain Python code.\nPython files require a function name. Use this format:\n tools: file://tools.py:get_tools");
|
|
384
|
+
throw new Error("Invalid tools configuration: expected an array or object, but got a string.\nIf using file://, ensure the file contains valid JSON or YAML tool definitions.");
|
|
385
|
+
}
|
|
386
|
+
return loaded;
|
|
244
387
|
}
|
|
245
|
-
|
|
246
|
-
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/util/providerRef.ts
|
|
390
|
+
const PROVIDER_OPTION_KEYS = new Set([
|
|
391
|
+
"id",
|
|
392
|
+
"label",
|
|
393
|
+
"config",
|
|
394
|
+
"prompts",
|
|
395
|
+
"transform",
|
|
396
|
+
"delay",
|
|
397
|
+
"env",
|
|
398
|
+
"inputs"
|
|
399
|
+
]);
|
|
400
|
+
/** Returns true if the value is a non-empty string suitable as a provider identifier. */
|
|
401
|
+
function isValidProviderId(id) {
|
|
402
|
+
return typeof id === "string" && id !== "";
|
|
403
|
+
}
|
|
404
|
+
function getProviderLabel(provider) {
|
|
405
|
+
if ((typeof provider === "object" || typeof provider === "function") && provider !== null && "label" in provider && typeof provider.label === "string") return provider.label;
|
|
247
406
|
}
|
|
248
407
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* with the same prompt but different strategies.
|
|
252
|
-
*
|
|
253
|
-
* @param testCase - The test case to generate a key for
|
|
254
|
-
* @returns A JSON string that uniquely identifies the test case
|
|
408
|
+
* Resolves relative file paths in provider IDs to absolute paths for consistent matching.
|
|
409
|
+
* Handles file://, exec:, python:, golang: prefixes and bare .js/.ts/.mjs paths.
|
|
255
410
|
*/
|
|
256
|
-
function
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
vars: filteredVars,
|
|
261
|
-
strategyId
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Deduplicates an array of test cases based on their vars and strategyId.
|
|
266
|
-
* Tests with the same vars but different strategies are considered different.
|
|
267
|
-
* Runtime variables (like _conversation, sessionId) are filtered out before comparison.
|
|
268
|
-
*
|
|
269
|
-
* @param tests - Array of test cases to deduplicate
|
|
270
|
-
* @returns Deduplicated array of test cases
|
|
271
|
-
*/
|
|
272
|
-
function deduplicateTestCases(tests) {
|
|
273
|
-
const seen = /* @__PURE__ */ new Set();
|
|
274
|
-
return tests.filter((test) => {
|
|
275
|
-
const key = getTestCaseDeduplicationKey(test);
|
|
276
|
-
if (seen.has(key)) return false;
|
|
277
|
-
seen.add(key);
|
|
278
|
-
return true;
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
function resultIsForTestCase(result, testCase) {
|
|
282
|
-
const testProviderId = testCase.provider ? providerToIdentifier(testCase.provider) : void 0;
|
|
283
|
-
const resultProviderId = providerToIdentifier(result.provider);
|
|
284
|
-
const providersMatch = !testProviderId || !resultProviderId || testProviderId === resultProviderId;
|
|
285
|
-
const resultVars = filterRuntimeVars(result.vars);
|
|
286
|
-
const testVars = filterRuntimeVars(testCase.vars);
|
|
287
|
-
const doVarsMatch = varsMatch(testVars, resultVars);
|
|
288
|
-
const isMatch = doVarsMatch && providersMatch;
|
|
289
|
-
if (!isMatch) {
|
|
290
|
-
const varKeys = testVars ? Object.keys(testVars).join(", ") : "none";
|
|
291
|
-
logger.debug(`[resultIsForTestCase] No match: vars=${doVarsMatch}, providers=${providersMatch}`, {
|
|
292
|
-
testProvider: testProviderId || "none",
|
|
293
|
-
resultProvider: resultProviderId || "none",
|
|
294
|
-
testVarKeys: varKeys
|
|
295
|
-
});
|
|
411
|
+
function canonicalizeProviderId(id) {
|
|
412
|
+
if (id.startsWith("file://")) {
|
|
413
|
+
const filePath = id.slice(7);
|
|
414
|
+
return path.isAbsolute(filePath) ? id : `file://${path.resolve(filePath)}`;
|
|
296
415
|
}
|
|
297
|
-
|
|
416
|
+
for (const prefix of [
|
|
417
|
+
"exec:",
|
|
418
|
+
"python:",
|
|
419
|
+
"golang:"
|
|
420
|
+
]) if (id.startsWith(prefix)) {
|
|
421
|
+
const filePath = id.slice(prefix.length);
|
|
422
|
+
if (filePath.includes("/") || filePath.includes("\\")) return `${prefix}${path.resolve(filePath)}`;
|
|
423
|
+
return id;
|
|
424
|
+
}
|
|
425
|
+
if ((id.endsWith(".js") || id.endsWith(".ts") || id.endsWith(".mjs")) && (id.includes("/") || id.includes("\\"))) return `file://${path.resolve(id)}`;
|
|
426
|
+
return id;
|
|
298
427
|
}
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/util/env.ts
|
|
301
428
|
/**
|
|
302
|
-
*
|
|
303
|
-
* @param envPath - Single path, array of paths, or undefined for default .env loading.
|
|
304
|
-
* When paths are explicitly specified, all files must exist or an error is thrown.
|
|
305
|
-
* When multiple files are provided, later files override values from earlier files.
|
|
429
|
+
* Returns true for provider refs that should be expanded from YAML/JSON config files.
|
|
306
430
|
*/
|
|
307
|
-
function
|
|
308
|
-
|
|
309
|
-
const paths = (Array.isArray(envPath) ? envPath : [envPath]).flatMap((p) => p.includes(",") ? p.split(",").map((s) => s.trim()) : p.trim()).filter((p) => p.length > 0);
|
|
310
|
-
if (paths.length === 0) {
|
|
311
|
-
dotenv.config({ quiet: true });
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
for (const p of paths) if (!fs$2.existsSync(p)) throw new Error(`Environment file not found: ${p}`);
|
|
315
|
-
if (paths.length === 1) logger.info(`Loading environment variables from ${paths[0]}`);
|
|
316
|
-
else logger.info(`Loading environment variables from: ${paths.join(", ")}`);
|
|
317
|
-
const pathArg = paths.length === 1 ? paths[0] : paths;
|
|
318
|
-
dotenv.config({
|
|
319
|
-
path: pathArg,
|
|
320
|
-
override: true,
|
|
321
|
-
quiet: true
|
|
322
|
-
});
|
|
323
|
-
} else dotenv.config({ quiet: true });
|
|
431
|
+
function isProviderConfigFileReference(providerPath) {
|
|
432
|
+
return providerPath.startsWith("file://") && (providerPath.endsWith(".yaml") || providerPath.endsWith(".yml") || providerPath.endsWith(".json"));
|
|
324
433
|
}
|
|
325
|
-
//#endregion
|
|
326
|
-
//#region src/util/functions/loadFunction.ts
|
|
327
|
-
const functionCache = {};
|
|
328
434
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
435
|
+
* Reads a provider config file and normalizes single-provider and multi-provider files.
|
|
436
|
+
* Returns a `wasArray` flag so callers can detect multi-provider files that require
|
|
437
|
+
* `loadApiProviders` instead of `loadApiProvider`.
|
|
332
438
|
*/
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (!isJavascriptFile(resolvedPath) && !resolvedPath.endsWith(".py")) throw new Error(`File must be a JavaScript (${JAVASCRIPT_EXTENSIONS.join(", ")}) or Python (.py) file`);
|
|
439
|
+
function readProviderConfigFile(providerPath, basePath) {
|
|
440
|
+
const relativePath = providerPath.slice(7);
|
|
441
|
+
const resolvedPath = path.isAbsolute(relativePath) ? relativePath : path.join(basePath || process.cwd(), relativePath);
|
|
442
|
+
let rawContent;
|
|
338
443
|
try {
|
|
339
|
-
|
|
340
|
-
if (isJavascriptFile(resolvedPath)) {
|
|
341
|
-
const module = await importModule(resolvedPath, functionName);
|
|
342
|
-
let moduleFunc;
|
|
343
|
-
if (functionName) moduleFunc = module;
|
|
344
|
-
else moduleFunc = typeof module === "function" ? module : module?.default?.default || module?.default || module?.[defaultFunctionName] || module;
|
|
345
|
-
if (typeof moduleFunc !== "function") throw new Error(functionName ? `JavaScript file must export a "${functionName}" function` : `JavaScript file must export a function (as default export or named export "${defaultFunctionName}")`);
|
|
346
|
-
func = moduleFunc;
|
|
347
|
-
} else {
|
|
348
|
-
const result = (...args) => runPython(resolvedPath, functionName || defaultFunctionName, args);
|
|
349
|
-
func = result;
|
|
350
|
-
}
|
|
351
|
-
if (useCache) functionCache[cacheKey] = func;
|
|
352
|
-
return func;
|
|
444
|
+
rawContent = yaml.load(fs.readFileSync(resolvedPath, "utf8"));
|
|
353
445
|
} catch (err) {
|
|
354
|
-
|
|
355
|
-
throw err;
|
|
446
|
+
throw new Error(`Failed to load provider config ${relativePath}: ${err instanceof Error ? err.message : err}`);
|
|
356
447
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
function parseFileUrl(fileUrl) {
|
|
364
|
-
if (!fileUrl.startsWith("file://")) throw new Error("URL must start with file://");
|
|
365
|
-
const urlWithoutProtocol = fileUrl.slice(7);
|
|
366
|
-
const lastColonIndex = urlWithoutProtocol.lastIndexOf(":");
|
|
367
|
-
if (lastColonIndex > 1) return {
|
|
368
|
-
filePath: urlWithoutProtocol.slice(0, lastColonIndex),
|
|
369
|
-
functionName: urlWithoutProtocol.slice(lastColonIndex + 1)
|
|
448
|
+
const fileContent = maybeLoadConfigFromExternalFile(rawContent);
|
|
449
|
+
invariant(fileContent, `Provider config ${relativePath} is undefined`);
|
|
450
|
+
return {
|
|
451
|
+
configs: [fileContent].flat(),
|
|
452
|
+
relativePath,
|
|
453
|
+
wasArray: Array.isArray(fileContent)
|
|
370
454
|
};
|
|
371
|
-
return { filePath: urlWithoutProtocol };
|
|
372
455
|
}
|
|
373
|
-
//#endregion
|
|
374
|
-
//#region src/util/templates.ts
|
|
375
456
|
/**
|
|
376
|
-
*
|
|
377
|
-
* @param filters - Optional map of custom Nunjucks filters.
|
|
378
|
-
* @param throwOnUndefined - Whether to throw an error on undefined variables.
|
|
379
|
-
* @param isGrader - Whether this engine is being used in a grader context.
|
|
380
|
-
* Nunjucks is always enabled in grader mode.
|
|
381
|
-
* @returns A configured Nunjucks environment.
|
|
457
|
+
* Loads provider config objects from a file-backed provider reference.
|
|
382
458
|
*/
|
|
383
|
-
function
|
|
384
|
-
|
|
385
|
-
const env = nunjucks.configure({
|
|
386
|
-
autoescape: false,
|
|
387
|
-
throwOnUndefined
|
|
388
|
-
});
|
|
389
|
-
const envGlobals = {
|
|
390
|
-
...getEnvBool("PROMPTFOO_DISABLE_TEMPLATE_ENV_VARS", getEnvBool("PROMPTFOO_SELF_HOSTED", false)) ? {} : process.env,
|
|
391
|
-
...state.config?.env
|
|
392
|
-
};
|
|
393
|
-
env.addGlobal("env", envGlobals);
|
|
394
|
-
env.addFilter("load", function(str) {
|
|
395
|
-
return JSON.parse(str);
|
|
396
|
-
});
|
|
397
|
-
if (filters) for (const [name, filter] of Object.entries(filters)) env.addFilter(name, filter);
|
|
398
|
-
return env;
|
|
459
|
+
function loadProviderConfigsFromFile(providerPath, basePath) {
|
|
460
|
+
return readProviderConfigFile(providerPath, basePath).configs;
|
|
399
461
|
}
|
|
400
462
|
/**
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* @returns An array of variables used in the template.
|
|
463
|
+
* Pure, synchronous classifier that converts every supported provider reference shape
|
|
464
|
+
* into a discriminated descriptor. Does not read files or instantiate providers.
|
|
404
465
|
*/
|
|
405
|
-
function
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (
|
|
466
|
+
function normalizeProviderRef(provider, options = {}) {
|
|
467
|
+
const { index } = options;
|
|
468
|
+
if (typeof provider === "string") {
|
|
469
|
+
if (!isValidProviderId(provider)) return {
|
|
470
|
+
kind: "unknown",
|
|
471
|
+
id: index === void 0 ? "unknown" : `unknown-${index}`
|
|
472
|
+
};
|
|
473
|
+
if (isProviderConfigFileReference(provider)) return {
|
|
474
|
+
kind: "file",
|
|
475
|
+
id: provider,
|
|
476
|
+
loadProviderPath: provider
|
|
477
|
+
};
|
|
478
|
+
return {
|
|
479
|
+
kind: "named",
|
|
480
|
+
id: provider,
|
|
481
|
+
loadProviderPath: provider
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (typeof provider === "function") {
|
|
485
|
+
const label = getProviderLabel(provider);
|
|
486
|
+
return {
|
|
487
|
+
kind: "function",
|
|
488
|
+
id: label ?? (index === void 0 ? "custom-function" : `custom-function-${index}`),
|
|
489
|
+
label
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (typeof provider === "object" && provider !== null && !Array.isArray(provider)) {
|
|
493
|
+
const providerId = provider.id;
|
|
494
|
+
const label = getProviderLabel(provider);
|
|
495
|
+
if (isValidProviderId(providerId)) return {
|
|
496
|
+
kind: "options",
|
|
497
|
+
id: providerId,
|
|
498
|
+
label,
|
|
499
|
+
loadOptions: provider,
|
|
500
|
+
loadProviderPath: providerId
|
|
501
|
+
};
|
|
502
|
+
const keys = Object.keys(provider);
|
|
503
|
+
if (keys.length === 1 && !PROVIDER_OPTION_KEYS.has(keys[0])) {
|
|
504
|
+
const originalId = keys[0];
|
|
505
|
+
const providerObject = provider[originalId];
|
|
506
|
+
if (typeof providerObject === "object" && providerObject !== null && !Array.isArray(providerObject) && isValidProviderId(originalId)) {
|
|
507
|
+
const id = isValidProviderId(providerObject.id) ? providerObject.id : originalId;
|
|
508
|
+
return {
|
|
509
|
+
kind: "map",
|
|
510
|
+
id,
|
|
511
|
+
label: getProviderLabel(providerObject),
|
|
512
|
+
loadOptions: {
|
|
513
|
+
...providerObject,
|
|
514
|
+
id
|
|
515
|
+
},
|
|
516
|
+
loadProviderPath: originalId
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (isValidProviderId(label)) return {
|
|
521
|
+
kind: "unknown",
|
|
522
|
+
id: label,
|
|
523
|
+
label
|
|
524
|
+
};
|
|
413
525
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
526
|
+
return {
|
|
527
|
+
kind: "unknown",
|
|
528
|
+
id: index === void 0 ? "unknown" : `unknown-${index}`
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/util/provider.ts
|
|
533
|
+
function providerToIdentifier(provider) {
|
|
534
|
+
if (!provider) return;
|
|
535
|
+
if (typeof provider === "string") return canonicalizeProviderId(provider);
|
|
536
|
+
const { label } = normalizeProviderRef(provider);
|
|
537
|
+
if (label) return label;
|
|
538
|
+
if (isApiProvider(provider)) return canonicalizeProviderId(provider.id());
|
|
539
|
+
if (isProviderOptions(provider)) {
|
|
540
|
+
if (provider.id) return canonicalizeProviderId(provider.id);
|
|
541
|
+
return;
|
|
418
542
|
}
|
|
419
|
-
return
|
|
543
|
+
if (typeof provider === "object" && "id" in provider && typeof provider.id === "string") return canonicalizeProviderId(provider.id);
|
|
420
544
|
}
|
|
421
545
|
/**
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* @returns An array of variables used in the templates.
|
|
546
|
+
* Gets a descriptive identifier string for a provider, showing both label and ID when both exist.
|
|
547
|
+
* Useful for error messages to help users debug provider reference issues.
|
|
425
548
|
*/
|
|
426
|
-
function
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
return
|
|
549
|
+
function getProviderDescription(provider) {
|
|
550
|
+
const label = provider.label;
|
|
551
|
+
const id = provider.id();
|
|
552
|
+
if (label && label !== id) return `${label} (${id})`;
|
|
553
|
+
return id;
|
|
430
554
|
}
|
|
431
|
-
//#endregion
|
|
432
|
-
//#region src/util/render.ts
|
|
433
555
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
* Supports full Nunjucks syntax for env vars including filters and expressions:
|
|
438
|
-
* - {{ env.VAR_NAME }}
|
|
439
|
-
* - {{ env['VAR-NAME'] }}
|
|
440
|
-
* - {{ env["VAR-NAME"] }}
|
|
441
|
-
* - {{ env.VAR | default('fallback') }}
|
|
442
|
-
* - {{ env.VAR | upper }}
|
|
443
|
-
*
|
|
444
|
-
* Preserves non-env templates for runtime rendering:
|
|
445
|
-
* - {{ vars.x }} - preserved as literal
|
|
446
|
-
* - {{ prompt }} - preserved as literal
|
|
447
|
-
*
|
|
448
|
-
* Implementation: Uses regex to find env templates, delegates to Nunjucks for rendering.
|
|
449
|
-
* This ensures full Nunjucks feature support while preserving non-env templates.
|
|
450
|
-
*
|
|
451
|
-
* @param obj - The object to process
|
|
452
|
-
* @param envOverrides - Optional env vars to merge with (or replace) the base env
|
|
453
|
-
* @param replaceBase - If true, envOverrides replaces the base env entirely instead of merging
|
|
454
|
-
* @returns The object with only env templates rendered
|
|
556
|
+
* Checks if a provider reference matches a given provider.
|
|
557
|
+
* Supports exact matching and wildcard patterns.
|
|
455
558
|
*/
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (!match.match(/\benv\.|env\[/)) return match;
|
|
467
|
-
const varMatch = match.match(/env\.(\w+)|env\[['"]([^'"]+)['"]\]/);
|
|
468
|
-
const varName = varMatch?.[1] || varMatch?.[2];
|
|
469
|
-
if (match.includes("|") || varName && varName in envGlobals && envGlobals[varName] !== void 0) try {
|
|
470
|
-
return nunjucks.renderString(match, { env: envGlobals });
|
|
471
|
-
} catch (error) {
|
|
472
|
-
logger.debug(`Failed to render env template "${match}": ${error instanceof Error ? error.message : String(error)}`);
|
|
473
|
-
return match;
|
|
474
|
-
}
|
|
475
|
-
return match;
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
if (Array.isArray(obj)) return obj.map((item) => renderEnvOnlyInObject(item, envOverrides, replaceBase));
|
|
479
|
-
if (typeof obj === "object" && obj !== null) {
|
|
480
|
-
const result = {};
|
|
481
|
-
for (const key in obj) result[key] = renderEnvOnlyInObject(obj[key], envOverrides, replaceBase);
|
|
482
|
-
return result;
|
|
559
|
+
function doesProviderRefMatch(ref, provider) {
|
|
560
|
+
const label = provider.label;
|
|
561
|
+
const id = provider.id();
|
|
562
|
+
const canonicalRef = canonicalizeProviderId(ref);
|
|
563
|
+
const canonicalId = canonicalizeProviderId(id);
|
|
564
|
+
if (label && label === ref) return true;
|
|
565
|
+
if (id === ref || canonicalId === canonicalRef) return true;
|
|
566
|
+
if (ref.endsWith("*")) {
|
|
567
|
+
const prefix = ref.slice(0, -1);
|
|
568
|
+
if (label?.startsWith(prefix) || id.startsWith(prefix) || canonicalId.startsWith(prefix)) return true;
|
|
483
569
|
}
|
|
484
|
-
return
|
|
570
|
+
if (label?.startsWith(`${ref}:`) || id.startsWith(`${ref}:`) || canonicalId.startsWith(`${ref}:`)) return true;
|
|
571
|
+
return false;
|
|
485
572
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return result;
|
|
494
|
-
} else if (typeof obj === "function") return renderVarsInObject(obj({ vars }));
|
|
495
|
-
return obj;
|
|
573
|
+
/**
|
|
574
|
+
* Checks if a provider is allowed based on a list of allowed references.
|
|
575
|
+
*/
|
|
576
|
+
function isProviderAllowed(provider, allowedProviders) {
|
|
577
|
+
if (!Array.isArray(allowedProviders)) return true;
|
|
578
|
+
if (allowedProviders.length === 0) return false;
|
|
579
|
+
return allowedProviders.some((ref) => doesProviderRefMatch(ref, provider));
|
|
496
580
|
}
|
|
497
|
-
//#endregion
|
|
498
|
-
//#region src/util/file.ts
|
|
499
581
|
/**
|
|
500
|
-
*
|
|
501
|
-
* This
|
|
582
|
+
* Detects if a provider uses OpenAI models.
|
|
583
|
+
* This includes direct OpenAI providers and Azure OpenAI.
|
|
502
584
|
*/
|
|
503
|
-
function
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
585
|
+
function isOpenAiProvider(providerId) {
|
|
586
|
+
const lowerProviderId = providerId.toLowerCase();
|
|
587
|
+
if (lowerProviderId.startsWith("openai:")) return true;
|
|
588
|
+
if (lowerProviderId.startsWith("azureopenai:")) return true;
|
|
589
|
+
if (lowerProviderId.startsWith("azure:")) {
|
|
590
|
+
if ([
|
|
591
|
+
"gpt",
|
|
592
|
+
"openai",
|
|
593
|
+
"davinci",
|
|
594
|
+
"curie",
|
|
595
|
+
"babbage",
|
|
596
|
+
"ada",
|
|
597
|
+
"text-embedding",
|
|
598
|
+
"whisper",
|
|
599
|
+
"dall-e",
|
|
600
|
+
"tts"
|
|
601
|
+
].some((indicator) => lowerProviderId.includes(indicator))) return true;
|
|
602
|
+
}
|
|
603
|
+
return false;
|
|
510
604
|
}
|
|
511
605
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
* @param filePath - The input to process. Can be a file path string starting with "file://",
|
|
516
|
-
* an array of file paths, or any other type of data.
|
|
517
|
-
* @param context - Optional context to control file loading behavior. 'assertion' context
|
|
518
|
-
* preserves Python/JS file references instead of loading their content.
|
|
519
|
-
* @returns The loaded content if the input was a file path, otherwise the original input.
|
|
520
|
-
* For JSON and YAML files, the content is parsed into an object.
|
|
521
|
-
* For other file types, the raw file content is returned as a string.
|
|
522
|
-
*
|
|
523
|
-
* @throws {Error} If the specified file does not exist.
|
|
606
|
+
* Detects if a provider uses Anthropic/Claude models.
|
|
607
|
+
* This includes direct Anthropic providers, Bedrock with Claude, and Vertex with Claude.
|
|
524
608
|
*/
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (!filePath.startsWith("file://")) return filePath;
|
|
531
|
-
const renderedFilePath = getNunjucksEngineForFilePath().renderString(filePath, {});
|
|
532
|
-
const { filePath: cleanPath, functionName } = parseFileUrl(renderedFilePath);
|
|
533
|
-
if (context === "assertion" && (cleanPath.endsWith(".py") || isJavascriptFile(cleanPath))) {
|
|
534
|
-
logger.debug(`Preserving Python/JS file reference in assertion context: ${renderedFilePath}`);
|
|
535
|
-
return renderedFilePath;
|
|
609
|
+
function isAnthropicProvider(providerId) {
|
|
610
|
+
const lowerProviderId = providerId.toLowerCase();
|
|
611
|
+
if (lowerProviderId.startsWith("anthropic:")) return true;
|
|
612
|
+
if (lowerProviderId.startsWith("bedrock:")) {
|
|
613
|
+
if (lowerProviderId.includes("claude") || lowerProviderId.includes("anthropic")) return true;
|
|
536
614
|
}
|
|
537
|
-
if (
|
|
538
|
-
|
|
539
|
-
return renderedFilePath;
|
|
615
|
+
if (lowerProviderId.startsWith("vertex:")) {
|
|
616
|
+
if (lowerProviderId.includes("claude")) return true;
|
|
540
617
|
}
|
|
541
|
-
|
|
542
|
-
const pathToUse = functionName && !(cleanPath.endsWith(".py") || isJavascriptFile(cleanPath)) ? renderedFilePath.slice(7) : cleanPath;
|
|
543
|
-
const resolvedPath = path$1.resolve(state.basePath || "", pathToUse);
|
|
544
|
-
if (hasMagic(pathToUse)) {
|
|
545
|
-
const matchedFiles = globSync(resolvedPath, { windowsPathsNoEscape: true });
|
|
546
|
-
if (matchedFiles.length === 0) throw new Error(`No files found matching pattern: ${resolvedPath}`);
|
|
547
|
-
const allContents = [];
|
|
548
|
-
for (const matchedFile of matchedFiles) {
|
|
549
|
-
let contents;
|
|
550
|
-
try {
|
|
551
|
-
contents = fs$2.readFileSync(matchedFile, "utf8");
|
|
552
|
-
} catch (error) {
|
|
553
|
-
if (error.code === "ENOENT") {
|
|
554
|
-
logger.debug(`File disappeared during glob expansion: ${matchedFile}`);
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
throw error;
|
|
558
|
-
}
|
|
559
|
-
if (matchedFile.endsWith(".json")) {
|
|
560
|
-
const parsed = JSON.parse(contents);
|
|
561
|
-
if (Array.isArray(parsed)) allContents.push(...parsed);
|
|
562
|
-
else allContents.push(parsed);
|
|
563
|
-
} else if (matchedFile.endsWith(".yaml") || matchedFile.endsWith(".yml")) {
|
|
564
|
-
const parsed = yaml.load(contents);
|
|
565
|
-
if (parsed === null || parsed === void 0) continue;
|
|
566
|
-
if (Array.isArray(parsed)) allContents.push(...parsed);
|
|
567
|
-
else allContents.push(parsed);
|
|
568
|
-
} else if (matchedFile.endsWith(".csv")) {
|
|
569
|
-
const records = parse$1(contents, { columns: true });
|
|
570
|
-
if (records.length > 0 && Object.keys(records[0]).length === 1) allContents.push(...records.map((record) => Object.values(record)[0]));
|
|
571
|
-
else allContents.push(...records);
|
|
572
|
-
} else allContents.push(contents);
|
|
573
|
-
}
|
|
574
|
-
return allContents;
|
|
575
|
-
}
|
|
576
|
-
const finalPath = resolvedPath;
|
|
577
|
-
let contents;
|
|
578
|
-
try {
|
|
579
|
-
contents = fs$2.readFileSync(finalPath, "utf8");
|
|
580
|
-
} catch (error) {
|
|
581
|
-
if (error.code === "ENOENT") throw new Error(`File does not exist: ${finalPath}`);
|
|
582
|
-
throw new Error(`Failed to read file ${finalPath}: ${error}`);
|
|
583
|
-
}
|
|
584
|
-
if (finalPath.endsWith(".json")) try {
|
|
585
|
-
return JSON.parse(contents);
|
|
586
|
-
} catch (error) {
|
|
587
|
-
throw new Error(`Failed to parse JSON file ${finalPath}: ${error}`);
|
|
588
|
-
}
|
|
589
|
-
if (finalPath.endsWith(".yaml") || finalPath.endsWith(".yml")) try {
|
|
590
|
-
return yaml.load(contents);
|
|
591
|
-
} catch (error) {
|
|
592
|
-
throw new Error(`Failed to parse YAML file ${finalPath}: ${error}`);
|
|
593
|
-
}
|
|
594
|
-
if (finalPath.endsWith(".csv")) {
|
|
595
|
-
const records = parse$1(contents, { columns: true });
|
|
596
|
-
if (records.length > 0 && Object.keys(records[0]).length === 1) return records.map((record) => Object.values(record)[0]);
|
|
597
|
-
return records;
|
|
598
|
-
}
|
|
599
|
-
return contents;
|
|
618
|
+
return false;
|
|
600
619
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
620
|
+
const KNOWN_ENV_VARS = {
|
|
621
|
+
openai: "OPENAI_API_KEY",
|
|
622
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
623
|
+
google: "GOOGLE_API_KEY",
|
|
624
|
+
mistral: "MISTRAL_API_KEY",
|
|
625
|
+
cohere: "COHERE_API_KEY",
|
|
626
|
+
replicate: "REPLICATE_API_TOKEN",
|
|
627
|
+
voyage: "VOYAGE_API_KEY",
|
|
628
|
+
ai21: "AI21_API_KEY",
|
|
629
|
+
xai: "XAI_API_KEY",
|
|
630
|
+
groq: "GROQ_API_KEY",
|
|
631
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
632
|
+
perplexity: "PERPLEXITY_API_KEY",
|
|
633
|
+
hyperbolic: "HYPERBOLIC_API_KEY",
|
|
634
|
+
cerebras: "CEREBRAS_API_KEY",
|
|
635
|
+
togetherai: "TOGETHER_API_KEY",
|
|
636
|
+
fal: "FAL_KEY",
|
|
637
|
+
huggingface: "HF_TOKEN",
|
|
638
|
+
"cloudflare-ai": "CLOUDFLARE_API_KEY"
|
|
639
|
+
};
|
|
640
|
+
function getDefaultEnvVar(providerId) {
|
|
641
|
+
const prefix = providerId.split(":")[0];
|
|
642
|
+
return KNOWN_ENV_VARS[prefix] || `${prefix.toUpperCase()}_API_KEY`;
|
|
612
643
|
}
|
|
613
644
|
/**
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* @param config - The configuration object to process
|
|
617
|
-
* @param context - Optional context to control file loading behavior
|
|
618
|
-
* @returns The configuration with external file references resolved
|
|
645
|
+
* Pre-checks providers for missing API keys before evaluation starts.
|
|
646
|
+
* Assumes getApiKey() is side-effect free (no network calls or token refresh).
|
|
619
647
|
*/
|
|
620
|
-
function
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
648
|
+
function checkProviderApiKeys(providers) {
|
|
649
|
+
const missingApiKeys = /* @__PURE__ */ new Map();
|
|
650
|
+
for (const provider of providers) {
|
|
651
|
+
const p = provider;
|
|
652
|
+
if (typeof p.getApiKey !== "function") continue;
|
|
653
|
+
if (provider.id().startsWith("azure:")) continue;
|
|
654
|
+
const requiresKey = typeof p.requiresApiKey === "function" ? p.requiresApiKey() : p.config?.apiKeyRequired !== false;
|
|
655
|
+
let apiKey;
|
|
656
|
+
try {
|
|
657
|
+
apiKey = p.getApiKey();
|
|
658
|
+
} catch {
|
|
659
|
+
apiKey = void 0;
|
|
627
660
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
/**
|
|
633
|
-
* Parses a file path or glob pattern to extract function names and file extensions.
|
|
634
|
-
* Function names can be specified in the filename like this:
|
|
635
|
-
* prompt.py:myFunction or prompts.js:myFunction.
|
|
636
|
-
* @param basePath - The base path for file resolution.
|
|
637
|
-
* @param promptPath - The path or glob pattern.
|
|
638
|
-
* @returns Parsed details including function name, file extension, and directory status.
|
|
639
|
-
*/
|
|
640
|
-
function parsePathOrGlob(basePath, promptPath) {
|
|
641
|
-
if (promptPath.startsWith("file://")) promptPath = promptPath.slice(7);
|
|
642
|
-
const filePath = path$1.resolve(basePath, promptPath);
|
|
643
|
-
let filename = path$1.relative(basePath, filePath);
|
|
644
|
-
let functionName;
|
|
645
|
-
if (filename.includes(":")) {
|
|
646
|
-
const lastColonIndex = filename.lastIndexOf(":");
|
|
647
|
-
if (lastColonIndex > 1) {
|
|
648
|
-
const pathWithoutFunction = filename.slice(0, lastColonIndex);
|
|
649
|
-
if (isJavascriptFile(pathWithoutFunction) || pathWithoutFunction.endsWith(".py") || pathWithoutFunction.endsWith(".go") || pathWithoutFunction.endsWith(".rb")) {
|
|
650
|
-
functionName = filename.slice(lastColonIndex + 1);
|
|
651
|
-
filename = pathWithoutFunction;
|
|
652
|
-
}
|
|
661
|
+
if (requiresKey && !apiKey) {
|
|
662
|
+
const envVar = p.config?.apiKeyEnvar || getDefaultEnvVar(provider.id());
|
|
663
|
+
if (!missingApiKeys.has(envVar)) missingApiKeys.set(envVar, []);
|
|
664
|
+
missingApiKeys.get(envVar).push(provider.id());
|
|
653
665
|
}
|
|
654
666
|
}
|
|
655
|
-
|
|
656
|
-
try {
|
|
657
|
-
stats = fs$2.statSync(path$1.join(basePath, filename));
|
|
658
|
-
} catch (err) {
|
|
659
|
-
if (getEnvBool("PROMPTFOO_STRICT_FILES")) throw err;
|
|
660
|
-
}
|
|
661
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
662
|
-
const isPathPattern = stats?.isDirectory() || hasMagic(promptPath) || hasMagic(normalizedFilePath);
|
|
663
|
-
const safeFilename = path$1.relative(basePath, safeResolve(basePath, filename));
|
|
664
|
-
return {
|
|
665
|
-
extension: isPathPattern ? void 0 : path$1.parse(safeFilename).ext,
|
|
666
|
-
filePath: path$1.join(basePath, safeFilename),
|
|
667
|
-
functionName,
|
|
668
|
-
isPathPattern
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
function readOutput(outputPath) {
|
|
672
|
-
const ext = path$1.parse(outputPath).ext.slice(1);
|
|
673
|
-
switch (ext) {
|
|
674
|
-
case "json": return JSON.parse(fs$2.readFileSync(outputPath, "utf-8"));
|
|
675
|
-
default: throw new Error(`Unsupported output file format: ${ext} currently only supports json`);
|
|
676
|
-
}
|
|
667
|
+
return missingApiKeys;
|
|
677
668
|
}
|
|
678
669
|
/**
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
670
|
+
* Detects if a provider uses Google models.
|
|
671
|
+
* This includes direct Google/Vertex providers with Gemini and other Google models.
|
|
672
|
+
* Note: Vertex with Claude models is NOT counted as Google (it's Anthropic).
|
|
682
673
|
*/
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
674
|
+
function isGoogleProvider(providerId) {
|
|
675
|
+
const lowerProviderId = providerId.toLowerCase();
|
|
676
|
+
if (lowerProviderId.startsWith("google:")) return true;
|
|
677
|
+
if (lowerProviderId.startsWith("vertex:")) {
|
|
678
|
+
if (!lowerProviderId.includes("claude")) return true;
|
|
688
679
|
}
|
|
689
|
-
return
|
|
680
|
+
return false;
|
|
690
681
|
}
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/util/comparison.ts
|
|
691
684
|
/**
|
|
692
|
-
*
|
|
693
|
-
*
|
|
685
|
+
* Explicit runtime variable names that don't follow the underscore convention.
|
|
686
|
+
* These are added during evaluation but aren't part of the original test definition.
|
|
694
687
|
*
|
|
695
|
-
*
|
|
696
|
-
* - Need variable rendering ({{ vars.x }}, {{ env.X }})
|
|
697
|
-
* - May reference external files (file://path.json)
|
|
698
|
-
* - Don't have nested file references that need loading
|
|
688
|
+
* - sessionId: Added by multi-turn strategy providers (GOAT, Crescendo)
|
|
699
689
|
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
690
|
+
* Note: Variables starting with underscore (e.g., _conversation) are automatically
|
|
691
|
+
* treated as runtime variables and filtered out.
|
|
692
|
+
*/
|
|
693
|
+
const EXPLICIT_RUNTIME_VAR_KEYS = ["sessionId"];
|
|
694
|
+
/**
|
|
695
|
+
* Checks if a variable key is a runtime-only variable that should be filtered
|
|
696
|
+
* when comparing test cases.
|
|
702
697
|
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
698
|
+
* Runtime variables are identified by:
|
|
699
|
+
* 1. Starting with underscore (_) - convention for internal/runtime vars
|
|
700
|
+
* 2. Being in the explicit runtime var list (for legacy vars like sessionId)
|
|
706
701
|
*/
|
|
707
|
-
function
|
|
708
|
-
return
|
|
702
|
+
function isRuntimeVar(key) {
|
|
703
|
+
return key.startsWith("_") || EXPLICIT_RUNTIME_VAR_KEYS.includes(key);
|
|
709
704
|
}
|
|
710
705
|
/**
|
|
711
|
-
*
|
|
706
|
+
* Filters out runtime-only variables that are added during evaluation
|
|
707
|
+
* but aren't part of the original test definition.
|
|
712
708
|
*
|
|
713
|
-
* This
|
|
714
|
-
*
|
|
715
|
-
* 2. A nested schema reference for json_schema type (schema: file://schema.json)
|
|
709
|
+
* This is used when comparing test cases to determine if a result
|
|
710
|
+
* corresponds to a particular test, regardless of runtime state.
|
|
716
711
|
*
|
|
717
|
-
*
|
|
712
|
+
* Runtime variables are identified by:
|
|
713
|
+
* - Starting with underscore (e.g., _conversation, _metadata)
|
|
714
|
+
* - Being in the explicit list (e.g., sessionId for backward compatibility)
|
|
715
|
+
*/
|
|
716
|
+
function filterRuntimeVars(vars) {
|
|
717
|
+
if (!vars || typeof vars !== "object" || Array.isArray(vars)) return vars;
|
|
718
|
+
const filtered = {};
|
|
719
|
+
for (const [key, value] of Object.entries(vars)) if (!isRuntimeVar(key)) filtered[key] = value;
|
|
720
|
+
return filtered;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Extracts only runtime variables from a vars object.
|
|
724
|
+
* This is the inverse of filterRuntimeVars.
|
|
718
725
|
*
|
|
719
|
-
*
|
|
720
|
-
* @param vars - Variables for template rendering
|
|
721
|
-
* @returns The processed response_format with all files loaded
|
|
726
|
+
* Used to restore runtime state when re-running filtered tests.
|
|
722
727
|
*/
|
|
723
|
-
function
|
|
724
|
-
if (
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
if (loaded.schema !== void 0) return {
|
|
732
|
-
...loaded,
|
|
733
|
-
schema: loadedSchema
|
|
734
|
-
};
|
|
735
|
-
else if (loaded.json_schema?.schema !== void 0) return {
|
|
736
|
-
...loaded,
|
|
737
|
-
json_schema: {
|
|
738
|
-
...loaded.json_schema,
|
|
739
|
-
schema: loadedSchema
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return loaded;
|
|
728
|
+
function extractRuntimeVars(vars) {
|
|
729
|
+
if (!vars || typeof vars !== "object" || Array.isArray(vars)) return;
|
|
730
|
+
const extracted = {};
|
|
731
|
+
for (const [key, value] of Object.entries(vars)) if (isRuntimeVar(key)) extracted[key] = value;
|
|
732
|
+
return Object.keys(extracted).length > 0 ? extracted : void 0;
|
|
733
|
+
}
|
|
734
|
+
function varsMatch(vars1, vars2) {
|
|
735
|
+
return deepEqual(vars1, vars2);
|
|
745
736
|
}
|
|
746
737
|
/**
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
738
|
+
* Generate a unique key for a test case for deduplication purposes.
|
|
739
|
+
* Excludes runtime variables and includes strategyId to distinguish tests
|
|
740
|
+
* with the same prompt but different strategies.
|
|
750
741
|
*
|
|
751
|
-
*
|
|
742
|
+
* @param testCase - The test case to generate a key for
|
|
743
|
+
* @returns A JSON string that uniquely identifies the test case
|
|
744
|
+
*/
|
|
745
|
+
function getTestCaseDeduplicationKey(testCase) {
|
|
746
|
+
const filteredVars = filterRuntimeVars(testCase.vars);
|
|
747
|
+
const strategyId = testCase.metadata?.strategyId || "none";
|
|
748
|
+
return JSON.stringify({
|
|
749
|
+
vars: filteredVars,
|
|
750
|
+
strategyId
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Deduplicates an array of test cases based on their vars and strategyId.
|
|
755
|
+
* Tests with the same vars but different strategies are considered different.
|
|
756
|
+
* Runtime variables (like _conversation, sessionId) are filtered out before comparison.
|
|
752
757
|
*
|
|
753
|
-
* @param
|
|
754
|
-
* @
|
|
755
|
-
* @returns The processed tools configuration with variables rendered and content loaded from files if needed.
|
|
756
|
-
* @throws {Error} If the loaded tools are in an invalid format
|
|
758
|
+
* @param tests - Array of test cases to deduplicate
|
|
759
|
+
* @returns Deduplicated array of test cases
|
|
757
760
|
*/
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
if (
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
}
|
|
783
|
-
if (!toolDefinitions || typeof toolDefinitions === "string" || typeof toolDefinitions === "number" || typeof toolDefinitions === "boolean") throw new Error(`Function "${functionName}" must return an array or object of tool definitions, but returned: ${toolDefinitions === null ? "null" : typeof toolDefinitions}`);
|
|
784
|
-
logger.debug(`[maybeLoadToolsFromExternalFile] Successfully loaded ${Array.isArray(toolDefinitions) ? toolDefinitions.length : "object"} tools`);
|
|
785
|
-
return toolDefinitions;
|
|
786
|
-
} catch (err) {
|
|
787
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
788
|
-
const basePath = state.basePath || process.cwd();
|
|
789
|
-
throw new Error(`Failed to load tools from ${rendered}:\n${errorMessage}\n\nMake sure the function "${functionName}" exists and returns a valid tool definition array.\nResolved from: ${basePath}`);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
if (filePath.endsWith(".py") || isJavascriptFile(filePath)) {
|
|
793
|
-
const ext = filePath.endsWith(".py") ? "Python" : "JavaScript";
|
|
794
|
-
const basePath = state.basePath || process.cwd();
|
|
795
|
-
throw new Error(`Cannot load tools from ${rendered}\n${ext} files require a function name. Use this format:\n tools: file://${filePath}:get_tools\n\nYour ${ext} file should export a function that returns tool definitions:\n` + (filePath.endsWith(".py") ? ` def get_tools():\n return [{"type": "function", "function": {...}}]` : ` module.exports.get_tools = () => [{ type: "function", function: {...} }];`) + `\n\nResolved from: ${basePath}`);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
if (Array.isArray(rendered)) {
|
|
799
|
-
const results = await Promise.all(rendered.map((item) => maybeLoadToolsFromExternalFile(item, vars)));
|
|
800
|
-
if (results.every((r) => Array.isArray(r))) return results.flat();
|
|
801
|
-
return results;
|
|
802
|
-
}
|
|
803
|
-
if (typeof rendered !== "string") return rendered;
|
|
804
|
-
const loaded = maybeLoadFromExternalFile(rendered);
|
|
805
|
-
if (loaded !== void 0 && loaded !== null && typeof loaded === "string") {
|
|
806
|
-
if (loaded.startsWith("file://")) throw new Error(`Failed to load tools from ${loaded}\nEnsure the file exists and contains valid JSON or YAML tool definitions.`);
|
|
807
|
-
if (loaded.includes("def ") || loaded.includes("import ")) throw new Error("Invalid tools configuration: file appears to contain Python code.\nPython files require a function name. Use this format:\n tools: file://tools.py:get_tools");
|
|
808
|
-
throw new Error("Invalid tools configuration: expected an array or object, but got a string.\nIf using file://, ensure the file contains valid JSON or YAML tool definitions.");
|
|
761
|
+
function deduplicateTestCases(tests) {
|
|
762
|
+
const seen = /* @__PURE__ */ new Set();
|
|
763
|
+
return tests.filter((test) => {
|
|
764
|
+
const key = getTestCaseDeduplicationKey(test);
|
|
765
|
+
if (seen.has(key)) return false;
|
|
766
|
+
seen.add(key);
|
|
767
|
+
return true;
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
function resultIsForTestCase(result, testCase) {
|
|
771
|
+
const testProviderId = testCase.provider ? providerToIdentifier(testCase.provider) : void 0;
|
|
772
|
+
const resultProviderId = providerToIdentifier(result.provider);
|
|
773
|
+
const providersMatch = !testProviderId || !resultProviderId || testProviderId === resultProviderId;
|
|
774
|
+
const resultVars = filterRuntimeVars(result.vars);
|
|
775
|
+
const testVars = filterRuntimeVars(testCase.vars);
|
|
776
|
+
const doVarsMatch = varsMatch(testVars, resultVars);
|
|
777
|
+
const isMatch = doVarsMatch && providersMatch;
|
|
778
|
+
if (!isMatch) {
|
|
779
|
+
const varKeys = testVars ? Object.keys(testVars).join(", ") : "none";
|
|
780
|
+
logger.debug(`[resultIsForTestCase] No match: vars=${doVarsMatch}, providers=${providersMatch}`, {
|
|
781
|
+
testProvider: testProviderId || "none",
|
|
782
|
+
resultProvider: resultProviderId || "none",
|
|
783
|
+
testVarKeys: varKeys
|
|
784
|
+
});
|
|
809
785
|
}
|
|
810
|
-
return
|
|
786
|
+
return isMatch;
|
|
787
|
+
}
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region src/util/env.ts
|
|
790
|
+
/**
|
|
791
|
+
* Load environment variables from .env file(s).
|
|
792
|
+
* @param envPath - Single path, array of paths, or undefined for default .env loading.
|
|
793
|
+
* When paths are explicitly specified, all files must exist or an error is thrown.
|
|
794
|
+
* When multiple files are provided, later files override values from earlier files.
|
|
795
|
+
*/
|
|
796
|
+
function setupEnv(envPath) {
|
|
797
|
+
if (envPath) {
|
|
798
|
+
const paths = (Array.isArray(envPath) ? envPath : [envPath]).flatMap((p) => p.includes(",") ? p.split(",").map((s) => s.trim()) : p.trim()).filter((p) => p.length > 0);
|
|
799
|
+
if (paths.length === 0) {
|
|
800
|
+
dotenv.config({ quiet: true });
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
for (const p of paths) if (!fs$2.existsSync(p)) throw new Error(`Environment file not found: ${p}`);
|
|
804
|
+
if (paths.length === 1) logger.info(`Loading environment variables from ${paths[0]}`);
|
|
805
|
+
else logger.info(`Loading environment variables from: ${paths.join(", ")}`);
|
|
806
|
+
const pathArg = paths.length === 1 ? paths[0] : paths;
|
|
807
|
+
dotenv.config({
|
|
808
|
+
path: pathArg,
|
|
809
|
+
override: true,
|
|
810
|
+
quiet: true
|
|
811
|
+
});
|
|
812
|
+
} else dotenv.config({ quiet: true });
|
|
811
813
|
}
|
|
812
814
|
//#endregion
|
|
813
815
|
//#region src/googleSheets.ts
|
|
@@ -1422,6 +1424,6 @@ function printBorder() {
|
|
|
1422
1424
|
logger.info(border);
|
|
1423
1425
|
}
|
|
1424
1426
|
//#endregion
|
|
1425
|
-
export {
|
|
1427
|
+
export { maybeLoadFromExternalFile as A, isProviderConfigFileReference as C, getNunjucksEngineForFilePath as D, readProviderConfigFile as E, readFilters as F, readOutput as I, loadFunction as L, maybeLoadResponseFormatFromExternalFile as M, maybeLoadToolsFromExternalFile as N, getResolvedRelativePath as O, parsePathOrGlob as P, parseFileUrl as R, isProviderAllowed as S, normalizeProviderRef as T, doesProviderRefMatch as _, ComparisonEvalNotFoundError as a, isGoogleProvider as b, mergeComparisonTables as c, deduplicateTestCases as d, extractRuntimeVars as f, checkProviderApiKeys as g, resultIsForTestCase as h, writeOutput as i, maybeLoadFromExternalFileWithVars as j, maybeLoadConfigFromExternalFile as k, fetchCsvFromGoogleSheet as l, getTestCaseDeduplicationKey as m, createOutputMetadata as n, evalTableToJson as o, filterRuntimeVars as p, writeMultipleOutputs as r, generateEvalCsv as s, printBorder as t, setupEnv as u, getProviderDescription as v, loadProviderConfigsFromFile as w, isOpenAiProvider as x, isAnthropicProvider as y };
|
|
1426
1428
|
|
|
1427
|
-
//# sourceMappingURL=util-
|
|
1429
|
+
//# sourceMappingURL=util-BQOCAHQC.js.map
|