stageflow 0.40.0 → 0.42.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.
Files changed (326) hide show
  1. package/README.md +3 -1
  2. package/dist/agents/authorChat.d.ts +4 -0
  3. package/dist/agents/authorChat.js +113 -12
  4. package/dist/agents/authorSessionStore.d.ts +35 -0
  5. package/dist/agents/authorSessionStore.js +152 -0
  6. package/dist/agents/chatTurn.d.ts +2 -0
  7. package/dist/agents/chatTurn.js +17 -1
  8. package/dist/agents/errors.d.ts +1 -1
  9. package/dist/agents/errors.js +1 -0
  10. package/dist/agents/evalGate.d.ts +61 -0
  11. package/dist/agents/evalGate.js +103 -0
  12. package/dist/agents/evalSession.d.ts +26 -0
  13. package/dist/agents/evalSession.js +29 -0
  14. package/dist/agents/hostContext.d.ts +3 -0
  15. package/dist/agents/hostContext.js +4 -1
  16. package/dist/agents/runtime/piRuntime.d.ts +1 -1
  17. package/dist/agents/runtime/piRuntime.js +4 -4
  18. package/dist/agents/runtime/types.d.ts +3 -1
  19. package/dist/agents/service.d.ts +27 -2
  20. package/dist/agents/service.js +88 -13
  21. package/dist/agents/sessions.d.ts +8 -0
  22. package/dist/agents/sessions.js +50 -10
  23. package/dist/agents/store.d.ts +14 -3
  24. package/dist/agents/store.js +45 -7
  25. package/dist/agents/toolRisk.d.ts +21 -0
  26. package/dist/agents/toolRisk.js +50 -0
  27. package/dist/cli/doctorCommand.js +26 -0
  28. package/dist/cli/evalCommand.d.ts +20 -0
  29. package/dist/cli/evalCommand.js +720 -0
  30. package/dist/cli/exportAllCommand.js +1 -1
  31. package/dist/cli/hostClient.d.ts +60 -0
  32. package/dist/cli/hostClient.js +71 -0
  33. package/dist/cli/validateOutput.d.ts +2 -2
  34. package/dist/cli/validateOutput.js +2 -2
  35. package/dist/cli.js +10 -0
  36. package/dist/config/atomicWrite.d.ts +15 -0
  37. package/dist/config/atomicWrite.js +43 -0
  38. package/dist/config/browseCatalog.d.ts +2 -0
  39. package/dist/config/browseCatalog.js +4 -2
  40. package/dist/config/csv.d.ts +9 -0
  41. package/dist/config/csv.js +81 -0
  42. package/dist/config/datasetAgentCheck.d.ts +18 -0
  43. package/dist/config/datasetAgentCheck.js +109 -0
  44. package/dist/config/datasetCandidates.d.ts +22 -0
  45. package/dist/config/datasetCandidates.js +86 -0
  46. package/dist/config/datasetFromSessions.d.ts +34 -0
  47. package/dist/config/datasetFromSessions.js +105 -0
  48. package/dist/config/datasetHash.d.ts +1 -0
  49. package/dist/config/datasetHash.js +4 -0
  50. package/dist/config/datasetImport.d.ts +21 -0
  51. package/dist/config/datasetImport.js +120 -0
  52. package/dist/config/datasetStageCheck.d.ts +9 -0
  53. package/dist/config/datasetStageCheck.js +17 -0
  54. package/dist/config/datasetTypes.d.ts +27 -0
  55. package/dist/config/datasetTypes.js +1 -0
  56. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  57. package/dist/config/ensureCatalogScanRoot.js +2 -6
  58. package/dist/config/evalCatalog.d.ts +22 -0
  59. package/dist/config/evalCatalog.js +67 -0
  60. package/dist/config/evalWriteRoots.d.ts +17 -0
  61. package/dist/config/evalWriteRoots.js +109 -0
  62. package/dist/config/hostConfig.js +1 -0
  63. package/dist/config/installEvalSample.d.ts +34 -0
  64. package/dist/config/installEvalSample.js +182 -0
  65. package/dist/config/loadDataset.d.ts +16 -0
  66. package/dist/config/loadDataset.js +318 -0
  67. package/dist/config/loadEval.d.ts +32 -0
  68. package/dist/config/loadEval.js +1085 -0
  69. package/dist/config/loadOutcome.d.ts +5 -0
  70. package/dist/config/loadStageflowManifest.js +64 -0
  71. package/dist/config/resolveAgentTarget.d.ts +32 -0
  72. package/dist/config/resolveAgentTarget.js +118 -0
  73. package/dist/config/resolveEvalTarget.d.ts +21 -0
  74. package/dist/config/resolveEvalTarget.js +89 -0
  75. package/dist/config/scanCatalogPaths.d.ts +1 -1
  76. package/dist/config/scanCatalogPaths.js +17 -5
  77. package/dist/config/validateCatalog.d.ts +1 -1
  78. package/dist/config/validateCatalog.js +9 -1
  79. package/dist/config/validateEval.d.ts +36 -0
  80. package/dist/config/validateEval.js +405 -0
  81. package/dist/config/writeDataset.d.ts +54 -0
  82. package/dist/config/writeDataset.js +186 -0
  83. package/dist/config/writeEval.d.ts +25 -0
  84. package/dist/config/writeEval.js +398 -0
  85. package/dist/diagnostics/checks.js +1 -1
  86. package/dist/evals/agentDiff.d.ts +13 -0
  87. package/dist/evals/agentDiff.js +11 -0
  88. package/dist/evals/canonical.d.ts +3 -0
  89. package/dist/evals/canonical.js +24 -0
  90. package/dist/evals/caseTask.d.ts +3 -0
  91. package/dist/evals/caseTask.js +9 -0
  92. package/dist/evals/compare.d.ts +94 -0
  93. package/dist/evals/compare.js +201 -0
  94. package/dist/evals/constants.d.ts +60 -0
  95. package/dist/evals/constants.js +95 -0
  96. package/dist/evals/engine/FakeEngine.d.ts +33 -0
  97. package/dist/evals/engine/FakeEngine.js +176 -0
  98. package/dist/evals/engine/index.d.ts +9 -0
  99. package/dist/evals/engine/index.js +19 -0
  100. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +52 -0
  101. package/dist/evals/engine/promptfoo/PromptfooEngine.js +311 -0
  102. package/dist/evals/engine/promptfoo/errors.d.ts +5 -0
  103. package/dist/evals/engine/promptfoo/errors.js +8 -0
  104. package/dist/evals/engine/promptfoo/generate.d.ts +30 -0
  105. package/dist/evals/engine/promptfoo/generate.js +342 -0
  106. package/dist/evals/engine/promptfoo/guard.d.ts +10 -0
  107. package/dist/evals/engine/promptfoo/guard.js +116 -0
  108. package/dist/evals/engine/promptfoo/install.d.ts +17 -0
  109. package/dist/evals/engine/promptfoo/install.js +73 -0
  110. package/dist/evals/engine/promptfoo/judgeProviders.d.ts +2 -0
  111. package/dist/evals/engine/promptfoo/judgeProviders.js +9 -0
  112. package/dist/evals/engine/promptfoo/judgeServices.d.ts +6 -0
  113. package/dist/evals/engine/promptfoo/judgeServices.js +41 -0
  114. package/dist/evals/engine/promptfoo/locate.d.ts +21 -0
  115. package/dist/evals/engine/promptfoo/locate.js +68 -0
  116. package/dist/evals/engine/promptfoo/nodeVersion.d.ts +7 -0
  117. package/dist/evals/engine/promptfoo/nodeVersion.js +14 -0
  118. package/dist/evals/engine/promptfoo/normalize.d.ts +66 -0
  119. package/dist/evals/engine/promptfoo/normalize.js +261 -0
  120. package/dist/evals/engine/promptfoo/pinned.d.ts +5 -0
  121. package/dist/evals/engine/promptfoo/pinned.js +5 -0
  122. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +14 -0
  123. package/dist/evals/engine/promptfoo/rubricPrompt.js +103 -0
  124. package/dist/evals/engine/promptfoo/runTokens.d.ts +18 -0
  125. package/dist/evals/engine/promptfoo/runTokens.js +31 -0
  126. package/dist/evals/engine/promptfoo/status.d.ts +13 -0
  127. package/dist/evals/engine/promptfoo/status.js +16 -0
  128. package/dist/evals/estimate.d.ts +56 -0
  129. package/dist/evals/estimate.js +66 -0
  130. package/dist/evals/graders/codeGrader.d.ts +6 -0
  131. package/dist/evals/graders/codeGrader.js +85 -0
  132. package/dist/evals/graders/conversationCheckGrader.d.ts +6 -0
  133. package/dist/evals/graders/conversationCheckGrader.js +62 -0
  134. package/dist/evals/graders/fieldGrader.d.ts +11 -0
  135. package/dist/evals/graders/fieldGrader.js +117 -0
  136. package/dist/evals/graders/goal.d.ts +3 -0
  137. package/dist/evals/graders/goal.js +14 -0
  138. package/dist/evals/graders/index.d.ts +18 -0
  139. package/dist/evals/graders/index.js +34 -0
  140. package/dist/evals/graders/notApplicable.d.ts +5 -0
  141. package/dist/evals/graders/notApplicable.js +9 -0
  142. package/dist/evals/graders/path.d.ts +26 -0
  143. package/dist/evals/graders/path.js +75 -0
  144. package/dist/evals/graders/referenceGrader.d.ts +5 -0
  145. package/dist/evals/graders/referenceGrader.js +59 -0
  146. package/dist/evals/graders/schemaGrader.d.ts +5 -0
  147. package/dist/evals/graders/schemaGrader.js +29 -0
  148. package/dist/evals/graders/subject.d.ts +6 -0
  149. package/dist/evals/graders/subject.js +33 -0
  150. package/dist/evals/graders/toolCallsGrader.d.ts +6 -0
  151. package/dist/evals/graders/toolCallsGrader.js +100 -0
  152. package/dist/evals/graders/transcript.d.ts +12 -0
  153. package/dist/evals/graders/transcript.js +57 -0
  154. package/dist/evals/graders/types.d.ts +20 -0
  155. package/dist/evals/graders/types.js +1 -0
  156. package/dist/evals/judgeProviders.d.ts +16 -0
  157. package/dist/evals/judgeProviders.js +27 -0
  158. package/dist/evals/lineDiff.d.ts +5 -0
  159. package/dist/evals/lineDiff.js +34 -0
  160. package/dist/evals/listStatus.d.ts +31 -0
  161. package/dist/evals/listStatus.js +48 -0
  162. package/dist/evals/manager.d.ts +174 -0
  163. package/dist/evals/manager.js +787 -0
  164. package/dist/evals/plan.d.ts +58 -0
  165. package/dist/evals/plan.js +602 -0
  166. package/dist/evals/providerKeys.d.ts +1 -0
  167. package/dist/evals/providerKeys.js +21 -0
  168. package/dist/evals/read/agentTargets.d.ts +37 -0
  169. package/dist/evals/read/agentTargets.js +101 -0
  170. package/dist/evals/read/api.d.ts +196 -0
  171. package/dist/evals/read/api.js +358 -0
  172. package/dist/evals/read/compare.d.ts +4 -0
  173. package/dist/evals/read/compare.js +39 -0
  174. package/dist/evals/read/datasets.d.ts +105 -0
  175. package/dist/evals/read/datasets.js +318 -0
  176. package/dist/evals/read/list.d.ts +66 -0
  177. package/dist/evals/read/list.js +173 -0
  178. package/dist/evals/read/results.d.ts +46 -0
  179. package/dist/evals/read/results.js +107 -0
  180. package/dist/evals/read/runs.d.ts +149 -0
  181. package/dist/evals/read/runs.js +289 -0
  182. package/dist/evals/read/suggestions.d.ts +43 -0
  183. package/dist/evals/read/suggestions.js +73 -0
  184. package/dist/evals/runner/FakeAgentBackend.d.ts +40 -0
  185. package/dist/evals/runner/FakeAgentBackend.js +82 -0
  186. package/dist/evals/runner/agentCaseRunner.d.ts +24 -0
  187. package/dist/evals/runner/agentCaseRunner.js +388 -0
  188. package/dist/evals/runner/caseRunner.d.ts +7 -0
  189. package/dist/evals/runner/caseRunner.js +17 -0
  190. package/dist/evals/runner/conversationSubject.d.ts +18 -0
  191. package/dist/evals/runner/conversationSubject.js +123 -0
  192. package/dist/evals/runner/simulatorModel.d.ts +51 -0
  193. package/dist/evals/runner/simulatorModel.js +58 -0
  194. package/dist/evals/runner/simulatorPrompt.d.ts +29 -0
  195. package/dist/evals/runner/simulatorPrompt.js +59 -0
  196. package/dist/evals/runner/stageCaseRunner.d.ts +30 -0
  197. package/dist/evals/runner/stageCaseRunner.js +279 -0
  198. package/dist/evals/runner/userDriver.d.ts +71 -0
  199. package/dist/evals/runner/userDriver.js +104 -0
  200. package/dist/evals/scoring.d.ts +34 -0
  201. package/dist/evals/scoring.js +67 -0
  202. package/dist/evals/snapshot.d.ts +183 -0
  203. package/dist/evals/snapshot.js +65 -0
  204. package/dist/evals/stageDiff.d.ts +8 -0
  205. package/dist/evals/stageDiff.js +48 -0
  206. package/dist/evals/stats.d.ts +95 -0
  207. package/dist/evals/stats.js +220 -0
  208. package/dist/evals/statsAdapter.d.ts +4 -0
  209. package/dist/evals/statsAdapter.js +30 -0
  210. package/dist/evals/statsTypes.d.ts +51 -0
  211. package/dist/evals/statsTypes.js +18 -0
  212. package/dist/evals/storeError.d.ts +5 -0
  213. package/dist/evals/storeError.js +8 -0
  214. package/dist/evals/storeTypes.d.ts +207 -0
  215. package/dist/evals/storeTypes.js +1 -0
  216. package/dist/evals/tTable.d.ts +3 -0
  217. package/dist/evals/tTable.js +15 -0
  218. package/dist/evals/triage/constants.d.ts +26 -0
  219. package/dist/evals/triage/constants.js +33 -0
  220. package/dist/evals/triage/credentials.d.ts +13 -0
  221. package/dist/evals/triage/credentials.js +20 -0
  222. package/dist/evals/triage/jevClassifier.d.ts +14 -0
  223. package/dist/evals/triage/jevClassifier.js +124 -0
  224. package/dist/evals/triage/judgeClassifier.d.ts +10 -0
  225. package/dist/evals/triage/judgeClassifier.js +76 -0
  226. package/dist/evals/triage/prompts.d.ts +16 -0
  227. package/dist/evals/triage/prompts.js +64 -0
  228. package/dist/evals/triage/service.d.ts +79 -0
  229. package/dist/evals/triage/service.js +217 -0
  230. package/dist/evals/triage/stateBuilder.d.ts +19 -0
  231. package/dist/evals/triage/stateBuilder.js +125 -0
  232. package/dist/evals/triage/types.d.ts +60 -0
  233. package/dist/evals/triage/types.js +8 -0
  234. package/dist/evals/types.d.ts +347 -0
  235. package/dist/evals/types.js +1 -0
  236. package/dist/mcp/deps.d.ts +7 -0
  237. package/dist/mcp/evalTools.d.ts +3 -0
  238. package/dist/mcp/evalTools.js +280 -0
  239. package/dist/mcp/server.js +1 -0
  240. package/dist/mcp/tools.js +2 -0
  241. package/dist/models/response.d.ts +7 -2
  242. package/dist/models/response.js +6 -1
  243. package/dist/operatorAgent/agentAuthorContext.d.ts +1 -1
  244. package/dist/operatorAgent/agentAuthorContext.js +1 -1
  245. package/dist/operatorAgent/piModel.js +17 -4
  246. package/dist/operatorAgent/profiles/agentAuthor.d.ts +3 -1
  247. package/dist/operatorAgent/profiles/agentAuthor.js +145 -20
  248. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  249. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +48 -15
  250. package/dist/operatorAgent/profiles/authorReadable.d.ts +2 -0
  251. package/dist/operatorAgent/profiles/authorReadable.js +26 -0
  252. package/dist/package-meta.d.ts +1 -1
  253. package/dist/package-meta.js +1 -1
  254. package/dist/runstore/port.d.ts +43 -0
  255. package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -0
  256. package/dist/runstore/sqlite/SqliteRunStore.js +99 -5
  257. package/dist/runstore/sqlite/evalStore.d.ts +20 -0
  258. package/dist/runstore/sqlite/evalStore.js +541 -0
  259. package/dist/runstore/sqlite/migrations/015-evals.d.ts +12 -0
  260. package/dist/runstore/sqlite/migrations/015-evals.js +157 -0
  261. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.d.ts +12 -0
  262. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.js +20 -0
  263. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.d.ts +11 -0
  264. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.js +36 -0
  265. package/dist/runstore/sqlite/migrations/index.js +6 -0
  266. package/dist/runstore/sqlite/suggestionStore.d.ts +14 -0
  267. package/dist/runstore/sqlite/suggestionStore.js +132 -0
  268. package/dist/runtime/pipelineRunner.d.ts +2 -1
  269. package/dist/runtime/pipelineRunner.js +2 -0
  270. package/dist/runtime/runManager.d.ts +2 -1
  271. package/dist/runtime/runManager.js +8 -6
  272. package/dist/runtime/runRetentionSweep.js +2 -2
  273. package/dist/server/agentAuthorRoutes.js +50 -1
  274. package/dist/server/bootstrap.d.ts +8 -0
  275. package/dist/server/bootstrap.js +56 -0
  276. package/dist/server/controlToken.js +3 -0
  277. package/dist/server/evalInternalRoutes.d.ts +14 -0
  278. package/dist/server/evalInternalRoutes.js +87 -0
  279. package/dist/server/evalRouteContext.d.ts +46 -0
  280. package/dist/server/evalRouteContext.js +43 -0
  281. package/dist/server/evalRoutes.d.ts +5 -0
  282. package/dist/server/evalRoutes.js +1427 -0
  283. package/dist/server/evalTriageRoutes.d.ts +2 -0
  284. package/dist/server/evalTriageRoutes.js +95 -0
  285. package/dist/server/http.d.ts +5 -0
  286. package/dist/server/http.js +32 -2
  287. package/dist/server/mcpHost.js +3 -0
  288. package/dist/toolkit/meta.d.ts +1 -1
  289. package/dist/toolkit/meta.js +15 -0
  290. package/dist/toolkit/profiles.js +1 -1
  291. package/dist/types/stageflowManifest.d.ts +6 -0
  292. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +9 -0
  293. package/dist/ui/assets/{MarkdownText-D7tYaIFO.js → MarkdownText-BTrOqu5J.js} +1 -1
  294. package/dist/ui/assets/chat-Df_wCpak.js +5 -0
  295. package/dist/ui/assets/chat-P7KQznHm.css +2 -0
  296. package/dist/ui/assets/{main-BFo79krC.css → main-CHlmTNJe.css} +1 -1
  297. package/dist/ui/assets/main-CvVfxmMd.js +271 -0
  298. package/dist/ui/chat.html +3 -3
  299. package/dist/ui/index.html +3 -3
  300. package/examples/README.md +2 -0
  301. package/examples/document-policy-review/README.md +35 -0
  302. package/examples/document-policy-review/agents/compliance.agent.yaml +42 -0
  303. package/examples/document-policy-review/documents/acme-vendor-sow.md +15 -0
  304. package/examples/document-policy-review/documents/support-runbook.md +13 -0
  305. package/examples/document-policy-review/policies/acceptable-use.md +13 -0
  306. package/examples/document-policy-review/policies/data-handling.md +17 -0
  307. package/examples/document-policy-review/policies/vendor-security.md +17 -0
  308. package/examples/evals/README.md +123 -0
  309. package/examples/evals/critique-sample.dataset.jsonl +8 -0
  310. package/examples/evals/critique-sample.eval.yaml +34 -0
  311. package/examples/evals/critique-sample.task.yaml +4 -0
  312. package/examples/evals/eval-sample.pipeline.yaml +35 -0
  313. package/examples/evals/support-sample.agent.yaml +20 -0
  314. package/examples/evals/support-sample.dataset.jsonl +6 -0
  315. package/examples/evals/support-sample.eval.yaml +38 -0
  316. package/examples/stageflow.yaml +6 -0
  317. package/package.json +1 -1
  318. package/skills/stageflow/SKILL.md +5 -2
  319. package/skills/stageflow/references/control-surface.md +4 -2
  320. package/skills/stageflow/references/evals.md +68 -0
  321. package/skills/stageflow-author/SKILL.md +4 -0
  322. package/skills/stageflow-run/SKILL.md +4 -0
  323. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +0 -9
  324. package/dist/ui/assets/chat-BbLukq9W.js +0 -5
  325. package/dist/ui/assets/chat-zIZU_bP1.css +0 -2
  326. package/dist/ui/assets/main-CijYMeM0.js +0 -259
