@langwatch/mcp-server 2.0.0 → 2.2.0
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/.gitignore +7 -0
- package/.npmignore +41 -0
- package/CHANGELOG.md +280 -200
- package/CONTRIBUTING.md +10 -0
- package/README.md +82 -52
- package/dist/add-dashboard-widget-4ZC5UN73.js +50 -0
- package/dist/add-dashboard-widget-ACC6QYBG.js +49 -0
- package/dist/archive-project-KWFM3I5Z.js +19 -0
- package/dist/{archive-project-4EQGLBJJ.js → archive-project-SPVHXK6A.js} +4 -4
- package/dist/archive-run-plan-65QN3XRX.js +16 -0
- package/dist/archive-run-plan-Z4SYE6BP.js +15 -0
- package/dist/{archive-scenario-W6BXYDZ3.js → archive-scenario-A6QV5LOW.js} +4 -4
- package/dist/archive-scenario-FXEYQNEH.js +19 -0
- package/dist/archive-test-suite-5ADQ3KPQ.js +15 -0
- package/dist/archive-test-suite-NKZDZY3D.js +16 -0
- package/dist/assign-prompt-tag-5IUZXJ2T.js +23 -0
- package/dist/{assign-prompt-tag-VLXUWEKU.js → assign-prompt-tag-HBZLBIFD.js} +8 -4
- package/dist/{chunk-CDYQOY5U.js → chunk-3OCG3QPU.js} +7 -10
- package/dist/chunk-3YLN3ATJ.js +241 -0
- package/dist/chunk-5FNCQRCX.js +91 -0
- package/dist/chunk-6YZUOD34.js +55 -0
- package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
- package/dist/chunk-7VNSRCXO.js +129 -0
- package/dist/{chunk-7RHG56OH.js → chunk-B4J3PJLX.js} +11 -10
- package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
- package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
- package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
- package/dist/{chunk-WT4PO4A3.js → chunk-H6WGMG2T.js} +10 -7
- package/dist/{chunk-TOXJQDIZ.js → chunk-HGELNIVY.js} +135 -51
- package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
- package/dist/{chunk-J42FWRJ2.js → chunk-I7ATIZED.js} +7 -7
- package/dist/{chunk-AQJO2EI5.js → chunk-ILZDO25L.js} +7 -10
- package/dist/{chunk-4QC7SA2L.js → chunk-J7PQKBLD.js} +884 -559
- package/dist/{chunk-YX3A3CFM.js → chunk-JRJPH75H.js} +7 -13
- package/dist/chunk-K2I7Z3LZ.js +28 -0
- package/dist/{chunk-5CCZFUXS.js → chunk-NKMTIHIJ.js} +6 -9
- package/dist/chunk-O7EMWEVJ.js +44 -0
- package/dist/chunk-PTDR56Z3.js +37 -0
- package/dist/chunk-RABYBFX5.js +43 -0
- package/dist/chunk-SUPUUC4W.js +64 -0
- package/dist/{chunk-SFB4MU6H.js → chunk-SXFC3FNH.js} +4 -4
- package/dist/chunk-U24EACC4.js +67 -0
- package/dist/chunk-UNJM3BIL.js +21 -0
- package/dist/{chunk-ZWIL4EFL.js → chunk-UTJCWXPZ.js} +4 -4
- package/dist/chunk-UWLE5T2F.js +312 -0
- package/dist/chunk-VMWJD732.js +57 -0
- package/dist/chunk-XUIJYNRJ.js +15 -0
- package/dist/chunk-Y34DHR5K.js +67 -0
- package/dist/chunk-YCARO42Z.js +516 -0
- package/dist/config-EAKW6F26.js +16 -0
- package/dist/config.cjs +109 -0
- package/dist/config.js +5 -4
- package/dist/{create-agent-7XHO6UIS.js → create-agent-AYZ3LB3V.js} +4 -4
- package/dist/create-agent-S5ZBCEFT.js +23 -0
- package/dist/create-annotation-LHNCLHYP.js +29 -0
- package/dist/{create-annotation-YQXRQC6A.js → create-annotation-VUSXULQK.js} +10 -5
- package/dist/{create-api-key-YUS36KBU.js → create-api-key-NNYKARMG.js} +5 -7
- package/dist/create-api-key-T4KSNPKB.js +24 -0
- package/dist/create-dashboard-DHVFMEU4.js +18 -0
- package/dist/{create-dashboard-4Z5JO5LB.js → create-dashboard-WTKKS5Z6.js} +4 -4
- package/dist/{create-dataset-E6NMSVX7.js → create-dataset-2MC4SD4O.js} +6 -6
- package/dist/create-dataset-LUP5PVDM.js +22 -0
- package/dist/create-dataset-records-NSKBZP3G.js +20 -0
- package/dist/{create-dataset-records-SKHPRD2K.js → create-dataset-records-W4GLN2FV.js} +5 -7
- package/dist/{create-evaluator-JXF5PF33.js → create-evaluator-643G6LXL.js} +4 -4
- package/dist/create-evaluator-HMTYO5SS.js +27 -0
- package/dist/create-mcp-server.cjs +9513 -0
- package/dist/create-mcp-server.js +2647 -5
- package/dist/create-project-6N7PIYRY.js +44 -0
- package/dist/{create-project-CS2CB57Y.js → create-project-KAIC46UY.js} +4 -4
- package/dist/{create-prompt-F5RMX3L3.js → create-prompt-E67LXIYT.js} +5 -7
- package/dist/create-prompt-PKJ7EDRR.js +37 -0
- package/dist/create-prompt-tag-4YO3FSJ3.js +17 -0
- package/dist/{create-prompt-tag-GMRWTEFR.js → create-prompt-tag-OPQUEXWB.js} +3 -3
- package/dist/create-scenario-5PJBMAXO.js +33 -0
- package/dist/{create-scenario-JDR2VB2H.js → create-scenario-UEM77OE7.js} +11 -4
- package/dist/create-test-suite-4IPXVSFQ.js +37 -0
- package/dist/create-test-suite-SZEPVXSE.js +36 -0
- package/dist/create-trigger-5HZSCRAN.js +49 -0
- package/dist/create-trigger-PRELMT6J.js +48 -0
- package/dist/delete-agent-JUIZWAXG.js +18 -0
- package/dist/{delete-agent-O3EEH4S5.js → delete-agent-QMEK3XHK.js} +4 -4
- package/dist/delete-annotation-HY3O2OGR.js +17 -0
- package/dist/{delete-annotation-XP6FQM7A.js → delete-annotation-VKIQWUYW.js} +4 -4
- package/dist/delete-dashboard-76N66XXL.js +18 -0
- package/dist/{delete-dashboard-RFUNYCNP.js → delete-dashboard-JVIVHNJM.js} +4 -4
- package/dist/delete-dataset-HQCR66MG.js +18 -0
- package/dist/{delete-dataset-3MJFRDLP.js → delete-dataset-WKRUW4FP.js} +5 -7
- package/dist/{delete-dataset-records-WPMU5GL5.js → delete-dataset-records-C2IQFFVQ.js} +5 -7
- package/dist/delete-dataset-records-SDUHQLLM.js +19 -0
- package/dist/delete-evaluator-4SNSCLEE.js +16 -0
- package/dist/delete-evaluator-QP3QHTLN.js +15 -0
- package/dist/delete-prompt-tag-NZ4JQMYG.js +17 -0
- package/dist/{delete-prompt-tag-KNMZVSPB.js → delete-prompt-tag-YYQ4VMAD.js} +3 -3
- package/dist/{delete-workflow-3NUXO46U.js → delete-workflow-5J46T73T.js} +4 -4
- package/dist/delete-workflow-JSWABL5F.js +17 -0
- package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
- package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
- package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
- package/dist/discover-scenario-schema-OOFIQKOG.js +139 -0
- package/dist/{discover-schema-L6QDPON3.js → discover-schema-55YJBFGQ.js} +181 -186
- package/dist/discover-schema-A33SLMXW.js +442 -0
- package/dist/get-agent-LJQX7OF4.js +69 -0
- package/dist/get-agent-OTIYUFZA.js +70 -0
- package/dist/get-analytics-DCS72353.js +80 -0
- package/dist/get-analytics-IQZKJHLW.js +79 -0
- package/dist/get-dashboard-5NYJEIX2.js +29 -0
- package/dist/{get-dashboard-KHBEGZGL.js → get-dashboard-QMTYDQAZ.js} +4 -4
- package/dist/get-dataset-ALSP7RTX.js +64 -0
- package/dist/get-dataset-X4B5JDG2.js +63 -0
- package/dist/{get-evaluator-ADMKVWYZ.js → get-evaluator-ISMC4LLI.js} +4 -4
- package/dist/get-evaluator-XEUZGZCH.js +47 -0
- package/dist/{get-project-RAK7URQJ.js → get-project-5VKROSDH.js} +4 -4
- package/dist/get-project-RM6FRCBK.js +26 -0
- package/dist/get-prompt-OWXJZHQQ.js +95 -0
- package/dist/get-prompt-W52DY36E.js +94 -0
- package/dist/get-run-plan-BE3M2AUX.js +59 -0
- package/dist/get-run-plan-RUXQ6PAX.js +58 -0
- package/dist/get-scenario-RNM7BAX2.js +37 -0
- package/dist/{get-scenario-ZTAJJL5T.js → get-scenario-YM4ODWW6.js} +8 -4
- package/dist/get-simulation-run-6GIATQE5.js +69 -0
- package/dist/{get-simulation-run-T2ZQWYH2.js → get-simulation-run-LCFSMPGQ.js} +33 -20
- package/dist/get-test-suite-GC2O7YH6.js +49 -0
- package/dist/get-test-suite-P5LJ6VLF.js +48 -0
- package/dist/get-trace-KBRVV4NN.js +45 -0
- package/dist/{get-trace-K7U234Q4.js → get-trace-SEF3FMR5.js} +11 -13
- package/dist/get-workflow-7PVED7JE.js +25 -0
- package/dist/{get-workflow-L5L2PSK3.js → get-workflow-YXULW4GC.js} +4 -4
- package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
- package/dist/{http-server-YXHFPPGA.js → http-server-IR4QOS43.js} +4071 -342
- package/dist/index.js +60221 -38077
- package/dist/langwatch-api-annotations-6SA4OB4C.js +17 -0
- package/dist/{langwatch-api-annotations-FDAS6NQ7.js → langwatch-api-annotations-TBHLCQ5R.js} +4 -4
- package/dist/langwatch-api-dashboards-6EO2VVZW.js +17 -0
- package/dist/{langwatch-api-dashboards-JTVCS7ZJ.js → langwatch-api-dashboards-NHU5BZD7.js} +4 -4
- package/dist/langwatch-api-monitors-CBF3FGIM.js +47 -0
- package/dist/{langwatch-api-monitors-3J7UIQME.js → langwatch-api-monitors-YNWA2M5Y.js} +11 -16
- package/dist/langwatch-api-secrets-ADTOJELP.js +56 -0
- package/dist/langwatch-api-secrets-RNFO4LF2.js +55 -0
- package/dist/langwatch-api-triggers-KQDQC6L5.js +22 -0
- package/dist/langwatch-api-triggers-QS7HDIVW.js +23 -0
- package/dist/{langwatch-api-workflows-NBJX5KOG.js → langwatch-api-workflows-OKC4FXIF.js} +4 -4
- package/dist/langwatch-api-workflows-XGJE6SR5.js +15 -0
- package/dist/list-agents-HVYWEQMI.js +43 -0
- package/dist/list-agents-RBAOCF53.js +42 -0
- package/dist/list-annotations-HJH4NBLD.js +38 -0
- package/dist/{list-annotations-ZTVP4EEL.js → list-annotations-J4UAHSUN.js} +10 -5
- package/dist/{list-api-keys-CWD4UQT5.js → list-api-keys-7UTJRZDG.js} +10 -5
- package/dist/list-api-keys-A2PMV435.js +42 -0
- package/dist/list-dashboard-boards-OPWROHMG.js +33 -0
- package/dist/list-dashboard-boards-U2GJ7GC5.js +34 -0
- package/dist/{list-dashboards-PWG6P6KR.js → list-dashboards-ROSYFLDQ.js} +4 -4
- package/dist/list-dashboards-RQ635ZU7.js +32 -0
- package/dist/{list-dataset-records-IOE7LQXS.js → list-dataset-records-M5TKA6LK.js} +14 -10
- package/dist/list-dataset-records-WFM7F33C.js +43 -0
- package/dist/{list-datasets-ZT4JYUDT.js → list-datasets-B5X4P7BQ.js} +9 -13
- package/dist/list-datasets-THUKQHPX.js +56 -0
- package/dist/{list-evaluators-SJHC7VBH.js → list-evaluators-MYH2OVCI.js} +5 -7
- package/dist/list-evaluators-NLVWF6K7.js +32 -0
- package/dist/{list-model-providers-PPD2WPD2.js → list-model-providers-3JUGP4YI.js} +4 -4
- package/dist/list-model-providers-H3XKORFO.js +35 -0
- package/dist/{list-projects-K7QAIX2I.js → list-projects-6EJ5YYEW.js} +5 -7
- package/dist/list-projects-YNNSRMLC.js +40 -0
- package/dist/{list-prompt-tags-FOO6TNHB.js → list-prompt-tags-NVFQO6WX.js} +3 -3
- package/dist/list-prompt-tags-XUQTTAH4.js +23 -0
- package/dist/{list-prompts-SC2HAUFG.js → list-prompts-HSIMMHTF.js} +4 -6
- package/dist/list-prompts-IRI6AIXO.js +30 -0
- package/dist/list-run-plans-PX2XI6EK.js +46 -0
- package/dist/list-run-plans-QALLCN2K.js +45 -0
- package/dist/list-scenarios-QZRPVHS4.js +46 -0
- package/dist/list-scenarios-SSJQEMJP.js +45 -0
- package/dist/list-simulation-runs-KNOLGMDV.js +53 -0
- package/dist/{list-simulation-runs-KF5THG5H.js → list-simulation-runs-OEADLLQR.js} +19 -9
- package/dist/list-test-suites-ANAQBLDG.js +34 -0
- package/dist/list-test-suites-N4WVLBKK.js +35 -0
- package/dist/list-trigger-fires-JV3WFYYG.js +19 -0
- package/dist/list-trigger-fires-WTW4DVIW.js +20 -0
- package/dist/list-triggers-SQNIG35Q.js +45 -0
- package/dist/list-triggers-V4Y356AK.js +46 -0
- package/dist/{list-workflows-QSJWURCK.js → list-workflows-IMSX7SXU.js} +5 -7
- package/dist/list-workflows-ZHTX5LCL.js +33 -0
- package/dist/{rename-prompt-tag-EKWU5QF3.js → rename-prompt-tag-IQI7MHOI.js} +3 -3
- package/dist/rename-prompt-tag-PA2BRIE6.js +18 -0
- package/dist/rename-test-suite-4J7UX5S2.js +21 -0
- package/dist/rename-test-suite-NNVLMLKY.js +20 -0
- package/dist/report-issue-AS34ZMJF.js +898 -0
- package/dist/report-issue-DCZFLGDO.js +899 -0
- package/dist/rerun-run-plan-TG2V4WM4.js +22 -0
- package/dist/rerun-run-plan-ZI5OY3VA.js +21 -0
- package/dist/revoke-api-key-3J3ZA4UB.js +15 -0
- package/dist/{revoke-api-key-FTRTZMOF.js → revoke-api-key-SFMEFWJC.js} +4 -4
- package/dist/run-agent-W4VJP72Z.js +75 -0
- package/dist/run-agent-YNV6ZHW4.js +74 -0
- package/dist/run-plan-6C77GWG4.js +38 -0
- package/dist/run-plan-PAARQSI4.js +39 -0
- package/dist/run-query-KJDH544C.js +47 -0
- package/dist/run-query-T4QW2Z5O.js +46 -0
- package/dist/run-test-suite-SQJELEZX.js +26 -0
- package/dist/run-test-suite-STB6NUJM.js +25 -0
- package/dist/{run-workflow-QPXC627O.js → run-workflow-D6PQNBI6.js} +4 -4
- package/dist/{run-agent-6SM4XNA7.js → run-workflow-SP32SO5M.js} +10 -11
- package/dist/search-traces-HCBGOG6R.js +170 -0
- package/dist/search-traces-HSTQ6WTQ.js +171 -0
- package/dist/{set-model-provider-3WKT63ZN.js → set-model-provider-GF7NXUJA.js} +4 -4
- package/dist/set-model-provider-KDQ7C4EZ.js +33 -0
- package/dist/{update-agent-SMAEHLXH.js → update-agent-DWG43T2P.js} +4 -4
- package/dist/update-agent-MXNOBS4H.js +20 -0
- package/dist/update-dashboard-widget-CJKJLCDY.js +71 -0
- package/dist/update-dashboard-widget-CYQDS2NR.js +70 -0
- package/dist/update-dataset-DQH3OHOU.js +22 -0
- package/dist/{update-dataset-BDUK7TUO.js → update-dataset-SQZ6T4WX.js} +6 -6
- package/dist/{update-dataset-record-4ZSDRBSW.js → update-dataset-record-3AT4IPBC.js} +4 -4
- package/dist/update-dataset-record-7PLDGTMR.js +19 -0
- package/dist/update-evaluator-4LFTKFPT.js +24 -0
- package/dist/{update-evaluator-LS6TIBY7.js → update-evaluator-IGTX6PX4.js} +4 -4
- package/dist/{update-project-LKVYXENH.js → update-project-PLJIOZS6.js} +4 -4
- package/dist/update-project-QVHSJRUP.js +24 -0
- package/dist/update-prompt-GDKRYJI3.js +140 -0
- package/dist/update-prompt-MY263DYF.js +141 -0
- package/dist/update-scenario-7KA3JDXA.js +34 -0
- package/dist/{update-scenario-NEXCAQI7.js → update-scenario-MOHRQKAQ.js} +11 -4
- package/package.json +39 -44
- package/pyproject.toml +8 -8
- package/src/config.d.ts +4 -0
- package/src/config.ts +20 -29
- package/src/create-mcp-server.ts +1021 -596
- package/src/documentation-fetch.ts +21 -20
- package/src/http-security.ts +166 -135
- package/src/http-server.ts +189 -224
- package/src/index.ts +14 -16
- package/src/langwatch-api-agents.ts +202 -21
- package/src/langwatch-api-annotations.ts +11 -12
- package/src/langwatch-api-api-keys.ts +8 -9
- package/src/langwatch-api-boards.ts +46 -0
- package/src/langwatch-api-dashboard-widgets.ts +112 -0
- package/src/langwatch-api-dashboards.ts +13 -10
- package/src/langwatch-api-datasets.ts +98 -32
- package/src/langwatch-api-evaluators.ts +13 -19
- package/src/langwatch-api-model-providers.ts +4 -4
- package/src/langwatch-api-monitors.ts +14 -18
- package/src/langwatch-api-projects.ts +27 -9
- package/src/langwatch-api-query.ts +114 -0
- package/src/langwatch-api-run-plans.ts +140 -0
- package/src/langwatch-api-scenarios.ts +17 -10
- package/src/langwatch-api-secrets.ts +31 -24
- package/src/langwatch-api-simulation-runs.ts +10 -7
- package/src/langwatch-api-test-suites.ts +116 -0
- package/src/langwatch-api-triggers.ts +96 -34
- package/src/langwatch-api-workflows.ts +9 -9
- package/src/langwatch-api.ts +143 -109
- package/src/public-http-request.ts +21 -12
- package/src/schemas/analytics-metrics.ts +3 -15
- package/src/schemas/create-dataset.ts +3 -4
- package/src/schemas/run-plan.ts +76 -0
- package/src/schemas/suite-fields.ts +132 -0
- package/src/schemas/triggers.ts +365 -0
- package/src/tools/add-dashboard-widget.ts +63 -0
- package/src/tools/archive-project.ts +2 -4
- package/src/tools/archive-run-plan.ts +10 -0
- package/src/tools/archive-scenario.ts +2 -4
- package/src/tools/archive-test-suite.ts +10 -0
- package/src/tools/assign-prompt-tag.ts +6 -2
- package/src/tools/create-agent.ts +1 -1
- package/src/tools/create-annotation.ts +7 -2
- package/src/tools/create-api-key.ts +4 -6
- package/src/tools/create-dashboard.ts +1 -1
- package/src/tools/create-dataset-records.ts +4 -8
- package/src/tools/create-dataset.ts +5 -7
- package/src/tools/create-evaluator.ts +3 -5
- package/src/tools/create-project.ts +2 -2
- package/src/tools/create-prompt-tag.ts +2 -4
- package/src/tools/create-prompt.ts +9 -18
- package/src/tools/create-scenario.ts +11 -5
- package/src/tools/create-test-suite.ts +38 -0
- package/src/tools/create-trigger.ts +55 -0
- package/src/tools/dashboard-exists.ts +25 -0
- package/src/tools/dashboard-widget-queries.ts +32 -0
- package/src/tools/delete-agent.ts +1 -1
- package/src/tools/delete-annotation.ts +1 -1
- package/src/tools/delete-dashboard.ts +1 -1
- package/src/tools/delete-dataset-records.ts +4 -8
- package/src/tools/delete-dataset.ts +3 -7
- package/src/tools/delete-evaluator.ts +4 -6
- package/src/tools/delete-prompt-tag.ts +2 -4
- package/src/tools/delete-workflow.ts +1 -1
- package/src/tools/discover-evaluator-schema.ts +34 -25
- package/src/tools/discover-scenario-schema.ts +86 -12
- package/src/tools/discover-schema.ts +149 -27
- package/src/tools/experiment-run-status.ts +2 -8
- package/src/tools/format-dataset-mutation.ts +5 -10
- package/src/tools/format-run-plan.ts +54 -0
- package/src/tools/format-scenario.ts +12 -0
- package/src/tools/format-suite-details.ts +90 -0
- package/src/tools/get-agent.ts +54 -3
- package/src/tools/get-analytics.ts +52 -46
- package/src/tools/get-dashboard.ts +1 -1
- package/src/tools/get-dataset.ts +34 -16
- package/src/tools/get-evaluator.ts +4 -11
- package/src/tools/get-experiment-results.ts +219 -186
- package/src/tools/get-project.ts +2 -4
- package/src/tools/get-prompt.ts +100 -37
- package/src/tools/get-run-plan.ts +60 -0
- package/src/tools/get-scenario.ts +6 -5
- package/src/tools/get-simulation-run.ts +32 -20
- package/src/tools/get-test-suite.ts +46 -0
- package/src/tools/get-trace.ts +16 -17
- package/src/tools/get-workflow.ts +1 -1
- package/src/tools/list-agents.ts +20 -3
- package/src/tools/list-annotations.ts +8 -5
- package/src/tools/list-api-keys.ts +7 -2
- package/src/tools/list-dashboard-boards.ts +28 -0
- package/src/tools/list-dashboards.ts +1 -1
- package/src/tools/list-dataset-records.ts +8 -6
- package/src/tools/list-datasets.ts +12 -23
- package/src/tools/list-evaluators.ts +4 -8
- package/src/tools/list-experiment-runs.ts +5 -9
- package/src/tools/list-experiments.ts +5 -11
- package/src/tools/list-model-providers.ts +3 -5
- package/src/tools/list-projects.ts +2 -4
- package/src/tools/list-prompt-tags.ts +3 -3
- package/src/tools/list-prompts.ts +4 -8
- package/src/tools/list-run-plans.ts +47 -0
- package/src/tools/list-scenarios.ts +26 -17
- package/src/tools/list-simulation-runs.ts +13 -5
- package/src/tools/list-test-suites.ts +35 -0
- package/src/tools/list-trigger-fires.ts +17 -0
- package/src/tools/list-triggers.ts +39 -0
- package/src/tools/list-workflows.ts +2 -4
- package/src/tools/rename-prompt-tag.ts +1 -1
- package/src/tools/rename-test-suite.ts +15 -0
- package/src/tools/report-issue.ts +5 -9
- package/src/tools/rerun-run-plan.ts +18 -0
- package/src/tools/revoke-api-key.ts +2 -4
- package/src/tools/run-agent.ts +65 -14
- package/src/tools/run-experiment.ts +36 -37
- package/src/tools/run-plan.ts +46 -0
- package/src/tools/run-query.ts +47 -0
- package/src/tools/run-test-suite.ts +27 -0
- package/src/tools/run-workflow.ts +2 -5
- package/src/tools/search-traces.ts +161 -47
- package/src/tools/set-model-provider.ts +4 -6
- package/src/tools/test-agent.ts +18 -0
- package/src/tools/update-agent.ts +1 -1
- package/src/tools/update-dashboard-widget.ts +97 -0
- package/src/tools/update-dataset-record.ts +1 -1
- package/src/tools/update-dataset.ts +5 -7
- package/src/tools/update-evaluator.ts +3 -5
- package/src/tools/update-project.ts +1 -1
- package/src/tools/update-prompt.ts +185 -15
- package/src/tools/update-scenario.ts +11 -5
- package/src/tools/update-test-suite.ts +40 -0
- package/src/utils/date-parsing.ts +1 -1
- package/src/utils/escape-markdown.ts +3 -8
- package/src/utils/format-evaluations.ts +6 -6
- package/src/utils/markdown-table.ts +58 -0
- package/src/utils/trace-id-shape.ts +20 -0
- package/tsconfig.app.json +24 -0
- package/tsconfig.json +19 -12
- package/uv.lock +416 -296
- package/vitest.integration.config.js +10 -0
- package/vitest.scenario.config.js +9 -0
- package/.eslintrc.cjs +0 -36
- package/dist/archive-suite-4T6BSRS7.js +0 -16
- package/dist/chunk-77BNWQ3U.js +0 -86
- package/dist/chunk-A2LEVYQ5.js +0 -217
- package/dist/chunk-TJCXFNDT.js +0 -24
- package/dist/chunk-YZLYHLLI.js +0 -48
- package/dist/config.d.ts +0 -17
- package/dist/create-mcp-server.d.ts +0 -10
- package/dist/create-suite-LZS6QIAF.js +0 -39
- package/dist/delete-evaluator-33SP2JFX.js +0 -16
- package/dist/discover-evaluator-schema-XTAD4X7D.js +0 -2191
- package/dist/discover-scenario-schema-FE64QK2D.js +0 -67
- package/dist/get-agent-Y7MWZ65F.js +0 -29
- package/dist/get-analytics-TETCSOK6.js +0 -73
- package/dist/get-dataset-RCMU5VAH.js +0 -49
- package/dist/get-prompt-OY5B7TRI.js +0 -46
- package/dist/get-suite-J7JVWO6O.js +0 -45
- package/dist/index.d.ts +0 -2
- package/dist/langwatch-api-secrets-BUBSNCNO.js +0 -41
- package/dist/langwatch-api-triggers-EDFUCJN6.js +0 -31
- package/dist/list-agents-KFHKGPXV.js +0 -33
- package/dist/list-scenarios-F2WYNRIE.js +0 -41
- package/dist/list-suites-CRY4VWW6.js +0 -43
- package/dist/report-issue-XYVAPAYJ.js +0 -412
- package/dist/run-suite-NKZCJ2IN.js +0 -30
- package/dist/search-traces-A6HFKPLJ.js +0 -79
- package/dist/update-prompt-ODCRQRN6.js +0 -25
- package/dist/update-suite-SNO3GS7T.js +0 -40
- package/src/__tests__/all-tools.integration.test.ts +0 -1626
- package/src/__tests__/config.unit.test.ts +0 -202
- package/src/__tests__/datasets.integration.test.ts +0 -620
- package/src/__tests__/datasets.unit.test.ts +0 -244
- package/src/__tests__/date-parsing.unit.test.ts +0 -78
- package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
- package/src/__tests__/discover-schema.unit.test.ts +0 -118
- package/src/__tests__/docker.integration.test.ts +0 -318
- package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
- package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
- package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
- package/src/__tests__/experiment-status.unit.test.ts +0 -194
- package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
- package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
- package/src/__tests__/http-security-origin.unit.test.ts +0 -97
- package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
- package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
- package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
- package/src/__tests__/http-server-origin.integration.test.ts +0 -169
- package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
- package/src/__tests__/http-transport.integration.test.ts +0 -395
- package/src/__tests__/integration.integration.test.ts +0 -288
- package/src/__tests__/langwatch-api.unit.test.ts +0 -661
- package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
- package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
- package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
- package/src/__tests__/report-issue.integration.test.ts +0 -147
- package/src/__tests__/scenario-tools.integration.test.ts +0 -286
- package/src/__tests__/scenario-tools.unit.test.ts +0 -188
- package/src/__tests__/schemas.unit.test.ts +0 -85
- package/src/__tests__/support/http-server-harness.ts +0 -138
- package/src/__tests__/tools.unit.test.ts +0 -924
- package/src/langwatch-api-suites.ts +0 -118
- package/src/schemas/filter-fields.ts +0 -119
- package/src/schemas/index.ts +0 -4
- package/src/tools/archive-suite.ts +0 -12
- package/src/tools/create-suite.ts +0 -42
- package/src/tools/get-suite.ts +0 -47
- package/src/tools/list-suites.ts +0 -43
- package/src/tools/run-suite.ts +0 -28
- package/src/tools/update-suite.ts +0 -44
|
@@ -1,1626 +0,0 @@
|
|
|
1
|
-
import { createServer, type Server } from "http";
|
|
2
|
-
import { afterAll, beforeAll, describe, expect, it, vi, beforeEach } from "vitest";
|
|
3
|
-
import { initConfig } from "../config.js";
|
|
4
|
-
import {
|
|
5
|
-
fetchDocumentation,
|
|
6
|
-
resolveDocumentationUrl,
|
|
7
|
-
} from "../documentation-fetch.js";
|
|
8
|
-
|
|
9
|
-
// --- Canned responses for every API endpoint ---
|
|
10
|
-
|
|
11
|
-
const CANNED_TRACES_SEARCH = {
|
|
12
|
-
traces: [
|
|
13
|
-
{
|
|
14
|
-
trace_id: "trace-001",
|
|
15
|
-
formatted_trace:
|
|
16
|
-
"Root [server] 1200ms\n LLM Call [llm] 500ms\n Input: Hello, how are you?\n Output: I am fine, thank you!",
|
|
17
|
-
input: { value: "Hello, how are you?" },
|
|
18
|
-
output: { value: "I am fine, thank you!" },
|
|
19
|
-
timestamps: { started_at: 1700000000000 },
|
|
20
|
-
metadata: { user_id: "user-42", thread_id: "thread-1" },
|
|
21
|
-
evaluations: [
|
|
22
|
-
{
|
|
23
|
-
evaluator_id: "eval-1",
|
|
24
|
-
name: "Faithfulness",
|
|
25
|
-
score: 0.95,
|
|
26
|
-
passed: true,
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
pagination: { totalHits: 1 },
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const CANNED_TRACES_SEARCH_WITH_SCROLL = {
|
|
35
|
-
traces: [
|
|
36
|
-
{
|
|
37
|
-
trace_id: "trace-page-1",
|
|
38
|
-
input: { value: "Page 1 input" },
|
|
39
|
-
output: { value: "Page 1 output" },
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
pagination: { totalHits: 50, scrollId: "scroll-token-abc" },
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const CANNED_TRACES_EMPTY = {
|
|
46
|
-
traces: [],
|
|
47
|
-
pagination: { totalHits: 0 },
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const CANNED_TRACE_DETAIL = {
|
|
51
|
-
trace_id: "trace-001",
|
|
52
|
-
formatted_trace:
|
|
53
|
-
"Root [server] 1200ms\n LLM Call [llm] 500ms\n Input: Hello\n Output: Hi there",
|
|
54
|
-
timestamps: {
|
|
55
|
-
started_at: 1700000000000,
|
|
56
|
-
updated_at: 1700000001000,
|
|
57
|
-
inserted_at: 1700000001000,
|
|
58
|
-
},
|
|
59
|
-
metadata: {
|
|
60
|
-
user_id: "user-42",
|
|
61
|
-
thread_id: "thread-1",
|
|
62
|
-
customer_id: "cust-100",
|
|
63
|
-
labels: ["production"],
|
|
64
|
-
},
|
|
65
|
-
evaluations: [
|
|
66
|
-
{
|
|
67
|
-
evaluator_id: "eval-1",
|
|
68
|
-
name: "Faithfulness",
|
|
69
|
-
score: 0.95,
|
|
70
|
-
passed: true,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const CANNED_ANALYTICS = {
|
|
76
|
-
currentPeriod: [
|
|
77
|
-
{ date: "2024-01-01", "0__trace_id_cardinality": 42 },
|
|
78
|
-
{ date: "2024-01-02", "0__trace_id_cardinality": 58 },
|
|
79
|
-
],
|
|
80
|
-
previousPeriod: [],
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const CANNED_PROMPTS_LIST = [
|
|
84
|
-
{
|
|
85
|
-
id: "p1",
|
|
86
|
-
handle: "greeting-bot",
|
|
87
|
-
name: "Greeting Bot",
|
|
88
|
-
latestVersionNumber: 3,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
id: "p2",
|
|
92
|
-
handle: "qa-assistant",
|
|
93
|
-
name: "QA Assistant",
|
|
94
|
-
latestVersionNumber: 1,
|
|
95
|
-
},
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
const CANNED_PROMPT_DETAIL = {
|
|
99
|
-
id: "p1",
|
|
100
|
-
handle: "greeting-bot",
|
|
101
|
-
name: "Greeting Bot",
|
|
102
|
-
latestVersionNumber: 3,
|
|
103
|
-
versions: [
|
|
104
|
-
{
|
|
105
|
-
version: 3,
|
|
106
|
-
commitMessage: "Updated tone",
|
|
107
|
-
model: "openai/gpt-4o",
|
|
108
|
-
messages: [{ role: "system", content: "You are a friendly bot." }],
|
|
109
|
-
},
|
|
110
|
-
{ version: 2, commitMessage: "Added greeting" },
|
|
111
|
-
{ version: 1, commitMessage: "Initial version" },
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const CANNED_PROMPT_CREATED = {
|
|
116
|
-
id: "p-new",
|
|
117
|
-
handle: "new-prompt",
|
|
118
|
-
name: "New Prompt",
|
|
119
|
-
latestVersionNumber: 1,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const CANNED_PROMPT_UPDATED = {
|
|
123
|
-
id: "p1",
|
|
124
|
-
handle: "greeting-bot",
|
|
125
|
-
latestVersionNumber: 4,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const CANNED_SCENARIOS_LIST = [
|
|
129
|
-
{
|
|
130
|
-
id: "scen_abc123",
|
|
131
|
-
name: "Login Flow Happy Path",
|
|
132
|
-
situation: "User attempts to log in with valid credentials",
|
|
133
|
-
criteria: [
|
|
134
|
-
"Responds with a welcome message",
|
|
135
|
-
"Includes user name in greeting",
|
|
136
|
-
],
|
|
137
|
-
labels: ["auth", "happy-path"],
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
id: "scen_def456",
|
|
141
|
-
name: "Password Reset",
|
|
142
|
-
situation: "User requests a password reset link",
|
|
143
|
-
criteria: ["Sends reset email"],
|
|
144
|
-
labels: ["auth"],
|
|
145
|
-
},
|
|
146
|
-
];
|
|
147
|
-
|
|
148
|
-
const CANNED_SCENARIO_DETAIL = {
|
|
149
|
-
id: "scen_abc123",
|
|
150
|
-
name: "Login Flow Happy Path",
|
|
151
|
-
situation: "User attempts to log in with valid credentials",
|
|
152
|
-
criteria: [
|
|
153
|
-
"Responds with a welcome message",
|
|
154
|
-
"Includes user name in greeting",
|
|
155
|
-
],
|
|
156
|
-
labels: ["auth", "happy-path"],
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const CANNED_SCENARIO_CREATED = {
|
|
160
|
-
id: "scen_new789",
|
|
161
|
-
name: "New Scenario",
|
|
162
|
-
situation: "User does something",
|
|
163
|
-
criteria: ["Agent responds correctly"],
|
|
164
|
-
labels: ["test"],
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const CANNED_SCENARIO_UPDATED = {
|
|
168
|
-
id: "scen_abc123",
|
|
169
|
-
name: "Login Flow - Updated",
|
|
170
|
-
situation: "User logs in with correct email and pass",
|
|
171
|
-
criteria: [
|
|
172
|
-
"Responds with welcome message",
|
|
173
|
-
"Sets session cookie",
|
|
174
|
-
"Redirects to dashboard",
|
|
175
|
-
],
|
|
176
|
-
labels: ["auth", "happy-path"],
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const CANNED_SCENARIO_ARCHIVED = {
|
|
180
|
-
id: "scen_abc123",
|
|
181
|
-
archived: true,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const CANNED_EVALUATORS_LIST = [
|
|
185
|
-
{
|
|
186
|
-
id: "evaluator_abc123",
|
|
187
|
-
projectId: "proj_1",
|
|
188
|
-
name: "Toxicity Check",
|
|
189
|
-
slug: "toxicity-check",
|
|
190
|
-
type: "evaluator",
|
|
191
|
-
config: { evaluatorType: "openai/moderation" },
|
|
192
|
-
workflowId: null,
|
|
193
|
-
copiedFromEvaluatorId: null,
|
|
194
|
-
createdAt: "2024-01-01T00:00:00.000Z",
|
|
195
|
-
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
196
|
-
fields: [{ identifier: "input", type: "str" }],
|
|
197
|
-
outputFields: [{ identifier: "passed", type: "bool" }],
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: "evaluator_def456",
|
|
201
|
-
projectId: "proj_1",
|
|
202
|
-
name: "Exact Match",
|
|
203
|
-
slug: "exact-match",
|
|
204
|
-
type: "evaluator",
|
|
205
|
-
config: { evaluatorType: "langevals/exact_match" },
|
|
206
|
-
workflowId: null,
|
|
207
|
-
copiedFromEvaluatorId: null,
|
|
208
|
-
createdAt: "2024-01-02T00:00:00.000Z",
|
|
209
|
-
updatedAt: "2024-01-02T00:00:00.000Z",
|
|
210
|
-
fields: [
|
|
211
|
-
{ identifier: "output", type: "str" },
|
|
212
|
-
{ identifier: "expected_output", type: "str" },
|
|
213
|
-
],
|
|
214
|
-
outputFields: [{ identifier: "passed", type: "bool" }],
|
|
215
|
-
},
|
|
216
|
-
];
|
|
217
|
-
|
|
218
|
-
const CANNED_EVALUATOR_DETAIL = {
|
|
219
|
-
id: "evaluator_abc123",
|
|
220
|
-
projectId: "proj_1",
|
|
221
|
-
name: "Toxicity Check",
|
|
222
|
-
slug: "toxicity-check",
|
|
223
|
-
type: "evaluator",
|
|
224
|
-
config: {
|
|
225
|
-
evaluatorType: "openai/moderation",
|
|
226
|
-
settings: { model: "text-moderation-stable" },
|
|
227
|
-
},
|
|
228
|
-
workflowId: null,
|
|
229
|
-
copiedFromEvaluatorId: null,
|
|
230
|
-
createdAt: "2024-01-01T00:00:00.000Z",
|
|
231
|
-
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
232
|
-
fields: [
|
|
233
|
-
{ identifier: "input", type: "str" },
|
|
234
|
-
{ identifier: "output", type: "str", optional: true },
|
|
235
|
-
],
|
|
236
|
-
outputFields: [
|
|
237
|
-
{ identifier: "passed", type: "bool" },
|
|
238
|
-
{ identifier: "score", type: "float" },
|
|
239
|
-
],
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const CANNED_EVALUATOR_CREATED = {
|
|
243
|
-
id: "evaluator_new123",
|
|
244
|
-
projectId: "proj_1",
|
|
245
|
-
name: "My LLM Judge",
|
|
246
|
-
slug: "my-llm-judge",
|
|
247
|
-
type: "evaluator",
|
|
248
|
-
config: { evaluatorType: "langevals/llm_boolean" },
|
|
249
|
-
workflowId: null,
|
|
250
|
-
copiedFromEvaluatorId: null,
|
|
251
|
-
createdAt: "2024-01-01T00:00:00.000Z",
|
|
252
|
-
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
253
|
-
fields: [{ identifier: "input", type: "str" }],
|
|
254
|
-
outputFields: [{ identifier: "passed", type: "bool" }],
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const CANNED_EVALUATOR_UPDATED = {
|
|
258
|
-
id: "evaluator_abc123",
|
|
259
|
-
projectId: "proj_1",
|
|
260
|
-
name: "Updated Toxicity",
|
|
261
|
-
slug: "toxicity-check",
|
|
262
|
-
type: "evaluator",
|
|
263
|
-
config: { evaluatorType: "openai/moderation" },
|
|
264
|
-
workflowId: null,
|
|
265
|
-
copiedFromEvaluatorId: null,
|
|
266
|
-
createdAt: "2024-01-01T00:00:00.000Z",
|
|
267
|
-
updatedAt: "2024-01-02T00:00:00.000Z",
|
|
268
|
-
fields: [],
|
|
269
|
-
outputFields: [],
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const CANNED_MODEL_PROVIDERS_LIST = {
|
|
273
|
-
openai: {
|
|
274
|
-
provider: "openai",
|
|
275
|
-
enabled: true,
|
|
276
|
-
customKeys: { OPENAI_API_KEY: "HAS_KEY" },
|
|
277
|
-
models: ["gpt-4o", "gpt-4o-mini"],
|
|
278
|
-
embeddingsModels: ["text-embedding-3-small"],
|
|
279
|
-
deploymentMapping: null,
|
|
280
|
-
extraHeaders: [],
|
|
281
|
-
},
|
|
282
|
-
anthropic: {
|
|
283
|
-
provider: "anthropic",
|
|
284
|
-
enabled: false,
|
|
285
|
-
customKeys: null,
|
|
286
|
-
models: ["claude-sonnet-4-5-20250929"],
|
|
287
|
-
embeddingsModels: null,
|
|
288
|
-
deploymentMapping: null,
|
|
289
|
-
extraHeaders: [],
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
const CANNED_AGENTS_LIST = {
|
|
294
|
-
data: [
|
|
295
|
-
{ id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
|
|
296
|
-
],
|
|
297
|
-
pagination: { page: 1, limit: 50, total: 1, totalPages: 1 },
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const CANNED_AGENT_DETAIL = {
|
|
301
|
-
id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const CANNED_SUITES_LIST = [
|
|
305
|
-
{ id: "suite_abc", name: "Regression Suite", slug: "regression-suite", description: null, scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
|
|
306
|
-
];
|
|
307
|
-
|
|
308
|
-
const CANNED_SUITE_DETAIL = {
|
|
309
|
-
id: "suite_abc", name: "Regression Suite", slug: "regression-suite", description: "A test suite", scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const CANNED_SUITE_CREATED = {
|
|
313
|
-
id: "suite_new", name: "New Suite", slug: "new-suite", description: null, scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 1, labels: [], createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const CANNED_SUITE_RUN = {
|
|
317
|
-
scheduled: true, batchRunId: "batch_123", setId: "set_456", jobCount: 1, skippedArchived: { scenarios: [], targets: [] }, items: [{ scenarioRunId: "run_1", scenarioId: "scen_abc123", target: { type: "http", referenceId: "agent_abc" }, name: "Test" }],
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const CANNED_SIMULATION_RUNS = {
|
|
321
|
-
runs: [{ scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, timestamp: 1700000000000, updatedAt: 1700000001000 }],
|
|
322
|
-
hasMore: false,
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const CANNED_SIMULATION_RUN_DETAIL = {
|
|
326
|
-
scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, results: { verdict: "passed", reasoning: "All criteria met", metCriteria: ["Greets user"], unmetCriteria: [], error: null }, messages: [{ role: "user", content: "Hello" }, { role: "assistant", content: "Hi there!" }], timestamp: 1700000000000, updatedAt: 1700000001000,
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
const CANNED_DASHBOARDS_LIST = {
|
|
330
|
-
data: [{ id: "dash_abc", name: "Main Dashboard", order: 0, graphCount: 3, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" }],
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
const CANNED_WORKFLOWS_LIST = [
|
|
334
|
-
{ id: "wf_abc", name: "Test Workflow", icon: null, description: "A workflow", isEvaluator: false, isComponent: false, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
|
|
335
|
-
];
|
|
336
|
-
|
|
337
|
-
const CANNED_MODEL_PROVIDER_SET = {
|
|
338
|
-
openai: {
|
|
339
|
-
provider: "openai",
|
|
340
|
-
enabled: true,
|
|
341
|
-
customKeys: { OPENAI_API_KEY: "HAS_KEY" },
|
|
342
|
-
models: ["gpt-4o"],
|
|
343
|
-
embeddingsModels: null,
|
|
344
|
-
deploymentMapping: null,
|
|
345
|
-
extraHeaders: [],
|
|
346
|
-
},
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// --- Mock HTTP Server that handles ALL MCP API endpoints ---
|
|
350
|
-
|
|
351
|
-
/** Track last request for each route so tests can assert on request body/params. */
|
|
352
|
-
const lastRequests: Record<string, { method: string; url: string; body: string }> = {};
|
|
353
|
-
|
|
354
|
-
function createMockServer(): Server {
|
|
355
|
-
return createServer((req, res) => {
|
|
356
|
-
const authToken = req.headers["x-auth-token"];
|
|
357
|
-
if (authToken !== "test-integration-key") {
|
|
358
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
359
|
-
res.end(JSON.stringify({ message: "Invalid auth token." }));
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
let body = "";
|
|
364
|
-
req.on("data", (chunk: string) => (body += chunk));
|
|
365
|
-
req.on("end", () => {
|
|
366
|
-
const url = req.url ?? "";
|
|
367
|
-
const method = req.method ?? "GET";
|
|
368
|
-
res.setHeader("Content-Type", "application/json");
|
|
369
|
-
|
|
370
|
-
// Store last request for assertions
|
|
371
|
-
const routeKey = `${method} ${url.split("?")[0]}`;
|
|
372
|
-
lastRequests[routeKey] = { method, url, body };
|
|
373
|
-
|
|
374
|
-
// --- Trace endpoints ---
|
|
375
|
-
if (url === "/api/traces/search" && method === "POST") {
|
|
376
|
-
const parsed = JSON.parse(body);
|
|
377
|
-
// Return empty results when a special query is used
|
|
378
|
-
if (parsed.query === "__empty__") {
|
|
379
|
-
res.writeHead(200);
|
|
380
|
-
res.end(JSON.stringify(CANNED_TRACES_EMPTY));
|
|
381
|
-
} else if (parsed.pageSize === 5) {
|
|
382
|
-
res.writeHead(200);
|
|
383
|
-
res.end(JSON.stringify(CANNED_TRACES_SEARCH_WITH_SCROLL));
|
|
384
|
-
} else {
|
|
385
|
-
res.writeHead(200);
|
|
386
|
-
res.end(JSON.stringify(CANNED_TRACES_SEARCH));
|
|
387
|
-
}
|
|
388
|
-
} else if (
|
|
389
|
-
url.match(/^\/api\/traces\/trace-nonexistent(\?|$)/) &&
|
|
390
|
-
method === "GET"
|
|
391
|
-
) {
|
|
392
|
-
res.writeHead(404);
|
|
393
|
-
res.end(JSON.stringify({ message: "Trace not found" }));
|
|
394
|
-
} else if (
|
|
395
|
-
url.match(/^\/api\/traces\/[^/]+(\?|$)/) &&
|
|
396
|
-
method === "GET"
|
|
397
|
-
) {
|
|
398
|
-
res.writeHead(200);
|
|
399
|
-
res.end(JSON.stringify(CANNED_TRACE_DETAIL));
|
|
400
|
-
}
|
|
401
|
-
// --- Analytics endpoint ---
|
|
402
|
-
else if (url === "/api/analytics/timeseries" && method === "POST") {
|
|
403
|
-
res.writeHead(200);
|
|
404
|
-
res.end(JSON.stringify(CANNED_ANALYTICS));
|
|
405
|
-
}
|
|
406
|
-
// --- Prompt endpoints ---
|
|
407
|
-
else if (url === "/api/prompts" && method === "GET") {
|
|
408
|
-
res.writeHead(200);
|
|
409
|
-
res.end(JSON.stringify(CANNED_PROMPTS_LIST));
|
|
410
|
-
} else if (url === "/api/prompts" && method === "POST") {
|
|
411
|
-
res.writeHead(200);
|
|
412
|
-
res.end(JSON.stringify(CANNED_PROMPT_CREATED));
|
|
413
|
-
} else if (
|
|
414
|
-
url.match(/^\/api\/prompts\/[^/]+$/) &&
|
|
415
|
-
method === "GET"
|
|
416
|
-
) {
|
|
417
|
-
res.writeHead(200);
|
|
418
|
-
res.end(JSON.stringify(CANNED_PROMPT_DETAIL));
|
|
419
|
-
} else if (
|
|
420
|
-
url.match(/^\/api\/prompts\/[^/]+$/) &&
|
|
421
|
-
method === "PUT"
|
|
422
|
-
) {
|
|
423
|
-
res.writeHead(200);
|
|
424
|
-
res.end(JSON.stringify(CANNED_PROMPT_UPDATED));
|
|
425
|
-
}
|
|
426
|
-
// --- Scenario endpoints ---
|
|
427
|
-
else if (url === "/api/scenarios" && method === "GET") {
|
|
428
|
-
res.writeHead(200);
|
|
429
|
-
res.end(JSON.stringify(CANNED_SCENARIOS_LIST));
|
|
430
|
-
} else if (url === "/api/scenarios" && method === "POST") {
|
|
431
|
-
res.writeHead(200);
|
|
432
|
-
res.end(JSON.stringify(CANNED_SCENARIO_CREATED));
|
|
433
|
-
} else if (
|
|
434
|
-
url.match(/^\/api\/scenarios\/scen_nonexistent(\?|$)/) &&
|
|
435
|
-
method === "GET"
|
|
436
|
-
) {
|
|
437
|
-
res.writeHead(404);
|
|
438
|
-
res.end(JSON.stringify({ message: "Scenario not found" }));
|
|
439
|
-
} else if (
|
|
440
|
-
url.match(/^\/api\/scenarios\/[^/]+$/) &&
|
|
441
|
-
method === "GET"
|
|
442
|
-
) {
|
|
443
|
-
res.writeHead(200);
|
|
444
|
-
res.end(JSON.stringify(CANNED_SCENARIO_DETAIL));
|
|
445
|
-
} else if (
|
|
446
|
-
url.match(/^\/api\/scenarios\/[^/]+$/) &&
|
|
447
|
-
method === "PUT"
|
|
448
|
-
) {
|
|
449
|
-
res.writeHead(200);
|
|
450
|
-
res.end(JSON.stringify(CANNED_SCENARIO_UPDATED));
|
|
451
|
-
} else if (
|
|
452
|
-
url.match(/^\/api\/scenarios\/[^/]+$/) &&
|
|
453
|
-
method === "DELETE"
|
|
454
|
-
) {
|
|
455
|
-
res.writeHead(200);
|
|
456
|
-
res.end(JSON.stringify(CANNED_SCENARIO_ARCHIVED));
|
|
457
|
-
}
|
|
458
|
-
// --- Evaluator endpoints ---
|
|
459
|
-
else if (url === "/api/evaluators" && method === "GET") {
|
|
460
|
-
res.writeHead(200);
|
|
461
|
-
res.end(JSON.stringify(CANNED_EVALUATORS_LIST));
|
|
462
|
-
} else if (url === "/api/evaluators" && method === "POST") {
|
|
463
|
-
res.writeHead(200);
|
|
464
|
-
res.end(JSON.stringify(CANNED_EVALUATOR_CREATED));
|
|
465
|
-
} else if (
|
|
466
|
-
url.match(/^\/api\/evaluators\/evaluator_nonexistent(\?|$)/) &&
|
|
467
|
-
method === "GET"
|
|
468
|
-
) {
|
|
469
|
-
res.writeHead(404);
|
|
470
|
-
res.end(JSON.stringify({ message: "Evaluator not found" }));
|
|
471
|
-
} else if (
|
|
472
|
-
url.match(/^\/api\/evaluators\/[^/]+$/) &&
|
|
473
|
-
method === "GET"
|
|
474
|
-
) {
|
|
475
|
-
res.writeHead(200);
|
|
476
|
-
res.end(JSON.stringify(CANNED_EVALUATOR_DETAIL));
|
|
477
|
-
} else if (
|
|
478
|
-
url.match(/^\/api\/evaluators\/[^/]+$/) &&
|
|
479
|
-
method === "PUT"
|
|
480
|
-
) {
|
|
481
|
-
res.writeHead(200);
|
|
482
|
-
res.end(JSON.stringify(CANNED_EVALUATOR_UPDATED));
|
|
483
|
-
}
|
|
484
|
-
// --- Model Provider endpoints ---
|
|
485
|
-
else if (url === "/api/model-providers" && method === "GET") {
|
|
486
|
-
res.writeHead(200);
|
|
487
|
-
res.end(JSON.stringify(CANNED_MODEL_PROVIDERS_LIST));
|
|
488
|
-
} else if (
|
|
489
|
-
url.match(/^\/api\/model-providers\/[^/]+$/) &&
|
|
490
|
-
method === "PUT"
|
|
491
|
-
) {
|
|
492
|
-
res.writeHead(200);
|
|
493
|
-
res.end(JSON.stringify(CANNED_MODEL_PROVIDER_SET));
|
|
494
|
-
}
|
|
495
|
-
// --- Agent endpoints ---
|
|
496
|
-
else if (url === "/api/agents" && method === "GET") {
|
|
497
|
-
res.writeHead(200);
|
|
498
|
-
res.end(JSON.stringify(CANNED_AGENTS_LIST));
|
|
499
|
-
} else if (url === "/api/agents" && method === "POST") {
|
|
500
|
-
res.writeHead(201);
|
|
501
|
-
res.end(JSON.stringify(CANNED_AGENT_DETAIL));
|
|
502
|
-
} else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "GET") {
|
|
503
|
-
res.writeHead(200);
|
|
504
|
-
res.end(JSON.stringify(CANNED_AGENT_DETAIL));
|
|
505
|
-
} else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "PATCH") {
|
|
506
|
-
res.writeHead(200);
|
|
507
|
-
res.end(JSON.stringify(CANNED_AGENT_DETAIL));
|
|
508
|
-
} else if (url?.match(/^\/api\/agents\/[^/]+$/) && method === "DELETE") {
|
|
509
|
-
res.writeHead(200);
|
|
510
|
-
res.end(JSON.stringify({ id: "agent_abc", name: "Test Agent" }));
|
|
511
|
-
}
|
|
512
|
-
// --- Suite endpoints ---
|
|
513
|
-
else if (url === "/api/suites" && method === "GET") {
|
|
514
|
-
res.writeHead(200);
|
|
515
|
-
res.end(JSON.stringify(CANNED_SUITES_LIST));
|
|
516
|
-
} else if (url === "/api/suites" && method === "POST") {
|
|
517
|
-
res.writeHead(201);
|
|
518
|
-
res.end(JSON.stringify(CANNED_SUITE_CREATED));
|
|
519
|
-
} else if (url?.match(/^\/api\/suites\/[^/]+\/run$/) && method === "POST") {
|
|
520
|
-
res.writeHead(200);
|
|
521
|
-
res.end(JSON.stringify(CANNED_SUITE_RUN));
|
|
522
|
-
} else if (url?.match(/^\/api\/suites\/[^/]+\/duplicate$/) && method === "POST") {
|
|
523
|
-
res.writeHead(201);
|
|
524
|
-
res.end(JSON.stringify(CANNED_SUITE_CREATED));
|
|
525
|
-
} else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "GET") {
|
|
526
|
-
res.writeHead(200);
|
|
527
|
-
res.end(JSON.stringify(CANNED_SUITE_DETAIL));
|
|
528
|
-
} else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "PATCH") {
|
|
529
|
-
res.writeHead(200);
|
|
530
|
-
res.end(JSON.stringify(CANNED_SUITE_DETAIL));
|
|
531
|
-
} else if (url?.match(/^\/api\/suites\/[^/]+$/) && method === "DELETE") {
|
|
532
|
-
res.writeHead(200);
|
|
533
|
-
res.end(JSON.stringify({ id: "suite_abc", archived: true }));
|
|
534
|
-
}
|
|
535
|
-
// --- Simulation Run endpoints ---
|
|
536
|
-
else if (url?.match(/^\/api\/simulation-runs\/[^/]+$/) && method === "GET") {
|
|
537
|
-
res.writeHead(200);
|
|
538
|
-
res.end(JSON.stringify(CANNED_SIMULATION_RUN_DETAIL));
|
|
539
|
-
} else if (url?.match(/^\/api\/simulation-runs/) && method === "GET") {
|
|
540
|
-
res.writeHead(200);
|
|
541
|
-
res.end(JSON.stringify(CANNED_SIMULATION_RUNS));
|
|
542
|
-
}
|
|
543
|
-
// --- Dashboard endpoints ---
|
|
544
|
-
else if (url === "/api/dashboards" && method === "GET") {
|
|
545
|
-
res.writeHead(200);
|
|
546
|
-
res.end(JSON.stringify(CANNED_DASHBOARDS_LIST));
|
|
547
|
-
} else if (url === "/api/dashboards" && method === "POST") {
|
|
548
|
-
res.writeHead(201);
|
|
549
|
-
res.end(JSON.stringify({ id: "dash_new", name: "New Dashboard" }));
|
|
550
|
-
} else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "GET") {
|
|
551
|
-
res.writeHead(200);
|
|
552
|
-
res.end(JSON.stringify(CANNED_DASHBOARDS_LIST.data[0]));
|
|
553
|
-
} else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "DELETE") {
|
|
554
|
-
res.writeHead(200);
|
|
555
|
-
res.end(JSON.stringify({ id: "dash_abc", name: "Main Dashboard" }));
|
|
556
|
-
}
|
|
557
|
-
// --- Workflow endpoints ---
|
|
558
|
-
else if (url === "/api/workflows" && method === "GET") {
|
|
559
|
-
res.writeHead(200);
|
|
560
|
-
res.end(JSON.stringify(CANNED_WORKFLOWS_LIST));
|
|
561
|
-
} else if (url?.match(/^\/api\/workflows\/[^/]+\/run$/) && method === "POST") {
|
|
562
|
-
res.writeHead(200);
|
|
563
|
-
res.end(JSON.stringify({ output: "workflow result" }));
|
|
564
|
-
} else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "GET") {
|
|
565
|
-
res.writeHead(200);
|
|
566
|
-
res.end(JSON.stringify(CANNED_WORKFLOWS_LIST[0]));
|
|
567
|
-
} else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "DELETE") {
|
|
568
|
-
res.writeHead(200);
|
|
569
|
-
res.end(JSON.stringify({ id: "wf_abc", archived: true }));
|
|
570
|
-
}
|
|
571
|
-
// --- Monitor endpoints ---
|
|
572
|
-
else if (url === "/api/monitors" && method === "GET") {
|
|
573
|
-
res.writeHead(200);
|
|
574
|
-
res.end(JSON.stringify([
|
|
575
|
-
{ id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
|
576
|
-
]));
|
|
577
|
-
} else if (url === "/api/monitors" && method === "POST") {
|
|
578
|
-
res.writeHead(201);
|
|
579
|
-
res.end(JSON.stringify({ id: "mon_new", name: "New Monitor", slug: "new-monitor-abc12", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
|
|
580
|
-
} else if (url?.match(/^\/api\/monitors\/[^/]+\/toggle$/) && method === "POST") {
|
|
581
|
-
const monitorId = url?.split("/")[3];
|
|
582
|
-
res.writeHead(200);
|
|
583
|
-
res.end(JSON.stringify({ id: monitorId, enabled: true }));
|
|
584
|
-
} else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "GET") {
|
|
585
|
-
res.writeHead(200);
|
|
586
|
-
res.end(JSON.stringify({ id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
|
|
587
|
-
} else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "PATCH") {
|
|
588
|
-
res.writeHead(200);
|
|
589
|
-
res.end(JSON.stringify({ id: "mon_abc", name: "Updated Monitor", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: false, executionMode: "ON_MESSAGE", sample: 0.5, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" }));
|
|
590
|
-
} else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "DELETE") {
|
|
591
|
-
const monitorId = url?.split("/").pop();
|
|
592
|
-
res.writeHead(200);
|
|
593
|
-
res.end(JSON.stringify({ id: monitorId, deleted: true }));
|
|
594
|
-
}
|
|
595
|
-
// --- Secret endpoints ---
|
|
596
|
-
else if (url === "/api/secrets" && method === "GET") {
|
|
597
|
-
res.writeHead(200);
|
|
598
|
-
res.end(JSON.stringify([
|
|
599
|
-
{ id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
|
600
|
-
{ id: "secret_def", projectId: "proj_123", name: "DB_PASSWORD", createdAt: "2026-01-02T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" },
|
|
601
|
-
]));
|
|
602
|
-
} else if (url === "/api/secrets" && method === "POST") {
|
|
603
|
-
res.writeHead(201);
|
|
604
|
-
res.end(JSON.stringify({ id: "secret_new", projectId: "proj_123", name: "NEW_SECRET", createdAt: "2026-01-03T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
|
|
605
|
-
} else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "PUT") {
|
|
606
|
-
res.writeHead(200);
|
|
607
|
-
res.end(JSON.stringify({ id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
|
|
608
|
-
} else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "DELETE") {
|
|
609
|
-
const secretId = url?.split("/").pop();
|
|
610
|
-
res.writeHead(200);
|
|
611
|
-
res.end(JSON.stringify({ id: secretId, deleted: true }));
|
|
612
|
-
}
|
|
613
|
-
// --- Fallback ---
|
|
614
|
-
else {
|
|
615
|
-
res.writeHead(404);
|
|
616
|
-
res.end(
|
|
617
|
-
JSON.stringify({ message: `Not found: ${method} ${url}` }),
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// --- Integration Tests ---
|
|
625
|
-
// These verify that every MCP tool handler correctly communicates with the REST API
|
|
626
|
-
// through a real HTTP server. Formatting/digest assertions ensure the full chain works.
|
|
627
|
-
|
|
628
|
-
describe("All MCP tools integration", () => {
|
|
629
|
-
let server: Server;
|
|
630
|
-
let port: number;
|
|
631
|
-
|
|
632
|
-
beforeAll(async () => {
|
|
633
|
-
server = createMockServer();
|
|
634
|
-
await new Promise<void>((resolve) => {
|
|
635
|
-
server.listen(0, () => {
|
|
636
|
-
const addr = server.address();
|
|
637
|
-
port = typeof addr === "object" && addr ? addr.port : 0;
|
|
638
|
-
initConfig({
|
|
639
|
-
apiKey: "test-integration-key",
|
|
640
|
-
endpoint: `http://localhost:${port}`,
|
|
641
|
-
});
|
|
642
|
-
resolve();
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
afterAll(async () => {
|
|
648
|
-
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
// =====================
|
|
652
|
-
// 1. fetch_langwatch_docs
|
|
653
|
-
// =====================
|
|
654
|
-
describe("fetch_langwatch_docs", () => {
|
|
655
|
-
it("resolves the trusted docs index", () => {
|
|
656
|
-
expect(resolveDocumentationUrl("langwatch").toString()).toBe(
|
|
657
|
-
"https://langwatch.ai/docs/llms.txt",
|
|
658
|
-
);
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
it("fetches trusted docs without following redirects", async () => {
|
|
662
|
-
const mockFetch = vi.fn().mockResolvedValue(
|
|
663
|
-
new Response("# Integration Guide"),
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
const text = await fetchDocumentation(
|
|
667
|
-
"langwatch",
|
|
668
|
-
"integration",
|
|
669
|
-
mockFetch,
|
|
670
|
-
);
|
|
671
|
-
|
|
672
|
-
expect(text).toContain("Integration Guide");
|
|
673
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
674
|
-
new URL("https://langwatch.ai/docs/integration.md"),
|
|
675
|
-
{
|
|
676
|
-
redirect: "error",
|
|
677
|
-
signal: expect.any(AbortSignal),
|
|
678
|
-
},
|
|
679
|
-
);
|
|
680
|
-
});
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
// =====================
|
|
684
|
-
// 2. fetch_scenario_docs
|
|
685
|
-
// =====================
|
|
686
|
-
describe("fetch_scenario_docs", () => {
|
|
687
|
-
it("resolves the trusted Scenario docs index", () => {
|
|
688
|
-
expect(resolveDocumentationUrl("scenario").toString()).toBe(
|
|
689
|
-
"https://langwatch.ai/scenario/llms.txt",
|
|
690
|
-
);
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
it("normalizes a relative Scenario docs path", () => {
|
|
694
|
-
expect(resolveDocumentationUrl("scenario", "setup").toString()).toBe(
|
|
695
|
-
"https://langwatch.ai/scenario/setup.md",
|
|
696
|
-
);
|
|
697
|
-
});
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
// =====================
|
|
701
|
-
// 3. discover_schema
|
|
702
|
-
// =====================
|
|
703
|
-
describe("discover_schema", () => {
|
|
704
|
-
describe("when category is filters", () => {
|
|
705
|
-
/** @scenario Agent discovers available filter fields */
|
|
706
|
-
it("returns filter field documentation", async () => {
|
|
707
|
-
const { formatSchema } = await import(
|
|
708
|
-
"../tools/discover-schema.js"
|
|
709
|
-
);
|
|
710
|
-
const result = formatSchema("filters");
|
|
711
|
-
|
|
712
|
-
expect(result).toContain("## Available Filter Fields");
|
|
713
|
-
expect(result).toContain("filters");
|
|
714
|
-
});
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
describe("when category is metrics", () => {
|
|
718
|
-
/** @scenario Agent discovers available metrics with allowed aggregations */
|
|
719
|
-
it("returns metric documentation", async () => {
|
|
720
|
-
const { formatSchema } = await import(
|
|
721
|
-
"../tools/discover-schema.js"
|
|
722
|
-
);
|
|
723
|
-
const result = formatSchema("metrics");
|
|
724
|
-
|
|
725
|
-
expect(result).toContain("## Available Metrics");
|
|
726
|
-
});
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
describe("when category is aggregations", () => {
|
|
730
|
-
it("returns aggregation types", async () => {
|
|
731
|
-
const { formatSchema } = await import(
|
|
732
|
-
"../tools/discover-schema.js"
|
|
733
|
-
);
|
|
734
|
-
const result = formatSchema("aggregations");
|
|
735
|
-
|
|
736
|
-
expect(result).toContain("## Available Aggregation Types");
|
|
737
|
-
expect(result).toContain("cardinality");
|
|
738
|
-
expect(result).toContain("avg");
|
|
739
|
-
expect(result).toContain("sum");
|
|
740
|
-
});
|
|
741
|
-
});
|
|
742
|
-
|
|
743
|
-
describe("when category is groups", () => {
|
|
744
|
-
/** @scenario Agent discovers available group-by options */
|
|
745
|
-
it("returns group-by options", async () => {
|
|
746
|
-
const { formatSchema } = await import(
|
|
747
|
-
"../tools/discover-schema.js"
|
|
748
|
-
);
|
|
749
|
-
const result = formatSchema("groups");
|
|
750
|
-
|
|
751
|
-
expect(result).toContain("## Available Group-By Options");
|
|
752
|
-
});
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
describe("when category is scenarios", () => {
|
|
756
|
-
it("returns scenario schema documentation", async () => {
|
|
757
|
-
const { formatScenarioSchema } = await import(
|
|
758
|
-
"../tools/discover-scenario-schema.js"
|
|
759
|
-
);
|
|
760
|
-
const result = formatScenarioSchema();
|
|
761
|
-
|
|
762
|
-
expect(result).toContain("# Scenario Schema");
|
|
763
|
-
expect(result).toContain("**name** (required)");
|
|
764
|
-
expect(result).toContain("**situation** (required)");
|
|
765
|
-
});
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
describe("when category is evaluators", () => {
|
|
769
|
-
it("returns evaluator type overview", async () => {
|
|
770
|
-
const { formatEvaluatorSchema } = await import(
|
|
771
|
-
"../tools/discover-evaluator-schema.js"
|
|
772
|
-
);
|
|
773
|
-
const result = formatEvaluatorSchema();
|
|
774
|
-
|
|
775
|
-
expect(result).toContain("# Available Evaluator Types");
|
|
776
|
-
});
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
describe("when category is evaluators with specific type", () => {
|
|
780
|
-
it("returns detailed evaluator schema", async () => {
|
|
781
|
-
const { formatEvaluatorSchema } = await import(
|
|
782
|
-
"../tools/discover-evaluator-schema.js"
|
|
783
|
-
);
|
|
784
|
-
const result = formatEvaluatorSchema("langevals/llm_boolean");
|
|
785
|
-
|
|
786
|
-
expect(result).toContain("langevals/llm_boolean");
|
|
787
|
-
expect(result).toContain("## Settings");
|
|
788
|
-
});
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
describe("when evaluator type is unknown", () => {
|
|
792
|
-
it("returns an error message", async () => {
|
|
793
|
-
const { formatEvaluatorSchema } = await import(
|
|
794
|
-
"../tools/discover-evaluator-schema.js"
|
|
795
|
-
);
|
|
796
|
-
const result = formatEvaluatorSchema("nonexistent/type");
|
|
797
|
-
|
|
798
|
-
expect(result).toContain('Unknown evaluator type');
|
|
799
|
-
});
|
|
800
|
-
});
|
|
801
|
-
|
|
802
|
-
describe("when category is all", () => {
|
|
803
|
-
/** @scenario Agent discovers all schema information at once */
|
|
804
|
-
it("returns all schema categories", async () => {
|
|
805
|
-
const { formatSchema } = await import(
|
|
806
|
-
"../tools/discover-schema.js"
|
|
807
|
-
);
|
|
808
|
-
const result = formatSchema("all");
|
|
809
|
-
|
|
810
|
-
expect(result).toContain("## Available Filter Fields");
|
|
811
|
-
expect(result).toContain("## Available Metrics");
|
|
812
|
-
expect(result).toContain("## Available Aggregation Types");
|
|
813
|
-
expect(result).toContain("## Available Group-By Options");
|
|
814
|
-
});
|
|
815
|
-
});
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
// =====================
|
|
819
|
-
// 4. search_traces
|
|
820
|
-
// =====================
|
|
821
|
-
describe("search_traces", () => {
|
|
822
|
-
describe("when traces are found", () => {
|
|
823
|
-
/** @scenario Agent searches traces with a text query */
|
|
824
|
-
it("returns formatted trace digests", async () => {
|
|
825
|
-
const { handleSearchTraces } = await import(
|
|
826
|
-
"../tools/search-traces.js"
|
|
827
|
-
);
|
|
828
|
-
const result = await handleSearchTraces({
|
|
829
|
-
startDate: "24h",
|
|
830
|
-
endDate: "now",
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
expect(result).toContain("trace-001");
|
|
834
|
-
expect(result).toContain("LLM Call [llm] 500ms");
|
|
835
|
-
expect(result).toContain("1 trace");
|
|
836
|
-
});
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
describe("when no traces match", () => {
|
|
840
|
-
it("returns a no-results message", async () => {
|
|
841
|
-
const { handleSearchTraces } = await import(
|
|
842
|
-
"../tools/search-traces.js"
|
|
843
|
-
);
|
|
844
|
-
const result = await handleSearchTraces({
|
|
845
|
-
query: "__empty__",
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
expect(result).toBe("No traces found matching your query.");
|
|
849
|
-
});
|
|
850
|
-
});
|
|
851
|
-
|
|
852
|
-
describe("when pagination token is present", () => {
|
|
853
|
-
/** @scenario Agent paginates through trace results */
|
|
854
|
-
it("includes scroll ID for next page", async () => {
|
|
855
|
-
const { handleSearchTraces } = await import(
|
|
856
|
-
"../tools/search-traces.js"
|
|
857
|
-
);
|
|
858
|
-
const result = await handleSearchTraces({
|
|
859
|
-
pageSize: 5,
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
expect(result).toContain("scroll-token-abc");
|
|
863
|
-
});
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
describe("when format is json", () => {
|
|
867
|
-
it("returns parseable JSON", async () => {
|
|
868
|
-
const { handleSearchTraces } = await import(
|
|
869
|
-
"../tools/search-traces.js"
|
|
870
|
-
);
|
|
871
|
-
const result = await handleSearchTraces({
|
|
872
|
-
format: "json",
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
const parsed = JSON.parse(result);
|
|
876
|
-
expect(parsed.traces).toBeDefined();
|
|
877
|
-
expect(parsed.traces.length).toBeGreaterThan(0);
|
|
878
|
-
expect(parsed.traces[0].trace_id).toBe("trace-001");
|
|
879
|
-
});
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
describe("when filters are applied", () => {
|
|
883
|
-
/** @scenario Agent searches traces filtered by user_id */
|
|
884
|
-
it("passes filters to the API", async () => {
|
|
885
|
-
const { handleSearchTraces } = await import(
|
|
886
|
-
"../tools/search-traces.js"
|
|
887
|
-
);
|
|
888
|
-
await handleSearchTraces({
|
|
889
|
-
filters: { "metadata.user_id": ["user-42"] },
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
const req = lastRequests["POST /api/traces/search"];
|
|
893
|
-
expect(req).toBeDefined();
|
|
894
|
-
const parsed = JSON.parse(req!.body);
|
|
895
|
-
expect(parsed.filters).toEqual({
|
|
896
|
-
"metadata.user_id": ["user-42"],
|
|
897
|
-
});
|
|
898
|
-
});
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
describe("given a trace has evaluation results", () => {
|
|
902
|
-
/** @scenario Agent searches traces and sees evaluation results without a follow-up call */
|
|
903
|
-
it("includes evaluation status in the digest without a follow-up call", async () => {
|
|
904
|
-
const { handleSearchTraces } = await import(
|
|
905
|
-
"../tools/search-traces.js"
|
|
906
|
-
);
|
|
907
|
-
const result = await handleSearchTraces({
|
|
908
|
-
startDate: "24h",
|
|
909
|
-
endDate: "now",
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
expect(result).toContain("- **Faithfulness**: PASSED (score: 0.95)");
|
|
913
|
-
});
|
|
914
|
-
});
|
|
915
|
-
});
|
|
916
|
-
|
|
917
|
-
// =====================
|
|
918
|
-
// 5. get_trace
|
|
919
|
-
// =====================
|
|
920
|
-
describe("get_trace", () => {
|
|
921
|
-
describe("when trace exists", () => {
|
|
922
|
-
/** @scenario Agent gets a single trace by ID in AI-readable format */
|
|
923
|
-
it("returns formatted trace with metadata and evaluations", async () => {
|
|
924
|
-
const { handleGetTrace } = await import(
|
|
925
|
-
"../tools/get-trace.js"
|
|
926
|
-
);
|
|
927
|
-
const result = await handleGetTrace({ traceId: "trace-001" });
|
|
928
|
-
|
|
929
|
-
expect(result).toContain("# Trace: trace-001");
|
|
930
|
-
expect(result).toContain("LLM Call [llm] 500ms");
|
|
931
|
-
expect(result).toContain("Faithfulness");
|
|
932
|
-
expect(result).toContain("PASSED");
|
|
933
|
-
expect(result).toContain("**User**: user-42");
|
|
934
|
-
expect(result).toContain("**Thread**: thread-1");
|
|
935
|
-
expect(result).toContain("**Customer**: cust-100");
|
|
936
|
-
});
|
|
937
|
-
});
|
|
938
|
-
|
|
939
|
-
describe("when trace does not exist", () => {
|
|
940
|
-
/** @scenario Agent gets a trace that does not exist */
|
|
941
|
-
it("propagates the 404 error", async () => {
|
|
942
|
-
const { handleGetTrace } = await import(
|
|
943
|
-
"../tools/get-trace.js"
|
|
944
|
-
);
|
|
945
|
-
|
|
946
|
-
await expect(
|
|
947
|
-
handleGetTrace({ traceId: "trace-nonexistent" }),
|
|
948
|
-
).rejects.toThrow("404");
|
|
949
|
-
});
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
describe("when format is json", () => {
|
|
953
|
-
it("returns parseable JSON with full trace data", async () => {
|
|
954
|
-
const { handleGetTrace } = await import(
|
|
955
|
-
"../tools/get-trace.js"
|
|
956
|
-
);
|
|
957
|
-
const result = await handleGetTrace({
|
|
958
|
-
traceId: "trace-001",
|
|
959
|
-
format: "json",
|
|
960
|
-
});
|
|
961
|
-
|
|
962
|
-
const parsed = JSON.parse(result);
|
|
963
|
-
expect(parsed.trace_id).toBe("trace-001");
|
|
964
|
-
expect(parsed.evaluations).toBeDefined();
|
|
965
|
-
expect(parsed.metadata).toBeDefined();
|
|
966
|
-
});
|
|
967
|
-
});
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
// =====================
|
|
971
|
-
// 6. get_analytics
|
|
972
|
-
// =====================
|
|
973
|
-
describe("get_analytics", () => {
|
|
974
|
-
describe("when data is available", () => {
|
|
975
|
-
it("returns formatted analytics with markdown table", async () => {
|
|
976
|
-
const { handleGetAnalytics } = await import(
|
|
977
|
-
"../tools/get-analytics.js"
|
|
978
|
-
);
|
|
979
|
-
const result = await handleGetAnalytics({
|
|
980
|
-
metric: "metadata.trace_id",
|
|
981
|
-
aggregation: "cardinality",
|
|
982
|
-
startDate: "7d",
|
|
983
|
-
});
|
|
984
|
-
|
|
985
|
-
expect(result).toContain("42");
|
|
986
|
-
expect(result).toContain("58");
|
|
987
|
-
expect(result).toContain("| Date | Value |");
|
|
988
|
-
});
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
describe("when metric and aggregation are specified", () => {
|
|
992
|
-
it("passes them through to the API", async () => {
|
|
993
|
-
const { handleGetAnalytics } = await import(
|
|
994
|
-
"../tools/get-analytics.js"
|
|
995
|
-
);
|
|
996
|
-
await handleGetAnalytics({
|
|
997
|
-
metric: "performance.total_cost",
|
|
998
|
-
aggregation: "sum",
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
const req = lastRequests["POST /api/analytics/timeseries"];
|
|
1002
|
-
expect(req).toBeDefined();
|
|
1003
|
-
const parsed = JSON.parse(req!.body);
|
|
1004
|
-
expect(parsed.series[0].metric).toBe("performance.total_cost");
|
|
1005
|
-
expect(parsed.series[0].aggregation).toBe("sum");
|
|
1006
|
-
});
|
|
1007
|
-
});
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
// =====================
|
|
1011
|
-
// 7. platform_create_prompt
|
|
1012
|
-
// =====================
|
|
1013
|
-
describe("platform_create_prompt", () => {
|
|
1014
|
-
describe("when valid data is provided", () => {
|
|
1015
|
-
it("returns success confirmation with prompt details", async () => {
|
|
1016
|
-
const { handleCreatePrompt } = await import(
|
|
1017
|
-
"../tools/create-prompt.js"
|
|
1018
|
-
);
|
|
1019
|
-
const result = await handleCreatePrompt({
|
|
1020
|
-
name: "New Prompt",
|
|
1021
|
-
messages: [{ role: "system", content: "You are helpful." }],
|
|
1022
|
-
model: "openai/gpt-4o",
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
expect(result).toContain("created successfully");
|
|
1026
|
-
expect(result).toContain("p-new");
|
|
1027
|
-
expect(result).toContain("**Name**: New Prompt");
|
|
1028
|
-
expect(result).toContain("**Model**: openai/gpt-4o");
|
|
1029
|
-
});
|
|
1030
|
-
});
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
// =====================
|
|
1034
|
-
// 8. platform_list_prompts
|
|
1035
|
-
// =====================
|
|
1036
|
-
describe("platform_list_prompts", () => {
|
|
1037
|
-
describe("when prompts exist", () => {
|
|
1038
|
-
it("returns formatted prompt list", async () => {
|
|
1039
|
-
const { handleListPrompts } = await import(
|
|
1040
|
-
"../tools/list-prompts.js"
|
|
1041
|
-
);
|
|
1042
|
-
const result = await handleListPrompts();
|
|
1043
|
-
|
|
1044
|
-
expect(result).toContain("greeting-bot");
|
|
1045
|
-
expect(result).toContain("Greeting Bot");
|
|
1046
|
-
expect(result).toContain("qa-assistant");
|
|
1047
|
-
expect(result).toContain("QA Assistant");
|
|
1048
|
-
expect(result).toContain("# Prompts (2 total)");
|
|
1049
|
-
});
|
|
1050
|
-
});
|
|
1051
|
-
});
|
|
1052
|
-
|
|
1053
|
-
// =====================
|
|
1054
|
-
// 9. platform_get_prompt
|
|
1055
|
-
// =====================
|
|
1056
|
-
describe("platform_get_prompt", () => {
|
|
1057
|
-
describe("when prompt exists", () => {
|
|
1058
|
-
it("returns formatted prompt details with messages and versions", async () => {
|
|
1059
|
-
const { handleGetPrompt } = await import(
|
|
1060
|
-
"../tools/get-prompt.js"
|
|
1061
|
-
);
|
|
1062
|
-
const result = await handleGetPrompt({
|
|
1063
|
-
idOrHandle: "greeting-bot",
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
expect(result).toContain("# Prompt: Greeting Bot");
|
|
1067
|
-
expect(result).toContain("gpt-4o");
|
|
1068
|
-
expect(result).toContain("You are a friendly bot.");
|
|
1069
|
-
expect(result).toContain("v3");
|
|
1070
|
-
expect(result).toContain("## Version History");
|
|
1071
|
-
expect(result).toContain("Updated tone");
|
|
1072
|
-
});
|
|
1073
|
-
});
|
|
1074
|
-
});
|
|
1075
|
-
|
|
1076
|
-
// =====================
|
|
1077
|
-
// 10. platform_update_prompt
|
|
1078
|
-
// =====================
|
|
1079
|
-
describe("platform_update_prompt", () => {
|
|
1080
|
-
describe("when updating a prompt", () => {
|
|
1081
|
-
it("returns success message", async () => {
|
|
1082
|
-
const { handleUpdatePrompt } = await import(
|
|
1083
|
-
"../tools/update-prompt.js"
|
|
1084
|
-
);
|
|
1085
|
-
const result = await handleUpdatePrompt({
|
|
1086
|
-
idOrHandle: "greeting-bot",
|
|
1087
|
-
model: "openai/gpt-4o-mini",
|
|
1088
|
-
commitMessage: "Switch to mini",
|
|
1089
|
-
});
|
|
1090
|
-
|
|
1091
|
-
expect(result).toContain("updated successfully");
|
|
1092
|
-
expect(result).toContain("Switch to mini");
|
|
1093
|
-
});
|
|
1094
|
-
});
|
|
1095
|
-
});
|
|
1096
|
-
|
|
1097
|
-
// =====================
|
|
1098
|
-
// 11. platform_create_scenario
|
|
1099
|
-
// =====================
|
|
1100
|
-
describe("platform_create_scenario", () => {
|
|
1101
|
-
describe("when valid data is provided", () => {
|
|
1102
|
-
it("returns confirmation with new scenario ID", async () => {
|
|
1103
|
-
const { handleCreateScenario } = await import(
|
|
1104
|
-
"../tools/create-scenario.js"
|
|
1105
|
-
);
|
|
1106
|
-
const result = await handleCreateScenario({
|
|
1107
|
-
name: "New Scenario",
|
|
1108
|
-
situation: "User does something",
|
|
1109
|
-
criteria: ["Agent responds correctly"],
|
|
1110
|
-
labels: ["test"],
|
|
1111
|
-
});
|
|
1112
|
-
|
|
1113
|
-
expect(result).toContain("created successfully");
|
|
1114
|
-
expect(result).toContain("scen_new789");
|
|
1115
|
-
});
|
|
1116
|
-
});
|
|
1117
|
-
});
|
|
1118
|
-
|
|
1119
|
-
// =====================
|
|
1120
|
-
// 12. platform_list_scenarios
|
|
1121
|
-
// =====================
|
|
1122
|
-
describe("platform_list_scenarios", () => {
|
|
1123
|
-
describe("when scenarios exist", () => {
|
|
1124
|
-
it("returns formatted scenario list", async () => {
|
|
1125
|
-
const { handleListScenarios } = await import(
|
|
1126
|
-
"../tools/list-scenarios.js"
|
|
1127
|
-
);
|
|
1128
|
-
const result = await handleListScenarios({});
|
|
1129
|
-
|
|
1130
|
-
expect(result).toContain("# Scenarios (2 total)");
|
|
1131
|
-
expect(result).toContain("Login Flow Happy Path");
|
|
1132
|
-
expect(result).toContain("Password Reset");
|
|
1133
|
-
});
|
|
1134
|
-
});
|
|
1135
|
-
|
|
1136
|
-
describe("when format is json", () => {
|
|
1137
|
-
it("returns parseable JSON matching API response", async () => {
|
|
1138
|
-
const { handleListScenarios } = await import(
|
|
1139
|
-
"../tools/list-scenarios.js"
|
|
1140
|
-
);
|
|
1141
|
-
const result = await handleListScenarios({ format: "json" });
|
|
1142
|
-
|
|
1143
|
-
expect(JSON.parse(result)).toEqual(CANNED_SCENARIOS_LIST);
|
|
1144
|
-
});
|
|
1145
|
-
});
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
// =====================
|
|
1149
|
-
// 13. platform_get_scenario
|
|
1150
|
-
// =====================
|
|
1151
|
-
describe("platform_get_scenario", () => {
|
|
1152
|
-
describe("when the scenario exists", () => {
|
|
1153
|
-
it("returns formatted scenario details", async () => {
|
|
1154
|
-
const { handleGetScenario } = await import(
|
|
1155
|
-
"../tools/get-scenario.js"
|
|
1156
|
-
);
|
|
1157
|
-
const result = await handleGetScenario({
|
|
1158
|
-
scenarioId: "scen_abc123",
|
|
1159
|
-
});
|
|
1160
|
-
|
|
1161
|
-
expect(result).toContain("# Scenario: Login Flow Happy Path");
|
|
1162
|
-
expect(result).toContain("User attempts to log in");
|
|
1163
|
-
expect(result).toContain("Responds with a welcome message");
|
|
1164
|
-
});
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
describe("when the scenario does not exist", () => {
|
|
1168
|
-
it("propagates the 404 error", async () => {
|
|
1169
|
-
const { handleGetScenario } = await import(
|
|
1170
|
-
"../tools/get-scenario.js"
|
|
1171
|
-
);
|
|
1172
|
-
|
|
1173
|
-
await expect(
|
|
1174
|
-
handleGetScenario({ scenarioId: "scen_nonexistent" }),
|
|
1175
|
-
).rejects.toThrow("404");
|
|
1176
|
-
});
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
describe("when format is json", () => {
|
|
1180
|
-
it("returns parseable JSON", async () => {
|
|
1181
|
-
const { handleGetScenario } = await import(
|
|
1182
|
-
"../tools/get-scenario.js"
|
|
1183
|
-
);
|
|
1184
|
-
const result = await handleGetScenario({
|
|
1185
|
-
scenarioId: "scen_abc123",
|
|
1186
|
-
format: "json",
|
|
1187
|
-
});
|
|
1188
|
-
|
|
1189
|
-
expect(JSON.parse(result)).toEqual(CANNED_SCENARIO_DETAIL);
|
|
1190
|
-
});
|
|
1191
|
-
});
|
|
1192
|
-
});
|
|
1193
|
-
|
|
1194
|
-
// =====================
|
|
1195
|
-
// 14. platform_update_scenario
|
|
1196
|
-
// =====================
|
|
1197
|
-
describe("platform_update_scenario", () => {
|
|
1198
|
-
describe("when the scenario exists", () => {
|
|
1199
|
-
it("returns update confirmation with updated details", async () => {
|
|
1200
|
-
const { handleUpdateScenario } = await import(
|
|
1201
|
-
"../tools/update-scenario.js"
|
|
1202
|
-
);
|
|
1203
|
-
const result = await handleUpdateScenario({
|
|
1204
|
-
scenarioId: "scen_abc123",
|
|
1205
|
-
name: "Login Flow - Updated",
|
|
1206
|
-
});
|
|
1207
|
-
|
|
1208
|
-
expect(result).toContain("updated successfully");
|
|
1209
|
-
expect(result).toContain("scen_abc123");
|
|
1210
|
-
});
|
|
1211
|
-
});
|
|
1212
|
-
});
|
|
1213
|
-
|
|
1214
|
-
// =====================
|
|
1215
|
-
// 15. platform_archive_scenario
|
|
1216
|
-
// =====================
|
|
1217
|
-
describe("platform_archive_scenario", () => {
|
|
1218
|
-
describe("when the scenario exists", () => {
|
|
1219
|
-
it("returns confirmation that scenario was archived", async () => {
|
|
1220
|
-
const { handleArchiveScenario } = await import(
|
|
1221
|
-
"../tools/archive-scenario.js"
|
|
1222
|
-
);
|
|
1223
|
-
const result = await handleArchiveScenario({
|
|
1224
|
-
scenarioId: "scen_abc123",
|
|
1225
|
-
});
|
|
1226
|
-
|
|
1227
|
-
expect(result).toContain("archived successfully");
|
|
1228
|
-
expect(result).toContain("scen_abc123");
|
|
1229
|
-
expect(result).toContain("archived");
|
|
1230
|
-
});
|
|
1231
|
-
});
|
|
1232
|
-
});
|
|
1233
|
-
|
|
1234
|
-
// =====================
|
|
1235
|
-
// 16. platform_create_evaluator
|
|
1236
|
-
// =====================
|
|
1237
|
-
describe("platform_create_evaluator", () => {
|
|
1238
|
-
describe("when valid data is provided", () => {
|
|
1239
|
-
it("returns success confirmation with evaluator details", async () => {
|
|
1240
|
-
const { handleCreateEvaluator } = await import(
|
|
1241
|
-
"../tools/create-evaluator.js"
|
|
1242
|
-
);
|
|
1243
|
-
const result = await handleCreateEvaluator({
|
|
1244
|
-
name: "My LLM Judge",
|
|
1245
|
-
config: { evaluatorType: "langevals/llm_boolean" },
|
|
1246
|
-
});
|
|
1247
|
-
|
|
1248
|
-
expect(result).toContain("Evaluator created successfully!");
|
|
1249
|
-
expect(result).toContain("evaluator_new123");
|
|
1250
|
-
expect(result).toContain("my-llm-judge");
|
|
1251
|
-
expect(result).toContain("langevals/llm_boolean");
|
|
1252
|
-
});
|
|
1253
|
-
});
|
|
1254
|
-
});
|
|
1255
|
-
|
|
1256
|
-
// =====================
|
|
1257
|
-
// 17. platform_list_evaluators
|
|
1258
|
-
// =====================
|
|
1259
|
-
describe("platform_list_evaluators", () => {
|
|
1260
|
-
describe("when evaluators exist", () => {
|
|
1261
|
-
it("returns formatted evaluator list", async () => {
|
|
1262
|
-
const { handleListEvaluators } = await import(
|
|
1263
|
-
"../tools/list-evaluators.js"
|
|
1264
|
-
);
|
|
1265
|
-
const result = await handleListEvaluators();
|
|
1266
|
-
|
|
1267
|
-
expect(result).toContain("# Evaluators (2 total)");
|
|
1268
|
-
expect(result).toContain("Toxicity Check");
|
|
1269
|
-
expect(result).toContain("toxicity-check");
|
|
1270
|
-
expect(result).toContain("openai/moderation");
|
|
1271
|
-
expect(result).toContain("Exact Match");
|
|
1272
|
-
expect(result).toContain("exact-match");
|
|
1273
|
-
});
|
|
1274
|
-
});
|
|
1275
|
-
});
|
|
1276
|
-
|
|
1277
|
-
// =====================
|
|
1278
|
-
// 18. platform_get_evaluator
|
|
1279
|
-
// =====================
|
|
1280
|
-
describe("platform_get_evaluator", () => {
|
|
1281
|
-
describe("when the evaluator exists", () => {
|
|
1282
|
-
it("returns formatted evaluator details with config and fields", async () => {
|
|
1283
|
-
const { handleGetEvaluator } = await import(
|
|
1284
|
-
"../tools/get-evaluator.js"
|
|
1285
|
-
);
|
|
1286
|
-
const result = await handleGetEvaluator({
|
|
1287
|
-
idOrSlug: "evaluator_abc123",
|
|
1288
|
-
});
|
|
1289
|
-
|
|
1290
|
-
expect(result).toContain("# Evaluator: Toxicity Check");
|
|
1291
|
-
expect(result).toContain("openai/moderation");
|
|
1292
|
-
expect(result).toContain("text-moderation-stable");
|
|
1293
|
-
expect(result).toContain("## Input Fields");
|
|
1294
|
-
expect(result).toContain("**input** (str)");
|
|
1295
|
-
expect(result).toContain("## Output Fields");
|
|
1296
|
-
expect(result).toContain("**passed** (bool)");
|
|
1297
|
-
expect(result).toContain("**score** (float)");
|
|
1298
|
-
});
|
|
1299
|
-
});
|
|
1300
|
-
|
|
1301
|
-
describe("when the evaluator does not exist", () => {
|
|
1302
|
-
it("propagates the 404 error", async () => {
|
|
1303
|
-
const { handleGetEvaluator } = await import(
|
|
1304
|
-
"../tools/get-evaluator.js"
|
|
1305
|
-
);
|
|
1306
|
-
|
|
1307
|
-
await expect(
|
|
1308
|
-
handleGetEvaluator({ idOrSlug: "evaluator_nonexistent" }),
|
|
1309
|
-
).rejects.toThrow("404");
|
|
1310
|
-
});
|
|
1311
|
-
});
|
|
1312
|
-
});
|
|
1313
|
-
|
|
1314
|
-
// =====================
|
|
1315
|
-
// 19. platform_update_evaluator
|
|
1316
|
-
// =====================
|
|
1317
|
-
describe("platform_update_evaluator", () => {
|
|
1318
|
-
describe("when the evaluator exists", () => {
|
|
1319
|
-
it("returns update confirmation", async () => {
|
|
1320
|
-
const { handleUpdateEvaluator } = await import(
|
|
1321
|
-
"../tools/update-evaluator.js"
|
|
1322
|
-
);
|
|
1323
|
-
const result = await handleUpdateEvaluator({
|
|
1324
|
-
evaluatorId: "evaluator_abc123",
|
|
1325
|
-
name: "Updated Toxicity",
|
|
1326
|
-
});
|
|
1327
|
-
|
|
1328
|
-
expect(result).toContain("Evaluator updated successfully!");
|
|
1329
|
-
expect(result).toContain("evaluator_abc123");
|
|
1330
|
-
expect(result).toContain("Updated Toxicity");
|
|
1331
|
-
});
|
|
1332
|
-
});
|
|
1333
|
-
});
|
|
1334
|
-
|
|
1335
|
-
// =====================
|
|
1336
|
-
// 20. platform_set_model_provider
|
|
1337
|
-
// =====================
|
|
1338
|
-
describe("platform_set_model_provider", () => {
|
|
1339
|
-
describe("when setting a provider with API key", () => {
|
|
1340
|
-
it("returns success confirmation with provider details", async () => {
|
|
1341
|
-
const { handleSetModelProvider } = await import(
|
|
1342
|
-
"../tools/set-model-provider.js"
|
|
1343
|
-
);
|
|
1344
|
-
const result = await handleSetModelProvider({
|
|
1345
|
-
provider: "openai",
|
|
1346
|
-
enabled: true,
|
|
1347
|
-
customKeys: { OPENAI_API_KEY: "sk-test123" },
|
|
1348
|
-
});
|
|
1349
|
-
|
|
1350
|
-
expect(result).toContain("Model provider updated successfully!");
|
|
1351
|
-
expect(result).toContain("**Provider**: openai");
|
|
1352
|
-
expect(result).toContain("**Status**: enabled");
|
|
1353
|
-
expect(result).toContain("OPENAI_API_KEY: set");
|
|
1354
|
-
});
|
|
1355
|
-
});
|
|
1356
|
-
|
|
1357
|
-
describe("when setting a default model", () => {
|
|
1358
|
-
it("shows the normalized model name with provider prefix", async () => {
|
|
1359
|
-
const { handleSetModelProvider } = await import(
|
|
1360
|
-
"../tools/set-model-provider.js"
|
|
1361
|
-
);
|
|
1362
|
-
const result = await handleSetModelProvider({
|
|
1363
|
-
provider: "openai",
|
|
1364
|
-
enabled: true,
|
|
1365
|
-
defaultModel: "gpt-4o",
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
expect(result).toContain("**Default Model**: openai/gpt-4o");
|
|
1369
|
-
});
|
|
1370
|
-
});
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
|
-
// =====================
|
|
1374
|
-
// 21. platform_list_model_providers
|
|
1375
|
-
// =====================
|
|
1376
|
-
describe("platform_list_model_providers", () => {
|
|
1377
|
-
describe("when providers exist", () => {
|
|
1378
|
-
it("returns formatted provider list with status and key info", async () => {
|
|
1379
|
-
const { handleListModelProviders } = await import(
|
|
1380
|
-
"../tools/list-model-providers.js"
|
|
1381
|
-
);
|
|
1382
|
-
const result = await handleListModelProviders();
|
|
1383
|
-
|
|
1384
|
-
expect(result).toContain("# Model Providers (2 total)");
|
|
1385
|
-
expect(result).toContain("## openai");
|
|
1386
|
-
expect(result).toContain("**Status**: enabled");
|
|
1387
|
-
expect(result).toContain("## anthropic");
|
|
1388
|
-
expect(result).toContain("**Status**: disabled");
|
|
1389
|
-
expect(result).toContain("OPENAI_API_KEY: set");
|
|
1390
|
-
expect(result).toContain("2 available");
|
|
1391
|
-
});
|
|
1392
|
-
});
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
// =====================
|
|
1396
|
-
// Cross-cutting: authentication
|
|
1397
|
-
// =====================
|
|
1398
|
-
describe("when API key is invalid", () => {
|
|
1399
|
-
afterAll(() => {
|
|
1400
|
-
initConfig({
|
|
1401
|
-
apiKey: "test-integration-key",
|
|
1402
|
-
endpoint: `http://localhost:${port}`,
|
|
1403
|
-
});
|
|
1404
|
-
});
|
|
1405
|
-
|
|
1406
|
-
it("throws an error with 401 status for trace search", async () => {
|
|
1407
|
-
initConfig({
|
|
1408
|
-
apiKey: "bad-key",
|
|
1409
|
-
endpoint: `http://localhost:${port}`,
|
|
1410
|
-
});
|
|
1411
|
-
|
|
1412
|
-
const { handleSearchTraces } = await import(
|
|
1413
|
-
"../tools/search-traces.js"
|
|
1414
|
-
);
|
|
1415
|
-
await expect(
|
|
1416
|
-
handleSearchTraces({ startDate: "24h" }),
|
|
1417
|
-
).rejects.toThrow("401");
|
|
1418
|
-
});
|
|
1419
|
-
|
|
1420
|
-
it("throws an error with 401 status for evaluator list", async () => {
|
|
1421
|
-
initConfig({
|
|
1422
|
-
apiKey: "bad-key",
|
|
1423
|
-
endpoint: `http://localhost:${port}`,
|
|
1424
|
-
});
|
|
1425
|
-
|
|
1426
|
-
const { handleListEvaluators } = await import(
|
|
1427
|
-
"../tools/list-evaluators.js"
|
|
1428
|
-
);
|
|
1429
|
-
await expect(handleListEvaluators()).rejects.toThrow("401");
|
|
1430
|
-
});
|
|
1431
|
-
|
|
1432
|
-
it("throws an error with 401 status for model providers list", async () => {
|
|
1433
|
-
initConfig({
|
|
1434
|
-
apiKey: "bad-key",
|
|
1435
|
-
endpoint: `http://localhost:${port}`,
|
|
1436
|
-
});
|
|
1437
|
-
|
|
1438
|
-
const { handleListModelProviders } = await import(
|
|
1439
|
-
"../tools/list-model-providers.js"
|
|
1440
|
-
);
|
|
1441
|
-
await expect(handleListModelProviders()).rejects.toThrow("401");
|
|
1442
|
-
});
|
|
1443
|
-
});
|
|
1444
|
-
|
|
1445
|
-
// =====================
|
|
1446
|
-
// Suite Tools
|
|
1447
|
-
// =====================
|
|
1448
|
-
describe("platform_list_suites", () => {
|
|
1449
|
-
it("returns formatted suite list", async () => {
|
|
1450
|
-
const { handleListSuites } = await import("../tools/list-suites.js");
|
|
1451
|
-
const result = await handleListSuites({});
|
|
1452
|
-
|
|
1453
|
-
expect(result).toContain("Suites / Run Plans (1 total)");
|
|
1454
|
-
expect(result).toContain("Regression Suite");
|
|
1455
|
-
});
|
|
1456
|
-
});
|
|
1457
|
-
|
|
1458
|
-
describe("platform_get_suite", () => {
|
|
1459
|
-
it("returns suite details", async () => {
|
|
1460
|
-
const { handleGetSuite } = await import("../tools/get-suite.js");
|
|
1461
|
-
const result = await handleGetSuite({ id: "suite_abc" });
|
|
1462
|
-
|
|
1463
|
-
expect(result).toContain("Regression Suite");
|
|
1464
|
-
expect(result).toContain("suite_abc");
|
|
1465
|
-
});
|
|
1466
|
-
});
|
|
1467
|
-
|
|
1468
|
-
describe("platform_create_suite", () => {
|
|
1469
|
-
it("creates a suite and returns confirmation", async () => {
|
|
1470
|
-
const { handleCreateSuite } = await import("../tools/create-suite.js");
|
|
1471
|
-
const result = await handleCreateSuite({
|
|
1472
|
-
name: "New Suite",
|
|
1473
|
-
scenarioIds: ["scen_abc123"],
|
|
1474
|
-
targets: JSON.stringify([{ type: "http", referenceId: "agent_abc" }]),
|
|
1475
|
-
});
|
|
1476
|
-
|
|
1477
|
-
expect(result).toContain("created successfully");
|
|
1478
|
-
expect(result).toContain("New Suite");
|
|
1479
|
-
});
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
describe("platform_run_suite", () => {
|
|
1483
|
-
it("triggers a suite run and returns batch info", async () => {
|
|
1484
|
-
const { handleRunSuite } = await import("../tools/run-suite.js");
|
|
1485
|
-
const result = await handleRunSuite({ id: "suite_abc" });
|
|
1486
|
-
|
|
1487
|
-
expect(result).toContain("scheduled successfully");
|
|
1488
|
-
expect(result).toContain("batch_123");
|
|
1489
|
-
});
|
|
1490
|
-
});
|
|
1491
|
-
|
|
1492
|
-
describe("platform_archive_suite", () => {
|
|
1493
|
-
it("archives the suite", async () => {
|
|
1494
|
-
const { handleArchiveSuite } = await import("../tools/archive-suite.js");
|
|
1495
|
-
const result = await handleArchiveSuite({ id: "suite_abc" });
|
|
1496
|
-
|
|
1497
|
-
expect(result).toContain("archived");
|
|
1498
|
-
});
|
|
1499
|
-
});
|
|
1500
|
-
|
|
1501
|
-
// =====================
|
|
1502
|
-
// Simulation Run Tools
|
|
1503
|
-
// =====================
|
|
1504
|
-
describe("platform_list_simulation_runs", () => {
|
|
1505
|
-
it("returns formatted run list", async () => {
|
|
1506
|
-
const { handleListSimulationRuns } = await import("../tools/list-simulation-runs.js");
|
|
1507
|
-
const result = await handleListSimulationRuns({});
|
|
1508
|
-
|
|
1509
|
-
expect(result).toContain("Simulation Runs");
|
|
1510
|
-
expect(result).toContain("Login Flow");
|
|
1511
|
-
});
|
|
1512
|
-
});
|
|
1513
|
-
|
|
1514
|
-
describe("platform_get_simulation_run", () => {
|
|
1515
|
-
it("returns run details with conversation", async () => {
|
|
1516
|
-
const { handleGetSimulationRun } = await import("../tools/get-simulation-run.js");
|
|
1517
|
-
const result = await handleGetSimulationRun({ scenarioRunId: "run_abc" });
|
|
1518
|
-
|
|
1519
|
-
expect(result).toContain("Login Flow");
|
|
1520
|
-
expect(result).toContain("passed");
|
|
1521
|
-
expect(result).toContain("Hello");
|
|
1522
|
-
});
|
|
1523
|
-
});
|
|
1524
|
-
|
|
1525
|
-
// =====================
|
|
1526
|
-
// Monitor Tools
|
|
1527
|
-
// =====================
|
|
1528
|
-
describe("platform_list_monitors", () => {
|
|
1529
|
-
it("returns formatted monitor list", async () => {
|
|
1530
|
-
const { listMonitors } = await import("../langwatch-api-monitors.js");
|
|
1531
|
-
const monitors = await listMonitors();
|
|
1532
|
-
expect(monitors).toHaveLength(1);
|
|
1533
|
-
expect(monitors[0]!.name).toBe("Toxicity Check");
|
|
1534
|
-
expect(monitors[0]!.checkType).toBe("ragas/toxicity");
|
|
1535
|
-
});
|
|
1536
|
-
});
|
|
1537
|
-
|
|
1538
|
-
describe("platform_get_monitor", () => {
|
|
1539
|
-
it("returns monitor details", async () => {
|
|
1540
|
-
const { getMonitor } = await import("../langwatch-api-monitors.js");
|
|
1541
|
-
const monitor = await getMonitor("mon_abc");
|
|
1542
|
-
expect(monitor.id).toBe("mon_abc");
|
|
1543
|
-
expect(monitor.name).toBe("Toxicity Check");
|
|
1544
|
-
expect(monitor.enabled).toBe(true);
|
|
1545
|
-
});
|
|
1546
|
-
});
|
|
1547
|
-
|
|
1548
|
-
describe("platform_create_monitor", () => {
|
|
1549
|
-
it("creates a monitor and returns metadata", async () => {
|
|
1550
|
-
const { createMonitor } = await import("../langwatch-api-monitors.js");
|
|
1551
|
-
const monitor = await createMonitor({ name: "New Monitor", checkType: "ragas/toxicity" });
|
|
1552
|
-
expect(monitor.id).toBe("mon_new");
|
|
1553
|
-
expect(monitor.name).toBe("New Monitor");
|
|
1554
|
-
});
|
|
1555
|
-
});
|
|
1556
|
-
|
|
1557
|
-
describe("platform_update_monitor", () => {
|
|
1558
|
-
it("updates a monitor", async () => {
|
|
1559
|
-
const { updateMonitor } = await import("../langwatch-api-monitors.js");
|
|
1560
|
-
const monitor = await updateMonitor({ id: "mon_abc", enabled: false });
|
|
1561
|
-
expect(monitor.id).toBe("mon_abc");
|
|
1562
|
-
expect(monitor.enabled).toBe(false);
|
|
1563
|
-
});
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
describe("platform_delete_monitor", () => {
|
|
1567
|
-
it("deletes a monitor", async () => {
|
|
1568
|
-
const { deleteMonitor } = await import("../langwatch-api-monitors.js");
|
|
1569
|
-
const result = await deleteMonitor("mon_abc");
|
|
1570
|
-
expect(result.id).toBe("mon_abc");
|
|
1571
|
-
expect(result.deleted).toBe(true);
|
|
1572
|
-
});
|
|
1573
|
-
});
|
|
1574
|
-
|
|
1575
|
-
// =====================
|
|
1576
|
-
// Secret Tools
|
|
1577
|
-
// =====================
|
|
1578
|
-
describe("platform_list_secrets", () => {
|
|
1579
|
-
it("returns formatted secret list", async () => {
|
|
1580
|
-
const { listSecrets } = await import("../langwatch-api-secrets.js");
|
|
1581
|
-
const secrets = await listSecrets();
|
|
1582
|
-
expect(secrets).toHaveLength(2);
|
|
1583
|
-
expect(secrets[0]!.name).toBe("MY_API_KEY");
|
|
1584
|
-
});
|
|
1585
|
-
});
|
|
1586
|
-
|
|
1587
|
-
describe("platform_create_secret", () => {
|
|
1588
|
-
it("creates a secret and returns metadata", async () => {
|
|
1589
|
-
const { createSecret } = await import("../langwatch-api-secrets.js");
|
|
1590
|
-
const secret = await createSecret({ name: "NEW_SECRET", value: "sk-123" });
|
|
1591
|
-
expect(secret.id).toBe("secret_new");
|
|
1592
|
-
expect(secret.name).toBe("NEW_SECRET");
|
|
1593
|
-
});
|
|
1594
|
-
});
|
|
1595
|
-
|
|
1596
|
-
describe("platform_update_secret", () => {
|
|
1597
|
-
it("updates a secret value", async () => {
|
|
1598
|
-
const { updateSecret } = await import("../langwatch-api-secrets.js");
|
|
1599
|
-
const secret = await updateSecret({ id: "secret_abc", value: "new-val" });
|
|
1600
|
-
expect(secret.id).toBe("secret_abc");
|
|
1601
|
-
expect(secret.name).toBe("MY_API_KEY");
|
|
1602
|
-
});
|
|
1603
|
-
});
|
|
1604
|
-
|
|
1605
|
-
describe("platform_delete_secret", () => {
|
|
1606
|
-
it("deletes a secret", async () => {
|
|
1607
|
-
const { deleteSecret } = await import("../langwatch-api-secrets.js");
|
|
1608
|
-
const result = await deleteSecret("secret_abc");
|
|
1609
|
-
expect(result.id).toBe("secret_abc");
|
|
1610
|
-
expect(result.deleted).toBe(true);
|
|
1611
|
-
});
|
|
1612
|
-
});
|
|
1613
|
-
|
|
1614
|
-
// =====================
|
|
1615
|
-
// Agent Run Tool
|
|
1616
|
-
// =====================
|
|
1617
|
-
describe("platform_run_workflow", () => {
|
|
1618
|
-
it("executes a workflow and returns result", async () => {
|
|
1619
|
-
const { handleRunWorkflow } = await import("../tools/run-workflow.js");
|
|
1620
|
-
const result = await handleRunWorkflow({ id: "wf_abc" });
|
|
1621
|
-
|
|
1622
|
-
expect(result).toContain("executed successfully");
|
|
1623
|
-
expect(result).toContain("workflow result");
|
|
1624
|
-
});
|
|
1625
|
-
});
|
|
1626
|
-
});
|