stageflow 0.39.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.
- package/README.md +46 -19
- package/dist/agent/claudeAdapter.js +15 -1
- package/dist/agent/claudeTools.d.ts +13 -0
- package/dist/agent/claudeTools.js +98 -0
- package/dist/agent/piAdapter.js +38 -16
- package/dist/agent/sandboxPiTools.d.ts +99 -0
- package/dist/agent/sandboxPiTools.js +109 -0
- package/dist/agents/authorChat.d.ts +4 -0
- package/dist/agents/authorChat.js +113 -12
- package/dist/agents/authorSessionStore.d.ts +35 -0
- package/dist/agents/authorSessionStore.js +152 -0
- package/dist/agents/chatTurn.d.ts +2 -0
- package/dist/agents/chatTurn.js +17 -1
- package/dist/agents/errors.d.ts +1 -1
- package/dist/agents/errors.js +1 -0
- package/dist/agents/evalGate.d.ts +61 -0
- package/dist/agents/evalGate.js +103 -0
- package/dist/agents/evalSession.d.ts +26 -0
- package/dist/agents/evalSession.js +29 -0
- package/dist/agents/hostContext.d.ts +3 -0
- package/dist/agents/hostContext.js +4 -1
- package/dist/agents/runtime/piRuntime.d.ts +1 -1
- package/dist/agents/runtime/piRuntime.js +4 -4
- package/dist/agents/runtime/types.d.ts +3 -1
- package/dist/agents/service.d.ts +27 -2
- package/dist/agents/service.js +88 -13
- package/dist/agents/sessions.d.ts +8 -0
- package/dist/agents/sessions.js +50 -10
- package/dist/agents/store.d.ts +14 -3
- package/dist/agents/store.js +45 -7
- package/dist/agents/toolRisk.d.ts +21 -0
- package/dist/agents/toolRisk.js +50 -0
- package/dist/cli/doctorCommand.js +26 -0
- package/dist/cli/evalCommand.d.ts +20 -0
- package/dist/cli/evalCommand.js +720 -0
- package/dist/cli/exportAllCommand.js +1 -1
- package/dist/cli/hostClient.d.ts +60 -0
- package/dist/cli/hostClient.js +71 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.js +10 -0
- package/dist/config/atomicWrite.d.ts +15 -0
- package/dist/config/atomicWrite.js +43 -0
- package/dist/config/browseCatalog.d.ts +2 -0
- package/dist/config/browseCatalog.js +4 -2
- package/dist/config/csv.d.ts +9 -0
- package/dist/config/csv.js +81 -0
- package/dist/config/datasetAgentCheck.d.ts +18 -0
- package/dist/config/datasetAgentCheck.js +109 -0
- package/dist/config/datasetCandidates.d.ts +22 -0
- package/dist/config/datasetCandidates.js +86 -0
- package/dist/config/datasetFromSessions.d.ts +34 -0
- package/dist/config/datasetFromSessions.js +105 -0
- package/dist/config/datasetHash.d.ts +1 -0
- package/dist/config/datasetHash.js +4 -0
- package/dist/config/datasetImport.d.ts +21 -0
- package/dist/config/datasetImport.js +120 -0
- package/dist/config/datasetStageCheck.d.ts +9 -0
- package/dist/config/datasetStageCheck.js +17 -0
- package/dist/config/datasetTypes.d.ts +27 -0
- package/dist/config/datasetTypes.js +1 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
- package/dist/config/ensureCatalogScanRoot.js +2 -6
- package/dist/config/evalCatalog.d.ts +22 -0
- package/dist/config/evalCatalog.js +67 -0
- package/dist/config/evalWriteRoots.d.ts +17 -0
- package/dist/config/evalWriteRoots.js +109 -0
- package/dist/config/hostConfig.js +3 -0
- package/dist/config/installEvalSample.d.ts +34 -0
- package/dist/config/installEvalSample.js +182 -0
- package/dist/config/loadDataset.d.ts +16 -0
- package/dist/config/loadDataset.js +318 -0
- package/dist/config/loadEval.d.ts +32 -0
- package/dist/config/loadEval.js +1085 -0
- package/dist/config/loadOutcome.d.ts +5 -0
- package/dist/config/loadStageflowManifest.js +64 -0
- package/dist/config/loadTask.js +34 -1
- package/dist/config/resolveAgentTarget.d.ts +32 -0
- package/dist/config/resolveAgentTarget.js +118 -0
- package/dist/config/resolveEvalTarget.d.ts +21 -0
- package/dist/config/resolveEvalTarget.js +89 -0
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +17 -5
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +9 -1
- package/dist/config/validateEval.d.ts +36 -0
- package/dist/config/validateEval.js +405 -0
- package/dist/config/writeDataset.d.ts +54 -0
- package/dist/config/writeDataset.js +186 -0
- package/dist/config/writeEval.d.ts +25 -0
- package/dist/config/writeEval.js +398 -0
- package/dist/diagnostics/checks.js +1 -1
- package/dist/evals/agentDiff.d.ts +13 -0
- package/dist/evals/agentDiff.js +11 -0
- package/dist/evals/canonical.d.ts +3 -0
- package/dist/evals/canonical.js +24 -0
- package/dist/evals/caseTask.d.ts +3 -0
- package/dist/evals/caseTask.js +9 -0
- package/dist/evals/compare.d.ts +94 -0
- package/dist/evals/compare.js +201 -0
- package/dist/evals/constants.d.ts +60 -0
- package/dist/evals/constants.js +95 -0
- package/dist/evals/engine/FakeEngine.d.ts +33 -0
- package/dist/evals/engine/FakeEngine.js +176 -0
- package/dist/evals/engine/index.d.ts +9 -0
- package/dist/evals/engine/index.js +19 -0
- package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +52 -0
- package/dist/evals/engine/promptfoo/PromptfooEngine.js +311 -0
- package/dist/evals/engine/promptfoo/errors.d.ts +5 -0
- package/dist/evals/engine/promptfoo/errors.js +8 -0
- package/dist/evals/engine/promptfoo/generate.d.ts +30 -0
- package/dist/evals/engine/promptfoo/generate.js +342 -0
- package/dist/evals/engine/promptfoo/guard.d.ts +10 -0
- package/dist/evals/engine/promptfoo/guard.js +116 -0
- package/dist/evals/engine/promptfoo/install.d.ts +17 -0
- package/dist/evals/engine/promptfoo/install.js +73 -0
- package/dist/evals/engine/promptfoo/judgeProviders.d.ts +2 -0
- package/dist/evals/engine/promptfoo/judgeProviders.js +9 -0
- package/dist/evals/engine/promptfoo/judgeServices.d.ts +6 -0
- package/dist/evals/engine/promptfoo/judgeServices.js +41 -0
- package/dist/evals/engine/promptfoo/locate.d.ts +21 -0
- package/dist/evals/engine/promptfoo/locate.js +68 -0
- package/dist/evals/engine/promptfoo/nodeVersion.d.ts +7 -0
- package/dist/evals/engine/promptfoo/nodeVersion.js +14 -0
- package/dist/evals/engine/promptfoo/normalize.d.ts +66 -0
- package/dist/evals/engine/promptfoo/normalize.js +261 -0
- package/dist/evals/engine/promptfoo/pinned.d.ts +5 -0
- package/dist/evals/engine/promptfoo/pinned.js +5 -0
- package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +14 -0
- package/dist/evals/engine/promptfoo/rubricPrompt.js +103 -0
- package/dist/evals/engine/promptfoo/runTokens.d.ts +18 -0
- package/dist/evals/engine/promptfoo/runTokens.js +31 -0
- package/dist/evals/engine/promptfoo/status.d.ts +13 -0
- package/dist/evals/engine/promptfoo/status.js +16 -0
- package/dist/evals/estimate.d.ts +56 -0
- package/dist/evals/estimate.js +66 -0
- package/dist/evals/graders/codeGrader.d.ts +6 -0
- package/dist/evals/graders/codeGrader.js +85 -0
- package/dist/evals/graders/conversationCheckGrader.d.ts +6 -0
- package/dist/evals/graders/conversationCheckGrader.js +62 -0
- package/dist/evals/graders/fieldGrader.d.ts +11 -0
- package/dist/evals/graders/fieldGrader.js +117 -0
- package/dist/evals/graders/goal.d.ts +3 -0
- package/dist/evals/graders/goal.js +14 -0
- package/dist/evals/graders/index.d.ts +18 -0
- package/dist/evals/graders/index.js +34 -0
- package/dist/evals/graders/notApplicable.d.ts +5 -0
- package/dist/evals/graders/notApplicable.js +9 -0
- package/dist/evals/graders/path.d.ts +26 -0
- package/dist/evals/graders/path.js +75 -0
- package/dist/evals/graders/referenceGrader.d.ts +5 -0
- package/dist/evals/graders/referenceGrader.js +59 -0
- package/dist/evals/graders/schemaGrader.d.ts +5 -0
- package/dist/evals/graders/schemaGrader.js +29 -0
- package/dist/evals/graders/subject.d.ts +6 -0
- package/dist/evals/graders/subject.js +33 -0
- package/dist/evals/graders/toolCallsGrader.d.ts +6 -0
- package/dist/evals/graders/toolCallsGrader.js +100 -0
- package/dist/evals/graders/transcript.d.ts +12 -0
- package/dist/evals/graders/transcript.js +57 -0
- package/dist/evals/graders/types.d.ts +20 -0
- package/dist/evals/graders/types.js +1 -0
- package/dist/evals/judgeProviders.d.ts +16 -0
- package/dist/evals/judgeProviders.js +27 -0
- package/dist/evals/lineDiff.d.ts +5 -0
- package/dist/evals/lineDiff.js +34 -0
- package/dist/evals/listStatus.d.ts +31 -0
- package/dist/evals/listStatus.js +48 -0
- package/dist/evals/manager.d.ts +174 -0
- package/dist/evals/manager.js +787 -0
- package/dist/evals/plan.d.ts +58 -0
- package/dist/evals/plan.js +602 -0
- package/dist/evals/providerKeys.d.ts +1 -0
- package/dist/evals/providerKeys.js +21 -0
- package/dist/evals/read/agentTargets.d.ts +37 -0
- package/dist/evals/read/agentTargets.js +101 -0
- package/dist/evals/read/api.d.ts +196 -0
- package/dist/evals/read/api.js +358 -0
- package/dist/evals/read/compare.d.ts +4 -0
- package/dist/evals/read/compare.js +39 -0
- package/dist/evals/read/datasets.d.ts +105 -0
- package/dist/evals/read/datasets.js +318 -0
- package/dist/evals/read/list.d.ts +66 -0
- package/dist/evals/read/list.js +173 -0
- package/dist/evals/read/results.d.ts +46 -0
- package/dist/evals/read/results.js +107 -0
- package/dist/evals/read/runs.d.ts +149 -0
- package/dist/evals/read/runs.js +289 -0
- package/dist/evals/read/suggestions.d.ts +43 -0
- package/dist/evals/read/suggestions.js +73 -0
- package/dist/evals/runner/FakeAgentBackend.d.ts +40 -0
- package/dist/evals/runner/FakeAgentBackend.js +82 -0
- package/dist/evals/runner/agentCaseRunner.d.ts +24 -0
- package/dist/evals/runner/agentCaseRunner.js +388 -0
- package/dist/evals/runner/caseRunner.d.ts +7 -0
- package/dist/evals/runner/caseRunner.js +17 -0
- package/dist/evals/runner/conversationSubject.d.ts +18 -0
- package/dist/evals/runner/conversationSubject.js +123 -0
- package/dist/evals/runner/simulatorModel.d.ts +51 -0
- package/dist/evals/runner/simulatorModel.js +58 -0
- package/dist/evals/runner/simulatorPrompt.d.ts +29 -0
- package/dist/evals/runner/simulatorPrompt.js +59 -0
- package/dist/evals/runner/stageCaseRunner.d.ts +30 -0
- package/dist/evals/runner/stageCaseRunner.js +279 -0
- package/dist/evals/runner/userDriver.d.ts +71 -0
- package/dist/evals/runner/userDriver.js +104 -0
- package/dist/evals/scoring.d.ts +34 -0
- package/dist/evals/scoring.js +67 -0
- package/dist/evals/snapshot.d.ts +183 -0
- package/dist/evals/snapshot.js +65 -0
- package/dist/evals/stageDiff.d.ts +8 -0
- package/dist/evals/stageDiff.js +48 -0
- package/dist/evals/stats.d.ts +95 -0
- package/dist/evals/stats.js +220 -0
- package/dist/evals/statsAdapter.d.ts +4 -0
- package/dist/evals/statsAdapter.js +30 -0
- package/dist/evals/statsTypes.d.ts +51 -0
- package/dist/evals/statsTypes.js +18 -0
- package/dist/evals/storeError.d.ts +5 -0
- package/dist/evals/storeError.js +8 -0
- package/dist/evals/storeTypes.d.ts +207 -0
- package/dist/evals/storeTypes.js +1 -0
- package/dist/evals/tTable.d.ts +3 -0
- package/dist/evals/tTable.js +15 -0
- package/dist/evals/triage/constants.d.ts +26 -0
- package/dist/evals/triage/constants.js +33 -0
- package/dist/evals/triage/credentials.d.ts +13 -0
- package/dist/evals/triage/credentials.js +20 -0
- package/dist/evals/triage/jevClassifier.d.ts +14 -0
- package/dist/evals/triage/jevClassifier.js +124 -0
- package/dist/evals/triage/judgeClassifier.d.ts +10 -0
- package/dist/evals/triage/judgeClassifier.js +76 -0
- package/dist/evals/triage/prompts.d.ts +16 -0
- package/dist/evals/triage/prompts.js +64 -0
- package/dist/evals/triage/service.d.ts +79 -0
- package/dist/evals/triage/service.js +217 -0
- package/dist/evals/triage/stateBuilder.d.ts +19 -0
- package/dist/evals/triage/stateBuilder.js +125 -0
- package/dist/evals/triage/types.d.ts +60 -0
- package/dist/evals/triage/types.js +8 -0
- package/dist/evals/types.d.ts +347 -0
- package/dist/evals/types.js +1 -0
- package/dist/mcp/catalogTools.js +11 -1
- package/dist/mcp/deps.d.ts +7 -0
- package/dist/mcp/evalTools.d.ts +3 -0
- package/dist/mcp/evalTools.js +280 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +2 -0
- package/dist/models/response.d.ts +7 -2
- package/dist/models/response.js +6 -1
- package/dist/operatorAgent/agentAuthorContext.d.ts +1 -1
- package/dist/operatorAgent/agentAuthorContext.js +1 -1
- package/dist/operatorAgent/piModel.js +17 -4
- package/dist/operatorAgent/profiles/agentAuthor.d.ts +3 -1
- package/dist/operatorAgent/profiles/agentAuthor.js +145 -20
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +48 -15
- package/dist/operatorAgent/profiles/authorReadable.d.ts +2 -0
- package/dist/operatorAgent/profiles/authorReadable.js +26 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/runstore/port.d.ts +43 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +99 -5
- package/dist/runstore/sqlite/evalStore.d.ts +20 -0
- package/dist/runstore/sqlite/evalStore.js +541 -0
- package/dist/runstore/sqlite/migrations/015-evals.d.ts +12 -0
- package/dist/runstore/sqlite/migrations/015-evals.js +157 -0
- package/dist/runstore/sqlite/migrations/016-eval-subject-ref.d.ts +12 -0
- package/dist/runstore/sqlite/migrations/016-eval-subject-ref.js +20 -0
- package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.d.ts +11 -0
- package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.js +36 -0
- package/dist/runstore/sqlite/migrations/index.js +6 -0
- package/dist/runstore/sqlite/suggestionStore.d.ts +14 -0
- package/dist/runstore/sqlite/suggestionStore.js +132 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
- package/dist/runtime/gitCheckoutCapability.js +1 -1
- package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
- package/dist/runtime/gitWorktreeCheckout.js +120 -0
- package/dist/runtime/pipelineRunner.d.ts +2 -1
- package/dist/runtime/pipelineRunner.js +2 -0
- package/dist/runtime/runManager.d.ts +2 -1
- package/dist/runtime/runManager.js +8 -6
- package/dist/runtime/runRetentionSweep.js +2 -2
- package/dist/runtime/sandboxContainer.d.ts +62 -0
- package/dist/runtime/sandboxContainer.js +151 -0
- package/dist/runtime/sandboxDesk.d.ts +28 -0
- package/dist/runtime/sandboxDesk.js +242 -0
- package/dist/runtime/sandboxFiles.d.ts +20 -0
- package/dist/runtime/sandboxFiles.js +150 -0
- package/dist/runtime/stageEnvironment.js +2 -0
- package/dist/runtime/stageRoots.d.ts +9 -0
- package/dist/runtime/stageRoots.js +14 -0
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +38 -4
- package/dist/runtime/stageSandbox.d.ts +16 -0
- package/dist/runtime/stageSandbox.js +54 -0
- package/dist/runtime/stageWorker.js +138 -105
- package/dist/runtime/workspaceBinding.d.ts +2 -1
- package/dist/runtime/workspaceBinding.js +20 -4
- package/dist/server/agentAuthorRoutes.js +50 -1
- package/dist/server/bootstrap.d.ts +8 -0
- package/dist/server/bootstrap.js +56 -0
- package/dist/server/controlToken.js +3 -0
- package/dist/server/evalInternalRoutes.d.ts +14 -0
- package/dist/server/evalInternalRoutes.js +87 -0
- package/dist/server/evalRouteContext.d.ts +46 -0
- package/dist/server/evalRouteContext.js +43 -0
- package/dist/server/evalRoutes.d.ts +5 -0
- package/dist/server/evalRoutes.js +1427 -0
- package/dist/server/evalTriageRoutes.d.ts +2 -0
- package/dist/server/evalTriageRoutes.js +95 -0
- package/dist/server/http.d.ts +5 -0
- package/dist/server/http.js +32 -2
- package/dist/server/mcpHost.js +3 -0
- package/dist/toolkit/meta.d.ts +1 -1
- package/dist/toolkit/meta.js +15 -0
- package/dist/toolkit/profiles.js +1 -1
- package/dist/tools/readEmail.d.ts +3 -3
- package/dist/types/stageflowManifest.d.ts +6 -0
- package/dist/types/task.d.ts +11 -1
- package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +9 -0
- package/dist/ui/assets/{MarkdownText-D7tYaIFO.js → MarkdownText-BTrOqu5J.js} +1 -1
- package/dist/ui/assets/chat-Df_wCpak.js +5 -0
- package/dist/ui/assets/chat-P7KQznHm.css +2 -0
- package/dist/ui/assets/{main-BFo79krC.css → main-CHlmTNJe.css} +1 -1
- package/dist/ui/assets/main-CvVfxmMd.js +271 -0
- package/dist/ui/chat.html +3 -3
- package/dist/ui/index.html +3 -3
- package/examples/README.md +2 -0
- package/examples/document-policy-review/README.md +35 -0
- package/examples/document-policy-review/agents/compliance.agent.yaml +42 -0
- package/examples/document-policy-review/documents/acme-vendor-sow.md +15 -0
- package/examples/document-policy-review/documents/support-runbook.md +13 -0
- package/examples/document-policy-review/policies/acceptable-use.md +13 -0
- package/examples/document-policy-review/policies/data-handling.md +17 -0
- package/examples/document-policy-review/policies/vendor-security.md +17 -0
- package/examples/evals/README.md +123 -0
- package/examples/evals/critique-sample.dataset.jsonl +8 -0
- package/examples/evals/critique-sample.eval.yaml +34 -0
- package/examples/evals/critique-sample.task.yaml +4 -0
- package/examples/evals/eval-sample.pipeline.yaml +35 -0
- package/examples/evals/support-sample.agent.yaml +20 -0
- package/examples/evals/support-sample.dataset.jsonl +6 -0
- package/examples/evals/support-sample.eval.yaml +38 -0
- package/examples/memory/README.md +2 -0
- package/examples/memory/probe-bash.task.yaml +3 -0
- package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
- package/examples/ship-feature/README.md +15 -0
- package/examples/stageflow.yaml +6 -0
- package/package.json +1 -1
- package/skills/stageflow/SKILL.md +5 -2
- package/skills/stageflow/references/control-surface.md +4 -2
- package/skills/stageflow/references/evals.md +68 -0
- package/skills/stageflow-author/SKILL.md +4 -0
- package/skills/stageflow-run/SKILL.md +4 -0
- package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +0 -9
- package/dist/ui/assets/chat-BbLukq9W.js +0 -5
- package/dist/ui/assets/chat-zIZU_bP1.css +0 -2
- package/dist/ui/assets/main-CijYMeM0.js +0 -259
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { EVAL_LIMITS, EVAL_RUN_ID_PREFIX } from "../../evals/constants.js";
|
|
3
|
+
import { EvalStoreError } from "../../evals/storeError.js";
|
|
4
|
+
const now = () => new Date().toISOString();
|
|
5
|
+
const bool = (value) => (value === null ? null : value !== 0);
|
|
6
|
+
const json = (value) => (value === undefined || value === null ? null : JSON.stringify(value));
|
|
7
|
+
const parse = (value) => (value === null ? null : JSON.parse(value));
|
|
8
|
+
function runFromDb(row) {
|
|
9
|
+
return {
|
|
10
|
+
evalRunId: row.eval_run_id,
|
|
11
|
+
projectRoot: row.project_root,
|
|
12
|
+
evalId: row.eval_id,
|
|
13
|
+
runNumber: row.run_number,
|
|
14
|
+
evalPath: row.eval_path,
|
|
15
|
+
evalHash: row.eval_hash,
|
|
16
|
+
targetKind: row.target_kind,
|
|
17
|
+
targetRef: JSON.parse(row.target_ref_json),
|
|
18
|
+
targetVersionHash: row.target_version_hash,
|
|
19
|
+
datasetPath: row.dataset_path,
|
|
20
|
+
datasetHash: row.dataset_hash,
|
|
21
|
+
config: JSON.parse(row.config_json),
|
|
22
|
+
configFingerprint: row.config_fingerprint,
|
|
23
|
+
configComponents: JSON.parse(row.config_components_json),
|
|
24
|
+
status: row.status,
|
|
25
|
+
statusReason: row.status_reason,
|
|
26
|
+
passed: bool(row.passed),
|
|
27
|
+
repeats: row.repeats,
|
|
28
|
+
passThreshold: row.pass_threshold,
|
|
29
|
+
maxCostUsd: row.max_cost_usd,
|
|
30
|
+
casesTotal: row.cases_total,
|
|
31
|
+
resultsTotal: row.results_total,
|
|
32
|
+
resultsDone: row.results_done,
|
|
33
|
+
costUsd: row.cost_usd,
|
|
34
|
+
engineName: row.engine_name,
|
|
35
|
+
engineVersion: row.engine_version,
|
|
36
|
+
stageflowVersion: row.stageflow_version,
|
|
37
|
+
label: row.label,
|
|
38
|
+
startedVia: row.started_via,
|
|
39
|
+
callerId: row.caller_id,
|
|
40
|
+
summary: parse(row.summary_json),
|
|
41
|
+
createdAt: row.created_at,
|
|
42
|
+
startedAt: row.started_at,
|
|
43
|
+
finishedAt: row.finished_at,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function resultFromDb(row) {
|
|
47
|
+
return {
|
|
48
|
+
evalRunId: row.eval_run_id,
|
|
49
|
+
caseId: row.case_id,
|
|
50
|
+
repeatIndex: row.repeat_index,
|
|
51
|
+
status: row.status,
|
|
52
|
+
errorCode: row.error_code,
|
|
53
|
+
errorMessage: row.error_message,
|
|
54
|
+
stageRunId: row.stage_run_id,
|
|
55
|
+
subjectRef: row.subject_ref,
|
|
56
|
+
turns: row.turns,
|
|
57
|
+
toolCallCount: row.tool_call_count,
|
|
58
|
+
output: parse(row.output_json),
|
|
59
|
+
latencyMs: row.latency_ms,
|
|
60
|
+
costUsd: row.cost_usd,
|
|
61
|
+
tokensIn: row.tokens_in,
|
|
62
|
+
tokensOut: row.tokens_out,
|
|
63
|
+
score: row.score,
|
|
64
|
+
passed: row.passed !== 0,
|
|
65
|
+
hasGraderErrors: row.has_grader_errors !== 0,
|
|
66
|
+
budgetWarnings: parse(row.budget_warnings_json),
|
|
67
|
+
startedAt: row.started_at,
|
|
68
|
+
finishedAt: row.finished_at,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function gradeFromDb(row) {
|
|
72
|
+
return {
|
|
73
|
+
evalRunId: row.eval_run_id,
|
|
74
|
+
caseId: row.case_id,
|
|
75
|
+
repeatIndex: row.repeat_index,
|
|
76
|
+
graderId: row.grader_id,
|
|
77
|
+
graderType: row.grader_type,
|
|
78
|
+
status: row.status,
|
|
79
|
+
passed: bool(row.passed),
|
|
80
|
+
score: row.score,
|
|
81
|
+
rawValue: row.raw_value,
|
|
82
|
+
reason: row.reason,
|
|
83
|
+
details: parse(row.details_json),
|
|
84
|
+
errorCode: row.error_code,
|
|
85
|
+
judgeModel: row.judge_model,
|
|
86
|
+
judgeCostUsd: row.judge_cost_usd,
|
|
87
|
+
judgeTokensIn: row.judge_tokens_in,
|
|
88
|
+
judgeTokensOut: row.judge_tokens_out,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function insertGrade(db, evalRunId, caseId, repeatIndex, grade) {
|
|
92
|
+
db.prepare(`INSERT INTO eval_grades
|
|
93
|
+
(eval_run_id, case_id, repeat_index, grader_id, grader_type, status, passed, score, raw_value, reason,
|
|
94
|
+
details_json, error_code, judge_model, judge_cost_usd, judge_tokens_in, judge_tokens_out)
|
|
95
|
+
VALUES
|
|
96
|
+
(@eval_run_id, @case_id, @repeat_index, @grader_id, @grader_type, @status, @passed, @score, @raw_value, @reason,
|
|
97
|
+
@details_json, @error_code, @judge_model, @judge_cost_usd, @judge_tokens_in, @judge_tokens_out)`).run({
|
|
98
|
+
eval_run_id: evalRunId,
|
|
99
|
+
case_id: caseId,
|
|
100
|
+
repeat_index: repeatIndex,
|
|
101
|
+
grader_id: grade.graderId,
|
|
102
|
+
grader_type: grade.graderType,
|
|
103
|
+
status: grade.status,
|
|
104
|
+
passed: grade.passed === undefined || grade.passed === null ? null : grade.passed ? 1 : 0,
|
|
105
|
+
score: grade.score ?? null,
|
|
106
|
+
raw_value: grade.rawValue ?? null,
|
|
107
|
+
reason: grade.reason ?? null,
|
|
108
|
+
details_json: json(grade.details),
|
|
109
|
+
error_code: grade.errorCode ?? null,
|
|
110
|
+
judge_model: grade.judgeModel ?? null,
|
|
111
|
+
judge_cost_usd: grade.judgeCostUsd ?? null,
|
|
112
|
+
judge_tokens_in: grade.judgeTokensIn ?? null,
|
|
113
|
+
judge_tokens_out: grade.judgeTokensOut ?? null,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export function createEvalRun(db, input) {
|
|
117
|
+
const evalRunId = `${EVAL_RUN_ID_PREFIX}${randomBytes(6).toString("hex")}`;
|
|
118
|
+
db.transaction(() => {
|
|
119
|
+
const counter = db
|
|
120
|
+
.prepare(`INSERT INTO eval_run_counters (project_root, eval_id, last_number) VALUES (?, ?, 1)
|
|
121
|
+
ON CONFLICT (project_root, eval_id) DO UPDATE SET last_number = last_number + 1
|
|
122
|
+
RETURNING last_number`)
|
|
123
|
+
.get(input.projectRoot, input.evalId);
|
|
124
|
+
db.prepare(`INSERT INTO eval_runs
|
|
125
|
+
(eval_run_id, project_root, eval_id, run_number, eval_path, eval_hash, target_kind, target_ref_json,
|
|
126
|
+
target_version_hash, dataset_path, dataset_hash, config_json, config_fingerprint, config_components_json,
|
|
127
|
+
status, repeats, pass_threshold, max_cost_usd, cases_total, results_total, engine_name, engine_version,
|
|
128
|
+
stageflow_version, label, started_via, caller_id, created_at)
|
|
129
|
+
VALUES
|
|
130
|
+
(@eval_run_id, @project_root, @eval_id, @run_number, @eval_path, @eval_hash, @target_kind, @target_ref_json,
|
|
131
|
+
@target_version_hash, @dataset_path, @dataset_hash, @config_json, @config_fingerprint, @config_components_json,
|
|
132
|
+
@status, @repeats, @pass_threshold, @max_cost_usd, @cases_total, @results_total, @engine_name, @engine_version,
|
|
133
|
+
@stageflow_version, @label, @started_via, @caller_id, @created_at)`).run({
|
|
134
|
+
eval_run_id: evalRunId,
|
|
135
|
+
project_root: input.projectRoot,
|
|
136
|
+
eval_id: input.evalId,
|
|
137
|
+
run_number: counter.last_number,
|
|
138
|
+
eval_path: input.evalPath,
|
|
139
|
+
eval_hash: input.evalHash,
|
|
140
|
+
target_kind: input.targetKind,
|
|
141
|
+
target_ref_json: JSON.stringify(input.targetRef),
|
|
142
|
+
target_version_hash: input.targetVersionHash,
|
|
143
|
+
dataset_path: input.datasetPath,
|
|
144
|
+
dataset_hash: input.datasetHash,
|
|
145
|
+
config_json: JSON.stringify(input.config),
|
|
146
|
+
config_fingerprint: input.configFingerprint,
|
|
147
|
+
config_components_json: JSON.stringify(input.configComponents),
|
|
148
|
+
status: input.status ?? "queued",
|
|
149
|
+
repeats: input.repeats,
|
|
150
|
+
pass_threshold: input.passThreshold,
|
|
151
|
+
max_cost_usd: input.maxCostUsd,
|
|
152
|
+
cases_total: input.casesTotal,
|
|
153
|
+
results_total: input.resultsTotal,
|
|
154
|
+
engine_name: input.engineName,
|
|
155
|
+
engine_version: input.engineVersion,
|
|
156
|
+
stageflow_version: input.stageflowVersion,
|
|
157
|
+
label: input.label ?? null,
|
|
158
|
+
started_via: input.startedVia,
|
|
159
|
+
caller_id: input.callerId ?? null,
|
|
160
|
+
created_at: now(),
|
|
161
|
+
});
|
|
162
|
+
})();
|
|
163
|
+
return getEvalRun(db, evalRunId);
|
|
164
|
+
}
|
|
165
|
+
export function insertEvalCases(db, evalRunId, cases) {
|
|
166
|
+
const insert = db.prepare(`INSERT INTO eval_cases (eval_run_id, case_id, position, case_json) VALUES (?, ?, ?, ?)`);
|
|
167
|
+
db.transaction(() => {
|
|
168
|
+
for (const c of cases)
|
|
169
|
+
insert.run(evalRunId, c.caseId, c.position, JSON.stringify(c.case));
|
|
170
|
+
})();
|
|
171
|
+
}
|
|
172
|
+
export function upsertEvalResult(db, input) {
|
|
173
|
+
db.transaction(() => {
|
|
174
|
+
db.prepare(`INSERT INTO eval_results
|
|
175
|
+
(eval_run_id, case_id, repeat_index, status, error_code, error_message, stage_run_id, subject_ref, turns, tool_call_count, output_json, latency_ms,
|
|
176
|
+
cost_usd, tokens_in, tokens_out, score, passed, has_grader_errors, budget_warnings_json, started_at, finished_at)
|
|
177
|
+
VALUES
|
|
178
|
+
(@eval_run_id, @case_id, @repeat_index, @status, @error_code, @error_message, @stage_run_id, @subject_ref, @turns, @tool_call_count, @output_json, @latency_ms,
|
|
179
|
+
@cost_usd, @tokens_in, @tokens_out, @score, @passed, @has_grader_errors, @budget_warnings_json, @started_at, @finished_at)
|
|
180
|
+
ON CONFLICT (eval_run_id, case_id, repeat_index) DO UPDATE SET
|
|
181
|
+
status = excluded.status, error_code = excluded.error_code, error_message = excluded.error_message,
|
|
182
|
+
stage_run_id = excluded.stage_run_id, subject_ref = excluded.subject_ref, turns = excluded.turns,
|
|
183
|
+
tool_call_count = excluded.tool_call_count, output_json = excluded.output_json, latency_ms = excluded.latency_ms,
|
|
184
|
+
cost_usd = excluded.cost_usd, tokens_in = excluded.tokens_in, tokens_out = excluded.tokens_out,
|
|
185
|
+
score = excluded.score, passed = excluded.passed, has_grader_errors = excluded.has_grader_errors,
|
|
186
|
+
budget_warnings_json = excluded.budget_warnings_json, started_at = excluded.started_at,
|
|
187
|
+
finished_at = excluded.finished_at`).run({
|
|
188
|
+
eval_run_id: input.evalRunId,
|
|
189
|
+
case_id: input.caseId,
|
|
190
|
+
repeat_index: input.repeatIndex,
|
|
191
|
+
status: input.status,
|
|
192
|
+
error_code: input.errorCode ?? null,
|
|
193
|
+
error_message: input.errorMessage ?? null,
|
|
194
|
+
stage_run_id: input.stageRunId ?? null,
|
|
195
|
+
subject_ref: input.subjectRef ?? input.stageRunId ?? null,
|
|
196
|
+
turns: input.turns ?? null,
|
|
197
|
+
tool_call_count: input.toolCallCount ?? null,
|
|
198
|
+
output_json: json(input.output),
|
|
199
|
+
latency_ms: input.latencyMs ?? null,
|
|
200
|
+
cost_usd: input.costUsd ?? null,
|
|
201
|
+
tokens_in: input.tokensIn ?? null,
|
|
202
|
+
tokens_out: input.tokensOut ?? null,
|
|
203
|
+
score: input.score ?? null,
|
|
204
|
+
passed: input.passed ? 1 : 0,
|
|
205
|
+
has_grader_errors: input.hasGraderErrors ? 1 : 0,
|
|
206
|
+
budget_warnings_json: json(input.budgetWarnings),
|
|
207
|
+
started_at: input.startedAt ?? null,
|
|
208
|
+
finished_at: input.finishedAt ?? null,
|
|
209
|
+
});
|
|
210
|
+
db.prepare(`DELETE FROM eval_grades WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ?`).run(input.evalRunId, input.caseId, input.repeatIndex);
|
|
211
|
+
for (const grade of input.grades ?? []) {
|
|
212
|
+
insertGrade(db, input.evalRunId, input.caseId, input.repeatIndex, grade);
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
216
|
+
export function updateEvalRunProgress(db, evalRunId, progress) {
|
|
217
|
+
const sets = [];
|
|
218
|
+
const params = { eval_run_id: evalRunId };
|
|
219
|
+
if (progress.resultsDone !== undefined) {
|
|
220
|
+
sets.push("results_done = @results_done");
|
|
221
|
+
params.results_done = progress.resultsDone;
|
|
222
|
+
}
|
|
223
|
+
if (progress.costUsd !== undefined) {
|
|
224
|
+
sets.push("cost_usd = @cost_usd");
|
|
225
|
+
params.cost_usd = progress.costUsd;
|
|
226
|
+
}
|
|
227
|
+
if (progress.status !== undefined) {
|
|
228
|
+
sets.push("status = @status");
|
|
229
|
+
params.status = progress.status;
|
|
230
|
+
}
|
|
231
|
+
if (progress.statusReason !== undefined) {
|
|
232
|
+
sets.push("status_reason = @status_reason");
|
|
233
|
+
params.status_reason = progress.statusReason;
|
|
234
|
+
}
|
|
235
|
+
if (progress.startedAt !== undefined) {
|
|
236
|
+
sets.push("started_at = @started_at");
|
|
237
|
+
params.started_at = progress.startedAt;
|
|
238
|
+
}
|
|
239
|
+
if (sets.length === 0)
|
|
240
|
+
return;
|
|
241
|
+
const result = db.prepare(`UPDATE eval_runs SET ${sets.join(", ")} WHERE eval_run_id = @eval_run_id`).run(params);
|
|
242
|
+
if (result.changes === 0)
|
|
243
|
+
throw notFound(evalRunId);
|
|
244
|
+
}
|
|
245
|
+
export function finishEvalRun(db, evalRunId, input) {
|
|
246
|
+
const result = db
|
|
247
|
+
.prepare(`UPDATE eval_runs SET status = @status, status_reason = @status_reason, passed = @passed,
|
|
248
|
+
summary_json = @summary_json, finished_at = @finished_at WHERE eval_run_id = @eval_run_id`)
|
|
249
|
+
.run({
|
|
250
|
+
eval_run_id: evalRunId,
|
|
251
|
+
status: input.status,
|
|
252
|
+
status_reason: input.statusReason ?? null,
|
|
253
|
+
passed: input.passed ? 1 : 0,
|
|
254
|
+
summary_json: JSON.stringify(input.summary),
|
|
255
|
+
finished_at: input.finishedAt,
|
|
256
|
+
});
|
|
257
|
+
if (result.changes === 0)
|
|
258
|
+
throw notFound(evalRunId);
|
|
259
|
+
}
|
|
260
|
+
function notFound(evalRunId) {
|
|
261
|
+
return new EvalStoreError("EVAL_RUN_NOT_FOUND", `Eval run not found: ${evalRunId}`);
|
|
262
|
+
}
|
|
263
|
+
export function getEvalRun(db, evalRunId) {
|
|
264
|
+
const row = db.prepare(`SELECT * FROM eval_runs WHERE eval_run_id = ?`).get(evalRunId);
|
|
265
|
+
return row ? runFromDb(row) : null;
|
|
266
|
+
}
|
|
267
|
+
export function getEvalRunByNumber(db, projectRoot, evalId, runNumber) {
|
|
268
|
+
const row = db
|
|
269
|
+
.prepare(`SELECT * FROM eval_runs WHERE project_root = ? AND eval_id = ? AND run_number = ?`)
|
|
270
|
+
.get(projectRoot, evalId, runNumber);
|
|
271
|
+
return row ? runFromDb(row) : null;
|
|
272
|
+
}
|
|
273
|
+
export function listEvalRuns(db, query) {
|
|
274
|
+
const clauses = [];
|
|
275
|
+
const params = [];
|
|
276
|
+
if (query.projectRoot !== undefined) {
|
|
277
|
+
clauses.push("project_root = ?");
|
|
278
|
+
params.push(query.projectRoot);
|
|
279
|
+
}
|
|
280
|
+
if (query.evalId !== undefined) {
|
|
281
|
+
clauses.push("eval_id = ?");
|
|
282
|
+
params.push(query.evalId);
|
|
283
|
+
}
|
|
284
|
+
if (query.status !== undefined) {
|
|
285
|
+
clauses.push("status = ?");
|
|
286
|
+
params.push(query.status);
|
|
287
|
+
}
|
|
288
|
+
if (query.stageRunId !== undefined) {
|
|
289
|
+
clauses.push("eval_run_id IN (SELECT eval_run_id FROM eval_results WHERE stage_run_id = ?)");
|
|
290
|
+
params.push(query.stageRunId);
|
|
291
|
+
}
|
|
292
|
+
if (query.subjectRef !== undefined) {
|
|
293
|
+
clauses.push("eval_run_id IN (SELECT eval_run_id FROM eval_results WHERE subject_ref = ?)");
|
|
294
|
+
params.push(query.subjectRef);
|
|
295
|
+
}
|
|
296
|
+
if (query.cursor !== undefined) {
|
|
297
|
+
const [createdAt, id] = JSON.parse(Buffer.from(query.cursor, "base64url").toString("utf8"));
|
|
298
|
+
clauses.push("(created_at < ? OR (created_at = ? AND eval_run_id < ?))");
|
|
299
|
+
params.push(createdAt, createdAt, id);
|
|
300
|
+
}
|
|
301
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
302
|
+
const rows = db
|
|
303
|
+
.prepare(`SELECT * FROM eval_runs ${where} ORDER BY created_at DESC, eval_run_id DESC LIMIT ?`)
|
|
304
|
+
.all(...params, query.limit + 1);
|
|
305
|
+
const page = rows.slice(0, query.limit).map(runFromDb);
|
|
306
|
+
const last = page[page.length - 1];
|
|
307
|
+
const nextCursor = rows.length > query.limit && last
|
|
308
|
+
? Buffer.from(JSON.stringify([last.createdAt, last.evalRunId]), "utf8").toString("base64url")
|
|
309
|
+
: null;
|
|
310
|
+
return { items: page, nextCursor };
|
|
311
|
+
}
|
|
312
|
+
function attachGrades(db, results, evalRunId, caseId) {
|
|
313
|
+
const grades = db
|
|
314
|
+
.prepare(`SELECT * FROM eval_grades WHERE eval_run_id = ? ${caseId !== undefined ? "AND case_id = ?" : ""}
|
|
315
|
+
ORDER BY case_id, repeat_index, rowid`)
|
|
316
|
+
.all(...(caseId !== undefined ? [evalRunId, caseId] : [evalRunId]));
|
|
317
|
+
const byResult = new Map();
|
|
318
|
+
for (const grade of grades.map(gradeFromDb)) {
|
|
319
|
+
const key = `${grade.caseId}\u0000${grade.repeatIndex}`;
|
|
320
|
+
const list = byResult.get(key);
|
|
321
|
+
if (list)
|
|
322
|
+
list.push(grade);
|
|
323
|
+
else
|
|
324
|
+
byResult.set(key, [grade]);
|
|
325
|
+
}
|
|
326
|
+
return results.map((result) => ({
|
|
327
|
+
...result,
|
|
328
|
+
grades: byResult.get(`${result.caseId}\u0000${result.repeatIndex}`) ?? [],
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
export function listEvalResults(db, evalRunId) {
|
|
332
|
+
const rows = db
|
|
333
|
+
.prepare(`SELECT r.* FROM eval_results r JOIN eval_cases c ON c.eval_run_id = r.eval_run_id AND c.case_id = r.case_id
|
|
334
|
+
WHERE r.eval_run_id = ? ORDER BY c.position, r.repeat_index`)
|
|
335
|
+
.all(evalRunId);
|
|
336
|
+
return attachGrades(db, rows.map(resultFromDb), evalRunId);
|
|
337
|
+
}
|
|
338
|
+
export function getEvalResultsForCase(db, evalRunId, caseId) {
|
|
339
|
+
const rows = db
|
|
340
|
+
.prepare(`SELECT * FROM eval_results WHERE eval_run_id = ? AND case_id = ? ORDER BY repeat_index`)
|
|
341
|
+
.all(evalRunId, caseId);
|
|
342
|
+
return attachGrades(db, rows.map(resultFromDb), evalRunId, caseId);
|
|
343
|
+
}
|
|
344
|
+
export function getEvalCases(db, evalRunId) {
|
|
345
|
+
const rows = db
|
|
346
|
+
.prepare(`SELECT eval_run_id, case_id, position, case_json FROM eval_cases WHERE eval_run_id = ? ORDER BY position`)
|
|
347
|
+
.all(evalRunId);
|
|
348
|
+
return rows.map((row) => ({
|
|
349
|
+
evalRunId: row.eval_run_id,
|
|
350
|
+
caseId: row.case_id,
|
|
351
|
+
position: row.position,
|
|
352
|
+
case: JSON.parse(row.case_json),
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
function normalizeTags(tags) {
|
|
356
|
+
const seen = new Set();
|
|
357
|
+
const out = [];
|
|
358
|
+
for (const raw of tags) {
|
|
359
|
+
const name = raw.trim();
|
|
360
|
+
if (name.length < 1 || name.length > EVAL_LIMITS.failureTypeNameMaxChars) {
|
|
361
|
+
throw new Error(`Failure type must be 1-${EVAL_LIMITS.failureTypeNameMaxChars} characters`);
|
|
362
|
+
}
|
|
363
|
+
const key = name.toLowerCase();
|
|
364
|
+
if (seen.has(key))
|
|
365
|
+
continue;
|
|
366
|
+
seen.add(key);
|
|
367
|
+
out.push(name);
|
|
368
|
+
}
|
|
369
|
+
return out;
|
|
370
|
+
}
|
|
371
|
+
export function putEvalReview(db, evalRunId, caseId, repeatIndex, patch) {
|
|
372
|
+
if (typeof patch.note === "string" && patch.note.length > EVAL_LIMITS.reviewNoteMaxChars) {
|
|
373
|
+
throw new Error(`Review note must be at most ${EVAL_LIMITS.reviewNoteMaxChars} characters`);
|
|
374
|
+
}
|
|
375
|
+
const tags = patch.tags === undefined ? undefined : normalizeTags(patch.tags);
|
|
376
|
+
db.transaction(() => {
|
|
377
|
+
const result = db
|
|
378
|
+
.prepare(`SELECT r.passed, e.project_root, e.eval_id FROM eval_results r JOIN eval_runs e ON e.eval_run_id = r.eval_run_id
|
|
379
|
+
WHERE r.eval_run_id = ? AND r.case_id = ? AND r.repeat_index = ?`)
|
|
380
|
+
.get(evalRunId, caseId, repeatIndex);
|
|
381
|
+
if (!result) {
|
|
382
|
+
throw new EvalStoreError("EVAL_RUN_NOT_FOUND", `Eval result not found: ${evalRunId}/${caseId}/${repeatIndex}`);
|
|
383
|
+
}
|
|
384
|
+
if (tags !== undefined && tags.length > 0 && result.passed !== 0) {
|
|
385
|
+
throw new EvalStoreError("EVAL_TAGS_ON_PASSED_RESULT", "Failure types apply only to results marked failed");
|
|
386
|
+
}
|
|
387
|
+
const at = now();
|
|
388
|
+
const existing = db
|
|
389
|
+
.prepare(`SELECT 1 AS ok FROM eval_result_reviews WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ?`)
|
|
390
|
+
.get(evalRunId, caseId, repeatIndex);
|
|
391
|
+
if (patch.label !== undefined) {
|
|
392
|
+
db.prepare(`INSERT INTO eval_result_reviews (eval_run_id, case_id, repeat_index, label, note, created_at, updated_at)
|
|
393
|
+
VALUES (@eval_run_id, @case_id, @repeat_index, @label, @note, @at, @at)
|
|
394
|
+
ON CONFLICT (eval_run_id, case_id, repeat_index) DO UPDATE SET
|
|
395
|
+
label = excluded.label,
|
|
396
|
+
note = ${patch.note === undefined ? "eval_result_reviews.note" : "excluded.note"},
|
|
397
|
+
updated_at = excluded.updated_at`).run({
|
|
398
|
+
eval_run_id: evalRunId,
|
|
399
|
+
case_id: caseId,
|
|
400
|
+
repeat_index: repeatIndex,
|
|
401
|
+
label: patch.label,
|
|
402
|
+
note: patch.note ?? null,
|
|
403
|
+
at,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else if (patch.note !== undefined) {
|
|
407
|
+
if (existing) {
|
|
408
|
+
db.prepare(`UPDATE eval_result_reviews SET note = ?, updated_at = ? WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ?`).run(patch.note, at, evalRunId, caseId, repeatIndex);
|
|
409
|
+
}
|
|
410
|
+
else if (patch.note !== null) {
|
|
411
|
+
throw new Error("A review note needs a pass or fail label");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (tags !== undefined) {
|
|
415
|
+
const keep = new Set(tags.map((t) => t.toLowerCase()));
|
|
416
|
+
const current = db
|
|
417
|
+
.prepare(`SELECT tag FROM eval_result_tags WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ?`)
|
|
418
|
+
.all(evalRunId, caseId, repeatIndex);
|
|
419
|
+
const have = new Set(current.map((row) => row.tag.toLowerCase()));
|
|
420
|
+
for (const row of current) {
|
|
421
|
+
if (!keep.has(row.tag.toLowerCase())) {
|
|
422
|
+
db.prepare(`DELETE FROM eval_result_tags WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ? AND tag = ?`).run(evalRunId, caseId, repeatIndex, row.tag);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
for (const tag of tags) {
|
|
426
|
+
db.prepare(`INSERT INTO eval_failure_types (project_root, eval_id, name, created_at) VALUES (?, ?, ?, ?)
|
|
427
|
+
ON CONFLICT DO NOTHING`).run(result.project_root, result.eval_id, tag, at);
|
|
428
|
+
if (!have.has(tag.toLowerCase())) {
|
|
429
|
+
db.prepare(`INSERT INTO eval_result_tags (eval_run_id, case_id, repeat_index, tag, created_at) VALUES (?, ?, ?, ?, ?)`).run(evalRunId, caseId, repeatIndex, tag, at);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
})();
|
|
434
|
+
return readReview(db, evalRunId, caseId, repeatIndex);
|
|
435
|
+
}
|
|
436
|
+
function readReview(db, evalRunId, caseId, repeatIndex) {
|
|
437
|
+
return (getEvalReviews(db, evalRunId).find((r) => r.caseId === caseId && r.repeatIndex === repeatIndex) ?? {
|
|
438
|
+
evalRunId,
|
|
439
|
+
caseId,
|
|
440
|
+
repeatIndex,
|
|
441
|
+
label: null,
|
|
442
|
+
note: null,
|
|
443
|
+
tags: [],
|
|
444
|
+
createdAt: null,
|
|
445
|
+
updatedAt: null,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
export function getEvalReviews(db, evalRunId) {
|
|
449
|
+
const reviews = db
|
|
450
|
+
.prepare(`SELECT case_id, repeat_index, label, note, created_at, updated_at FROM eval_result_reviews WHERE eval_run_id = ?`)
|
|
451
|
+
.all(evalRunId);
|
|
452
|
+
const tagRows = db
|
|
453
|
+
.prepare(`SELECT case_id, repeat_index, tag FROM eval_result_tags WHERE eval_run_id = ? ORDER BY created_at, rowid`)
|
|
454
|
+
.all(evalRunId);
|
|
455
|
+
const merged = new Map();
|
|
456
|
+
const keyOf = (caseId, repeat) => `${caseId}\u0000${repeat}`;
|
|
457
|
+
for (const r of reviews) {
|
|
458
|
+
merged.set(keyOf(r.case_id, r.repeat_index), {
|
|
459
|
+
evalRunId,
|
|
460
|
+
caseId: r.case_id,
|
|
461
|
+
repeatIndex: r.repeat_index,
|
|
462
|
+
label: r.label,
|
|
463
|
+
note: r.note,
|
|
464
|
+
tags: [],
|
|
465
|
+
createdAt: r.created_at,
|
|
466
|
+
updatedAt: r.updated_at,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
for (const t of tagRows) {
|
|
470
|
+
const key = keyOf(t.case_id, t.repeat_index);
|
|
471
|
+
let row = merged.get(key);
|
|
472
|
+
if (!row) {
|
|
473
|
+
row = {
|
|
474
|
+
evalRunId,
|
|
475
|
+
caseId: t.case_id,
|
|
476
|
+
repeatIndex: t.repeat_index,
|
|
477
|
+
label: null,
|
|
478
|
+
note: null,
|
|
479
|
+
tags: [],
|
|
480
|
+
createdAt: null,
|
|
481
|
+
updatedAt: null,
|
|
482
|
+
};
|
|
483
|
+
merged.set(key, row);
|
|
484
|
+
}
|
|
485
|
+
row.tags.push(t.tag);
|
|
486
|
+
}
|
|
487
|
+
return [...merged.values()].sort((a, b) => a.caseId.localeCompare(b.caseId) || a.repeatIndex - b.repeatIndex);
|
|
488
|
+
}
|
|
489
|
+
export function listFailureTypes(db, projectRoot, evalId) {
|
|
490
|
+
const rows = db
|
|
491
|
+
.prepare(`SELECT ft.name AS name FROM eval_failure_types ft
|
|
492
|
+
WHERE ft.project_root = ? AND ft.eval_id = ?
|
|
493
|
+
ORDER BY COALESCE(
|
|
494
|
+
(SELECT MAX(t.created_at) FROM eval_result_tags t JOIN eval_runs r ON r.eval_run_id = t.eval_run_id
|
|
495
|
+
WHERE r.project_root = ft.project_root AND r.eval_id = ft.eval_id AND t.tag = ft.name COLLATE NOCASE),
|
|
496
|
+
ft.created_at) DESC, ft.rowid DESC`)
|
|
497
|
+
.all(projectRoot, evalId);
|
|
498
|
+
return rows.map((row) => row.name);
|
|
499
|
+
}
|
|
500
|
+
export function recordGraderVersions(db, projectRoot, evalId, graders) {
|
|
501
|
+
const out = {};
|
|
502
|
+
db.transaction(() => {
|
|
503
|
+
for (const { graderId, graderHash } of graders) {
|
|
504
|
+
const existing = db
|
|
505
|
+
.prepare(`SELECT ordinal FROM eval_grader_versions WHERE project_root = ? AND eval_id = ? AND grader_id = ? AND grader_hash = ?`)
|
|
506
|
+
.get(projectRoot, evalId, graderId, graderHash);
|
|
507
|
+
if (existing) {
|
|
508
|
+
out[graderId] = existing.ordinal;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const max = db
|
|
512
|
+
.prepare(`SELECT MAX(ordinal) AS m FROM eval_grader_versions WHERE project_root = ? AND eval_id = ? AND grader_id = ?`)
|
|
513
|
+
.get(projectRoot, evalId, graderId);
|
|
514
|
+
const ordinal = (max.m ?? 0) + 1;
|
|
515
|
+
db.prepare(`INSERT INTO eval_grader_versions (project_root, eval_id, grader_id, grader_hash, ordinal, first_seen_at)
|
|
516
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(projectRoot, evalId, graderId, graderHash, ordinal, now());
|
|
517
|
+
out[graderId] = ordinal;
|
|
518
|
+
}
|
|
519
|
+
})();
|
|
520
|
+
return out;
|
|
521
|
+
}
|
|
522
|
+
export function listEvalSubjectRefs(db, evalRunId, targetKind) {
|
|
523
|
+
const run = getEvalRun(db, evalRunId);
|
|
524
|
+
if (!run || run.targetKind !== targetKind)
|
|
525
|
+
return [];
|
|
526
|
+
return db
|
|
527
|
+
.prepare(`SELECT DISTINCT subject_ref FROM eval_results WHERE eval_run_id = ? AND subject_ref IS NOT NULL ORDER BY subject_ref`)
|
|
528
|
+
.all(evalRunId).map((row) => row.subject_ref);
|
|
529
|
+
}
|
|
530
|
+
export function deleteEvalRun(db, evalRunId) {
|
|
531
|
+
let stageRunIds = [];
|
|
532
|
+
db.transaction(() => {
|
|
533
|
+
if (!getEvalRun(db, evalRunId))
|
|
534
|
+
throw notFound(evalRunId);
|
|
535
|
+
stageRunIds = db
|
|
536
|
+
.prepare(`SELECT DISTINCT stage_run_id FROM eval_results WHERE eval_run_id = ? AND stage_run_id IS NOT NULL ORDER BY stage_run_id`)
|
|
537
|
+
.all(evalRunId).map((row) => row.stage_run_id);
|
|
538
|
+
db.prepare(`DELETE FROM eval_runs WHERE eval_run_id = ?`).run(evalRunId);
|
|
539
|
+
})();
|
|
540
|
+
return stageRunIds;
|
|
541
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
/**
|
|
3
|
+
* Eval runs, their cases, results, grades and the author's reviews, plus `runs.origin` so stage runs
|
|
4
|
+
* started by an eval can be hidden from normal run views. `eval_run_counters` keeps `run_number`
|
|
5
|
+
* from being reused after an eval run is deleted.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MIGRATION_015: {
|
|
8
|
+
version: number;
|
|
9
|
+
name: string;
|
|
10
|
+
minStageflowVersion: string;
|
|
11
|
+
up(db: Database.Database): void;
|
|
12
|
+
};
|