@@ -0,0 +1,342 @@
1
+ import { GUARD_FILENAME, GUARD_SOURCE } from "./guard.js";
2
+ import { EngineError } from "./errors.js";
3
+ import { mapJudge, unsupportedJudgeMessage } from "../../judgeProviders.js";
4
+ import { caseGoal, goalRubricText } from "../../graders/goal.js";
5
+ import { escapeTemplate, renderConversationRubricPrompt, renderRubricPrompt } from "./rubricPrompt.js";
6
+ export const CALLBACK_URL_ENV = "STAGEFLOW_EVAL_CALLBACK_URL";
7
+ export const RUN_TOKEN_ENV = "STAGEFLOW_EVAL_TOKEN";
8
+ export const CONFIG_FILE = "promptfooconfig.json";
9
+ export const PROVIDER_FILE = "provider.mjs";
10
+ export const ASSERTIONS_FILE = "assertions.mjs";
11
+ export const HOOKS_FILE = "hooks.mjs";
12
+ export const DATASET_FILE = "dataset.json";
13
+ export const RESULTS_STREAM_FILE = "results.ndjson";
14
+ export const RESULTS_FILE = "results.json";
15
+ export const EMPTY_EXPECTED = "(none)";
16
+ export function assertionFnName(index) {
17
+ return `grader${index}`;
18
+ }
19
+ export function buildProviderSource(evalRunId, agent = false) {
20
+ const okMeta = agent
21
+ ? `
22
+ subjectRef: reply.subjectRef,
23
+ turns: reply.output && reply.output.turns,
24
+ toolCallCount: reply.output && Array.isArray(reply.output.tool_calls) ? reply.output.tool_calls.length : undefined,`
25
+ : "";
26
+ const failKeys = agent ? '["stageRunId", "subjectRef", "latencyMs", "costUsd"]' : '["stageRunId", "latencyMs", "costUsd"]';
27
+ return `import http from "node:http";
28
+
29
+ const EVAL_RUN_ID = ${JSON.stringify(evalRunId)};
30
+
31
+ function post(url, token, payload) {
32
+ return new Promise((resolve, reject) => {
33
+ const body = JSON.stringify(payload);
34
+ const req = http.request(
35
+ url,
36
+ {
37
+ method: "POST",
38
+ headers: {
39
+ "content-type": "application/json",
40
+ "content-length": Buffer.byteLength(body),
41
+ authorization: "Bearer " + token,
42
+ },
43
+ },
44
+ (res) => {
45
+ const chunks = [];
46
+ res.on("data", (chunk) => chunks.push(chunk));
47
+ res.on("end", () => resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") }));
48
+ res.on("error", reject);
49
+ },
50
+ );
51
+ req.on("error", reject);
52
+ req.end(body);
53
+ });
54
+ }
55
+
56
+ export default class StageflowHostProvider {
57
+ id() {
58
+ return "stageflow-host";
59
+ }
60
+
61
+ async callApi(_prompt, context) {
62
+ const caseId = String(context?.vars?.caseId ?? "");
63
+ const index = context?.repeatIndex ?? context?.vars?.__repeatIndex;
64
+ const repeatIndex = Number.isInteger(index) ? index : 0;
65
+ const base = { caseId, repeatIndex };
66
+ const failure = (code, message, extra = {}) => ({
67
+ error: code + ": " + message,
68
+ metadata: { ...base, errorCode: code, ...extra },
69
+ });
70
+ try {
71
+ const url = process.env.${CALLBACK_URL_ENV};
72
+ const token = process.env.${RUN_TOKEN_ENV};
73
+ if (!url || !token) return failure("EVAL_ENGINE_ERROR", "host callback is not configured");
74
+ const res = await post(url, token, { eval_run_id: EVAL_RUN_ID, case_id: caseId, repeat_index: repeatIndex });
75
+ if (res.status !== 200) return failure("EVAL_ENGINE_ERROR", "host callback answered " + res.status);
76
+ let reply;
77
+ try {
78
+ reply = JSON.parse(res.body);
79
+ } catch {
80
+ return failure("EVAL_ENGINE_ERROR", "host callback reply is not JSON");
81
+ }
82
+ if (reply && reply.ok === true) {
83
+ return {
84
+ output: JSON.stringify(reply.output),
85
+ metadata: {
86
+ ...base,
87
+ stageRunId: reply.stageRunId,${okMeta}
88
+ latencyMs: reply.latencyMs,
89
+ costUsd: reply.costUsd,
90
+ tokensIn: reply.tokensIn,
91
+ tokensOut: reply.tokensOut,
92
+ },
93
+ };
94
+ }
95
+ const extra = {};
96
+ for (const key of ${failKeys}) {
97
+ if (reply && reply[key] !== undefined) extra[key] = reply[key];
98
+ }
99
+ return failure(
100
+ typeof reply?.code === "string" ? reply.code : "EVAL_ENGINE_ERROR",
101
+ typeof reply?.message === "string" ? reply.message : "stage case failed",
102
+ extra,
103
+ );
104
+ } catch {
105
+ return failure("EVAL_ENGINE_ERROR", "host callback failed");
106
+ }
107
+ }
108
+ }
109
+ `;
110
+ }
111
+ export function buildHooksSource(agent = false) {
112
+ const metaKeys = agent
113
+ ? '["caseId", "repeatIndex", "stageRunId", "subjectRef", "turns", "toolCallCount", "latencyMs", "costUsd", "tokensIn", "tokensOut", "errorCode"]'
114
+ : '["caseId", "repeatIndex", "stageRunId", "latencyMs", "costUsd", "tokensIn", "tokensOut", "errorCode"]';
115
+ return `import { appendFileSync } from "node:fs";
116
+
117
+ const STREAM = new URL(${JSON.stringify(RESULTS_STREAM_FILE)}, import.meta.url);
118
+ const META_KEYS = ${metaKeys};
119
+
120
+ function pickMeta(meta) {
121
+ const out = {};
122
+ if (meta && typeof meta === "object") {
123
+ for (const key of META_KEYS) if (meta[key] !== undefined) out[key] = meta[key];
124
+ }
125
+ return out;
126
+ }
127
+
128
+ function pickComponent(c) {
129
+ const graderError = c?.metadata?.graderError === true;
130
+ return {
131
+ metric: c?.assertion?.metric,
132
+ type: c?.assertion?.type,
133
+ pass: c?.pass === true,
134
+ score: typeof c?.score === "number" ? c.score : undefined,
135
+ reason: graderError || typeof c?.reason !== "string" ? undefined : c.reason,
136
+ tokensIn: typeof c?.tokensUsed?.prompt === "number" ? c.tokensUsed.prompt : undefined,
137
+ tokensOut: typeof c?.tokensUsed?.completion === "number" ? c.tokensUsed.completion : undefined,
138
+ graderError,
139
+ errorCode: typeof c?.metadata?.errorCode === "string" ? c.metadata.errorCode : undefined,
140
+ stageflow: c?.metadata?.stageflow,
141
+ };
142
+ }
143
+
144
+ export async function afterEach(context) {
145
+ try {
146
+ const r = context?.result ?? {};
147
+ const record = {
148
+ testIdx: r.testIdx,
149
+ caseId: typeof r.vars?.caseId === "string" ? r.vars.caseId : undefined,
150
+ failureReason: r.failureReason,
151
+ metadata: pickMeta(r.response?.metadata ?? r.metadata),
152
+ output: typeof r.response?.output === "string" ? r.response.output : undefined,
153
+ providerError: typeof r.response?.error === "string" ? r.response.error : undefined,
154
+ components: (r.gradingResult?.componentResults ?? []).map(pickComponent),
155
+ };
156
+ appendFileSync(STREAM, JSON.stringify(record) + "\\n");
157
+ } catch {
158
+ return;
159
+ }
160
+ }
161
+ `;
162
+ }
163
+ const STAGE_TO_SUBJECT = `function toSubject(output) {
164
+ const value = typeof output === "string" ? JSON.parse(output) : output;
165
+ return {
166
+ status: value.status,
167
+ summary: value.summary,
168
+ ...(value.payload !== undefined ? { payload: value.payload } : {}),
169
+ artifacts: Array.isArray(value.artifacts) ? value.artifacts : [],
170
+ };
171
+ }`;
172
+ const AGENT_TO_SUBJECT = `function toSubject(output) {
173
+ const value = typeof output === "string" ? JSON.parse(output) : output;
174
+ if (value.kind === "conversation") {
175
+ const { transcript: _transport, ...subject } = value;
176
+ return subject;
177
+ }
178
+ return {
179
+ status: value.status,
180
+ summary: value.summary,
181
+ ...(value.payload !== undefined ? { payload: value.payload } : {}),
182
+ artifacts: Array.isArray(value.artifacts) ? value.artifacts : [],
183
+ };
184
+ }`;
185
+ const MISSING_GOAL_GRADE = `function missingGoal(index) {
186
+ return {
187
+ pass: false,
188
+ score: 0,
189
+ reason: "EVAL_CASE_MISSING_EXPECTED",
190
+ metadata: {
191
+ graderError: true,
192
+ errorCode: "EVAL_CASE_MISSING_EXPECTED",
193
+ stageflow: {
194
+ graderId: GRADERS[index].id,
195
+ graderType: GRADERS[index].type,
196
+ status: "error",
197
+ errorCode: "EVAL_CASE_MISSING_EXPECTED",
198
+ reason: "case has no input.goal or expected.outcome",
199
+ },
200
+ },
201
+ };
202
+ }
203
+
204
+ `;
205
+ function isJudged(grader) {
206
+ return grader.type === "rubric" || grader.type === "goal_reached";
207
+ }
208
+ export function buildAssertionsSource(plan, graderModuleUrl) {
209
+ const agent = plan.target.kind === "agent";
210
+ const graders = plan.graders.map((g, index) => ({ index, grader: g }));
211
+ const exports = graders
212
+ .filter(({ grader }) => grader.type !== "rubric" && (agent || grader.type !== "goal_reached"))
213
+ .map(({ index }) => `export const ${assertionFnName(index)} = (output, context) => run(${index}, output, context);`);
214
+ return `import { readFileSync } from "node:fs";
215
+ import { gradeDeterministic } from ${JSON.stringify(graderModuleUrl)};
216
+
217
+ const GRADERS = ${JSON.stringify(plan.graders)};
218
+ const BASE_CONTEXT = ${JSON.stringify({
219
+ evalId: plan.evalId,
220
+ runId: plan.evalRunId,
221
+ passThreshold: plan.passThreshold,
222
+ ...(plan.outputSchema !== undefined ? { outputSchema: plan.outputSchema } : {}),
223
+ })};
224
+ const CASES = new Map(
225
+ JSON.parse(readFileSync(new URL(${JSON.stringify(DATASET_FILE)}, import.meta.url), "utf8")).map((c) => [c.id, c]),
226
+ );
227
+
228
+ ${agent ? AGENT_TO_SUBJECT : STAGE_TO_SUBJECT}
229
+
230
+ function engineError(index) {
231
+ return {
232
+ pass: false,
233
+ score: 0,
234
+ reason: "grader failed to run",
235
+ metadata: {
236
+ graderError: true,
237
+ errorCode: "EVAL_ENGINE_ERROR",
238
+ stageflow: { graderId: GRADERS[index].id, graderType: GRADERS[index].type, status: "error", errorCode: "EVAL_ENGINE_ERROR" },
239
+ },
240
+ };
241
+ }
242
+
243
+ ${agent ? MISSING_GOAL_GRADE : ""}function run(index, output, context) {
244
+ try {
245
+ const grader = GRADERS[index];
246
+ ${agent ? ' if (grader.type === "goal_reached") return missingGoal(index);\n' : ""} const testCase = CASES.get(context?.vars?.caseId);
247
+ if (!testCase) return engineError(index);
248
+ const repeat = context?.providerResponse?.metadata?.repeatIndex;
249
+ const grade = gradeDeterministic(grader, toSubject(output), testCase, {
250
+ ...BASE_CONTEXT,
251
+ repeat: Number.isInteger(repeat) ? repeat : 0,
252
+ });
253
+ if (grade.status === "error") {
254
+ return {
255
+ pass: false,
256
+ score: 0,
257
+ reason: grade.errorCode ?? "EVAL_ENGINE_ERROR",
258
+ metadata: { graderError: true, errorCode: grade.errorCode ?? "EVAL_ENGINE_ERROR", stageflow: grade },
259
+ };
260
+ }
261
+ return {
262
+ pass: grade.passed === true,
263
+ score: typeof grade.score === "number" ? grade.score : 0,
264
+ reason: grade.reason ?? (grade.passed === true ? "passed" : "failed"),
265
+ metadata: { stageflow: grade },
266
+ };
267
+ } catch {
268
+ return engineError(index);
269
+ }
270
+ }
271
+
272
+ ${exports.join("\n")}
273
+ `;
274
+ }
275
+ function judgeAssertion(grader, options, agent, testCase) {
276
+ const mapping = mapJudge(grader.judge);
277
+ if (mapping === null)
278
+ throw new EngineError("EVAL_INVALID", unsupportedJudgeMessage(grader.judge));
279
+ let rubric;
280
+ let prompt;
281
+ if (grader.type === "goal_reached") {
282
+ rubric = escapeTemplate(goalRubricText(caseGoal(testCase) ?? "", grader.guidance));
283
+ prompt = renderConversationRubricPrompt({ scale: "pass_fail", examples: [] });
284
+ }
285
+ else {
286
+ rubric = grader.rubric;
287
+ prompt = agent ? renderConversationRubricPrompt(grader) : renderRubricPrompt(grader);
288
+ }
289
+ return {
290
+ type: "llm-rubric",
291
+ value: rubric,
292
+ metric: grader.id,
293
+ weight: 1,
294
+ rubricPrompt: prompt,
295
+ provider: options.judgeProviderConfig === undefined
296
+ ? mapping.providerSpec
297
+ : { id: mapping.providerSpec, config: options.judgeProviderConfig },
298
+ };
299
+ }
300
+ function assertionsFor(plan, options, testCase) {
301
+ const agent = plan.target.kind === "agent";
302
+ return plan.graders.map((grader, index) => {
303
+ if (isJudged(grader) && !(grader.type === "goal_reached" && caseGoal(testCase) === undefined)) {
304
+ return judgeAssertion(grader, options, agent, testCase);
305
+ }
306
+ return {
307
+ type: "javascript",
308
+ value: `file://${ASSERTIONS_FILE}:${assertionFnName(index)}`,
309
+ metric: grader.id,
310
+ weight: 1,
311
+ };
312
+ });
313
+ }
314
+ export function buildConfig(plan, options) {
315
+ const agent = plan.target.kind === "agent";
316
+ const shared = agent ? undefined : assertionsFor(plan, options, plan.cases[0] ?? { input: {} });
317
+ return {
318
+ description: `Stageflow eval ${plan.evalId}`,
319
+ prompts: ["{{caseId}}"],
320
+ providers: [{ id: `file://${PROVIDER_FILE}` }],
321
+ extensions: [`file://${HOOKS_FILE}:afterEach`],
322
+ tests: plan.cases.map((c) => ({
323
+ description: c.id,
324
+ vars: {
325
+ caseId: c.id,
326
+ input: JSON.stringify(c.input),
327
+ expected: c.expected === undefined ? EMPTY_EXPECTED : JSON.stringify(c.expected),
328
+ },
329
+ assert: shared ?? assertionsFor(plan, options, c),
330
+ })),
331
+ };
332
+ }
333
+ export function generateFiles(plan, options) {
334
+ return {
335
+ [CONFIG_FILE]: `${JSON.stringify(buildConfig(plan, options), null, 2)}\n`,
336
+ [PROVIDER_FILE]: buildProviderSource(plan.evalRunId, plan.target.kind === "agent"),
337
+ [ASSERTIONS_FILE]: buildAssertionsSource(plan, options.graderModuleUrl),
338
+ [HOOKS_FILE]: buildHooksSource(plan.target.kind === "agent"),
339
+ [DATASET_FILE]: `${JSON.stringify(plan.cases, null, 2)}\n`,
340
+ [GUARD_FILENAME]: GUARD_SOURCE,
341
+ };
342
+ }
@@ -0,0 +1,10 @@
1
+ export declare const GUARD_FILENAME = "guard.cjs";
2
+ export declare const GUARD_SOURCE = "\"use strict\";\nconst http = require(\"node:http\");\nconst https = require(\"node:https\");\nconst { Writable } = require(\"node:stream\");\n\nconst BLOCKED = [\"promptfoo.app\", \"promptfoo.dev\"];\n\nfunction isBlocked(host) {\n if (typeof host !== \"string\") return false;\n const h = host.toLowerCase().replace(/\\.$/, \"\").replace(/^\\[|\\]$/g, \"\");\n return BLOCKED.some((d) => h === d || h.endsWith(\".\" + d));\n}\n\nfunction hostOf(input) {\n if (typeof input === \"string\") {\n try { return new URL(input).hostname; } catch { return undefined; }\n }\n if (input instanceof URL) return input.hostname;\n if (input && typeof input === \"object\") {\n if (typeof input.hostname === \"string\") return input.hostname;\n if (typeof input.host === \"string\") return input.host.replace(/:\\d+$/, \"\");\n if (typeof input.url === \"string\") return hostOf(input.url);\n }\n return undefined;\n}\n\nfunction blockedError(host) {\n const err = new Error(\"Stageflow blocked network access to \" + host);\n err.code = \"ECONNREFUSED\";\n return err;\n}\n\nfunction failedRequest(host) {\n const req = new Writable({ write(_c, _e, cb) { cb(); } });\n for (const name of [\"setHeader\", \"setTimeout\", \"setNoDelay\", \"setSocketKeepAlive\", \"flushHeaders\", \"abort\"]) {\n req[name] = () => req;\n }\n process.nextTick(() => req.emit(\"error\", blockedError(host)));\n return req;\n}\n\nfunction wrap(mod, name) {\n const original = mod[name];\n mod[name] = function (...args) {\n const host = hostOf(args[0]);\n if (isBlocked(host)) return failedRequest(host);\n return original.apply(this, args);\n };\n}\n\nfor (const mod of [http, https]) {\n wrap(mod, \"request\");\n wrap(mod, \"get\");\n}\n\nif (typeof globalThis.fetch === \"function\") {\n const originalFetch = globalThis.fetch;\n globalThis.fetch = function (input, init) {\n const host = hostOf(input);\n if (isBlocked(host)) return Promise.reject(blockedError(host));\n return originalFetch.call(this, input, init);\n };\n}\n\ntry { require(\"node:module\").syncBuiltinESMExports(); } catch {}\n";
3
+ export declare function writeGuardFile(dir: string): string;
4
+ export type GuardVia = "fetch" | "http" | "https";
5
+ export type GuardProbeResult = {
6
+ blocked: boolean;
7
+ status?: number;
8
+ code?: string;
9
+ };
10
+ export declare function probeGuard(via: GuardVia, url: string): Promise<GuardProbeResult>;
@@ -0,0 +1,116 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ export const GUARD_FILENAME = "guard.cjs";
6
+ export const GUARD_SOURCE = `"use strict";
7
+ const http = require("node:http");
8
+ const https = require("node:https");
9
+ const { Writable } = require("node:stream");
10
+
11
+ const BLOCKED = ["promptfoo.app", "promptfoo.dev"];
12
+
13
+ function isBlocked(host) {
14
+ if (typeof host !== "string") return false;
15
+ const h = host.toLowerCase().replace(/\\.$/, "").replace(/^\\[|\\]$/g, "");
16
+ return BLOCKED.some((d) => h === d || h.endsWith("." + d));
17
+ }
18
+
19
+ function hostOf(input) {
20
+ if (typeof input === "string") {
21
+ try { return new URL(input).hostname; } catch { return undefined; }
22
+ }
23
+ if (input instanceof URL) return input.hostname;
24
+ if (input && typeof input === "object") {
25
+ if (typeof input.hostname === "string") return input.hostname;
26
+ if (typeof input.host === "string") return input.host.replace(/:\\d+$/, "");
27
+ if (typeof input.url === "string") return hostOf(input.url);
28
+ }
29
+ return undefined;
30
+ }
31
+
32
+ function blockedError(host) {
33
+ const err = new Error("Stageflow blocked network access to " + host);
34
+ err.code = "ECONNREFUSED";
35
+ return err;
36
+ }
37
+
38
+ function failedRequest(host) {
39
+ const req = new Writable({ write(_c, _e, cb) { cb(); } });
40
+ for (const name of ["setHeader", "setTimeout", "setNoDelay", "setSocketKeepAlive", "flushHeaders", "abort"]) {
41
+ req[name] = () => req;
42
+ }
43
+ process.nextTick(() => req.emit("error", blockedError(host)));
44
+ return req;
45
+ }
46
+
47
+ function wrap(mod, name) {
48
+ const original = mod[name];
49
+ mod[name] = function (...args) {
50
+ const host = hostOf(args[0]);
51
+ if (isBlocked(host)) return failedRequest(host);
52
+ return original.apply(this, args);
53
+ };
54
+ }
55
+
56
+ for (const mod of [http, https]) {
57
+ wrap(mod, "request");
58
+ wrap(mod, "get");
59
+ }
60
+
61
+ if (typeof globalThis.fetch === "function") {
62
+ const originalFetch = globalThis.fetch;
63
+ globalThis.fetch = function (input, init) {
64
+ const host = hostOf(input);
65
+ if (isBlocked(host)) return Promise.reject(blockedError(host));
66
+ return originalFetch.call(this, input, init);
67
+ };
68
+ }
69
+
70
+ try { require("node:module").syncBuiltinESMExports(); } catch {}
71
+ `;
72
+ export function writeGuardFile(dir) {
73
+ const file = path.join(dir, GUARD_FILENAME);
74
+ writeFileSync(file, GUARD_SOURCE);
75
+ return file;
76
+ }
77
+ const PROBE_SCRIPT = `
78
+ const [via, url] = process.argv.slice(1);
79
+ const out = (o) => { process.stdout.write(JSON.stringify(o)); process.exit(0); };
80
+ const fail = (e) => out({ blocked: true, code: e && e.code });
81
+ if (via === "fetch") {
82
+ fetch(url).then((r) => out({ blocked: false, status: r.status }), fail);
83
+ } else {
84
+ const mod = require(via);
85
+ try {
86
+ const req = mod.request(url, (res) => { res.resume(); out({ blocked: false, status: res.statusCode }); });
87
+ req.on("error", fail);
88
+ req.end();
89
+ } catch (e) { fail(e); }
90
+ }
91
+ `;
92
+ export async function probeGuard(via, url) {
93
+ const dir = mkdtempSync(path.join(tmpdir(), "sf-guard-"));
94
+ try {
95
+ const guard = writeGuardFile(dir);
96
+ return await new Promise((resolve, reject) => {
97
+ const child = spawn(process.execPath, ["--require", guard, "-e", PROBE_SCRIPT, via, url], { stdio: ["ignore", "pipe", "pipe"] });
98
+ let stdout = "";
99
+ let stderr = "";
100
+ child.stdout.on("data", (d) => (stdout += d));
101
+ child.stderr.on("data", (d) => (stderr += d));
102
+ child.on("error", reject);
103
+ child.on("close", () => {
104
+ try {
105
+ resolve(JSON.parse(stdout));
106
+ }
107
+ catch {
108
+ reject(new Error(`guard probe failed: ${stderr || stdout}`));
109
+ }
110
+ });
111
+ });
112
+ }
113
+ finally {
114
+ rmSync(dir, { recursive: true, force: true });
115
+ }
116
+ }
@@ -0,0 +1,17 @@
1
+ export type RunNpm = (args: string[], opts: {
2
+ cwd: string;
3
+ log: (line: string) => void;
4
+ }) => Promise<{
5
+ code: number;
6
+ }>;
7
+ export type InstallResult = {
8
+ status: "installed" | "already_installed";
9
+ version: string;
10
+ dir: string;
11
+ };
12
+ export declare const realRunNpm: RunNpm;
13
+ export declare function installPromptfoo(opts: {
14
+ home: string;
15
+ runNpm?: RunNpm;
16
+ log?: (line: string) => void;
17
+ }): Promise<InstallResult>;
@@ -0,0 +1,73 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { createInterface } from "node:readline";
5
+ import { EngineError } from "./errors.js";
6
+ import { engineDir, engineRoot, locatePromptfoo } from "./locate.js";
7
+ import { assertNode } from "./nodeVersion.js";
8
+ import { ENGINE_INSTALL_COMMAND, ENGINE_MARKER_FILE, PINNED_PROMPTFOO_VERSION, } from "./pinned.js";
9
+ export const realRunNpm = (args, { cwd, log }) => new Promise((resolve, reject) => {
10
+ const nodeDir = path.dirname(process.execPath);
11
+ const npm = path.join(nodeDir, process.platform === "win32" ? "npm.cmd" : "npm");
12
+ const child = spawn(npm, args, {
13
+ cwd,
14
+ env: { ...process.env, PATH: `${nodeDir}${path.delimiter}${process.env.PATH ?? ""}` },
15
+ stdio: ["ignore", "pipe", "pipe"],
16
+ shell: process.platform === "win32",
17
+ });
18
+ for (const stream of [child.stdout, child.stderr]) {
19
+ createInterface({ input: stream }).on("line", log);
20
+ }
21
+ child.on("error", reject);
22
+ child.on("close", (code) => resolve({ code: code ?? 1 }));
23
+ });
24
+ function readIntegrity(dir) {
25
+ try {
26
+ const lock = JSON.parse(readFileSync(path.join(dir, "package-lock.json"), "utf8"));
27
+ return lock.packages?.["node_modules/promptfoo"]?.integrity ?? null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ export async function installPromptfoo(opts) {
34
+ const log = opts.log ?? (() => { });
35
+ const runNpm = opts.runNpm ?? realRunNpm;
36
+ assertNode();
37
+ const dir = engineDir(opts.home);
38
+ const existing = locatePromptfoo({ home: opts.home });
39
+ if (existing.installed) {
40
+ log(`promptfoo ${existing.version} is already installed`);
41
+ return { status: "already_installed", version: existing.version, dir };
42
+ }
43
+ const root = engineRoot(opts.home);
44
+ const rootExisted = existsSync(root);
45
+ rmSync(dir, { recursive: true, force: true });
46
+ mkdirSync(dir, { recursive: true });
47
+ try {
48
+ writeFileSync(path.join(dir, "package.json"), `${JSON.stringify({ name: "stageflow-eval-engine", private: true }, null, 2)}\n`);
49
+ const { code } = await runNpm(["install", "--no-audit", "--no-fund", `promptfoo@${PINNED_PROMPTFOO_VERSION}`], { cwd: dir, log });
50
+ if (code !== 0) {
51
+ throw new EngineError("EVAL_ENGINE_UNAVAILABLE", `npm install promptfoo@${PINNED_PROMPTFOO_VERSION} failed (exit ${code}). Retry with \`${ENGINE_INSTALL_COMMAND}\`.`);
52
+ }
53
+ const marker = {
54
+ version: PINNED_PROMPTFOO_VERSION,
55
+ node: process.versions.node,
56
+ installedAt: new Date().toISOString(),
57
+ integrity: readIntegrity(dir),
58
+ };
59
+ writeFileSync(path.join(dir, ENGINE_MARKER_FILE), `${JSON.stringify(marker, null, 2)}\n`);
60
+ const verified = locatePromptfoo({ home: opts.home });
61
+ if (!verified.installed) {
62
+ throw new EngineError("EVAL_ENGINE_UNAVAILABLE", verified.reason);
63
+ }
64
+ }
65
+ catch (err) {
66
+ rmSync(dir, { recursive: true, force: true });
67
+ if (!rootExisted)
68
+ rmSync(root, { recursive: true, force: true });
69
+ throw err;
70
+ }
71
+ log(`installed promptfoo ${PINNED_PROMPTFOO_VERSION}`);
72
+ return { status: "installed", version: PINNED_PROMPTFOO_VERSION, dir };
73
+ }
@@ -0,0 +1,2 @@
1
+ import type { ResolvedGrader } from "../../types.js";
2
+ export declare function assertJudgesSupported(graders: readonly ResolvedGrader[]): void;
@@ -0,0 +1,9 @@
1
+ import { mapJudge, unsupportedJudgeMessage } from "../../judgeProviders.js";
2
+ import { EngineError } from "./errors.js";
3
+ export function assertJudgesSupported(graders) {
4
+ for (const grader of graders) {
5
+ if ((grader.type === "rubric" || grader.type === "goal_reached") && mapJudge(grader.judge) === null) {
6
+ throw new EngineError("EVAL_INVALID", unsupportedJudgeMessage(grader.judge));
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ import type { JudgeMapping } from "../../judgeProviders.js";
2
+ import type { JudgePrice } from "./normalize.js";
3
+ export type JudgeKeyResolver = (mapping: JudgeMapping, projectRoot: string) => Promise<string | undefined> | string | undefined;
4
+ export type JudgePriceLoader = (modelIds: readonly string[], projectRoot: string) => Promise<Map<string, JudgePrice>>;
5
+ export declare const defaultResolveJudgeKey: JudgeKeyResolver;
6
+ export declare const defaultLoadJudgePrices: JudgePriceLoader;
@@ -0,0 +1,41 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
4
+ import { resolveCredentialBinding } from "../../../runtime/credentialBinding.js";
5
+ import { readAuthKey } from "../../providerKeys.js";
6
+ export const defaultResolveJudgeKey = (mapping, projectRoot) => {
7
+ const fromStore = readAuthKey(resolveCredentialBinding(projectRoot).authPath, mapping.stageflowProvider);
8
+ if (fromStore !== undefined)
9
+ return fromStore;
10
+ const fromEnv = process.env[mapping.envVar];
11
+ return fromEnv !== undefined && fromEnv.length > 0 ? fromEnv : undefined;
12
+ };
13
+ export const defaultLoadJudgePrices = async (modelIds, projectRoot) => {
14
+ const prices = new Map();
15
+ if (modelIds.length === 0)
16
+ return prices;
17
+ try {
18
+ const authPath = resolveCredentialBinding(projectRoot).authPath;
19
+ if (!existsSync(authPath))
20
+ return prices;
21
+ const runtime = await ModelRuntime.create({
22
+ authPath,
23
+ modelsPath: path.join(path.dirname(authPath), "models.json"),
24
+ refreshOnCreate: false,
25
+ });
26
+ for (const id of modelIds) {
27
+ const slash = id.indexOf("/");
28
+ const model = runtime
29
+ .getModels(id.slice(0, slash))
30
+ .find((candidate) => candidate.id === id.slice(slash + 1));
31
+ const cost = model?.cost;
32
+ if (cost !== undefined && typeof cost.input === "number" && typeof cost.output === "number") {
33
+ prices.set(id, { inputPerMillion: cost.input, outputPerMillion: cost.output });
34
+ }
35
+ }
36
+ }
37
+ catch {
38
+ return prices;
39
+ }
40
+ return prices;
41
+ };
@@ -0,0 +1,21 @@
1
+ export type EngineMarker = {
2
+ version: string;
3
+ node: string;
4
+ installedAt: string;
5
+ integrity: string | null;
6
+ };
7
+ export type LocateResult = {
8
+ installed: true;
9
+ version: string;
10
+ binPath: string;
11
+ dir: string;
12
+ } | {
13
+ installed: false;
14
+ reason: string;
15
+ };
16
+ export declare function engineRoot(home: string): string;
17
+ export declare function engineDir(home: string, version?: string): string;
18
+ export declare function engineBinPath(dir: string): string;
19
+ export declare function locatePromptfoo(opts: {
20
+ home: string;
21
+ }): LocateResult;