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.
- package/README.md +3 -1
- 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 +1 -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/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/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/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/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/types/stageflowManifest.d.ts +6 -0
- 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/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,105 @@
|
|
|
1
|
+
import { isAgentError } from "../agents/errors.js";
|
|
2
|
+
export class SessionCaseError extends Error {
|
|
3
|
+
}
|
|
4
|
+
const PREVIEW_CHARS = 160;
|
|
5
|
+
const SCRIPT_MAX_MESSAGES = 40;
|
|
6
|
+
const SCRIPT_MAX_CHARS = 4000;
|
|
7
|
+
const SESSION_TAG = "from-session";
|
|
8
|
+
function userMessages(detail) {
|
|
9
|
+
return detail.messages
|
|
10
|
+
.filter((m) => m.role === "user" && m.text.trim() !== "")
|
|
11
|
+
.map((m) => m.text.slice(0, SCRIPT_MAX_CHARS))
|
|
12
|
+
.slice(0, SCRIPT_MAX_MESSAGES);
|
|
13
|
+
}
|
|
14
|
+
function toolNames(detail) {
|
|
15
|
+
const names = new Set();
|
|
16
|
+
for (const span of detail.spans) {
|
|
17
|
+
if (span.kind !== "tool")
|
|
18
|
+
continue;
|
|
19
|
+
const name = span.attributes["gen_ai.tool.name"];
|
|
20
|
+
if (typeof name === "string" && name !== "")
|
|
21
|
+
names.add(name);
|
|
22
|
+
}
|
|
23
|
+
return [...names];
|
|
24
|
+
}
|
|
25
|
+
function shortId(sessionId) {
|
|
26
|
+
return sessionId.replace(/^ses_/, "").slice(0, 8);
|
|
27
|
+
}
|
|
28
|
+
function isFinishedStatus(status) {
|
|
29
|
+
return status === "open" || status === "closed";
|
|
30
|
+
}
|
|
31
|
+
function isFinishedUserSession(detail, agentId) {
|
|
32
|
+
const { session } = detail;
|
|
33
|
+
return session.agentId === agentId && session.source !== "eval" && isFinishedStatus(session.status);
|
|
34
|
+
}
|
|
35
|
+
/** Finished (idle or closed), non-eval sessions of the agent that have at least one user message, newest first. */
|
|
36
|
+
export async function listSessionCandidates(input) {
|
|
37
|
+
const { sessions, projectRoot, agentId, limit } = input;
|
|
38
|
+
const items = [];
|
|
39
|
+
let cursor;
|
|
40
|
+
do {
|
|
41
|
+
let page;
|
|
42
|
+
try {
|
|
43
|
+
page = await sessions.listSessions(projectRoot, agentId, {
|
|
44
|
+
limit: Math.max(limit, 25),
|
|
45
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (isAgentError(err))
|
|
50
|
+
throw new SessionCaseError(`Agent "${agentId}" was not found`);
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
for (const row of page.sessions) {
|
|
54
|
+
if (row.source === "eval" || !isFinishedStatus(row.status))
|
|
55
|
+
continue;
|
|
56
|
+
if (row.lastTurnStatus === "running" || row.lastTurnStatus === "waiting")
|
|
57
|
+
continue;
|
|
58
|
+
const detail = await sessions.getSession(projectRoot, row.id);
|
|
59
|
+
const messages = userMessages(detail);
|
|
60
|
+
if (messages.length === 0)
|
|
61
|
+
continue;
|
|
62
|
+
items.push({
|
|
63
|
+
session_id: row.id,
|
|
64
|
+
updated_at: row.updatedAt,
|
|
65
|
+
preview: messages[0].replace(/\s+/g, " ").slice(0, PREVIEW_CHARS),
|
|
66
|
+
turns: messages.length,
|
|
67
|
+
tools: toolNames(detail),
|
|
68
|
+
});
|
|
69
|
+
if (items.length >= limit)
|
|
70
|
+
return items;
|
|
71
|
+
}
|
|
72
|
+
cursor = page.nextCursor ?? undefined;
|
|
73
|
+
} while (cursor !== undefined);
|
|
74
|
+
return items;
|
|
75
|
+
}
|
|
76
|
+
/** One `script` scenario per session: its user messages, plus the tools it called as the expected tools. */
|
|
77
|
+
export async function casesFromSessions(input) {
|
|
78
|
+
const { sessions, projectRoot, agentId } = input;
|
|
79
|
+
const cases = [];
|
|
80
|
+
for (const sessionId of input.sessionIds) {
|
|
81
|
+
let detail;
|
|
82
|
+
try {
|
|
83
|
+
detail = await sessions.getSession(projectRoot, sessionId);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (isAgentError(err))
|
|
87
|
+
throw new SessionCaseError(`Session "${sessionId}" was not found`);
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
if (!isFinishedUserSession(detail, agentId)) {
|
|
91
|
+
throw new SessionCaseError(`Session "${sessionId}" is not a finished session of agent "${agentId}"`);
|
|
92
|
+
}
|
|
93
|
+
const script = userMessages(detail);
|
|
94
|
+
if (script.length === 0)
|
|
95
|
+
throw new SessionCaseError(`Session "${sessionId}" has no user messages`);
|
|
96
|
+
cases.push({
|
|
97
|
+
title: detail.session.title ?? `Session ${shortId(sessionId)}`,
|
|
98
|
+
input: { mode: "script", script, opening: script[0] },
|
|
99
|
+
expected: { tools: toolNames(detail) },
|
|
100
|
+
tags: [SESSION_TAG],
|
|
101
|
+
metadata: { source_session: sessionId },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return cases;
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function datasetContentHash(cases: readonly unknown[]): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DatasetCase } from "./datasetTypes.js";
|
|
2
|
+
export type UploadMappingEntry = {
|
|
3
|
+
column: string;
|
|
4
|
+
split?: string;
|
|
5
|
+
} | null;
|
|
6
|
+
export type UploadMapping = Record<string, UploadMappingEntry>;
|
|
7
|
+
export type UploadPreviewRow = {
|
|
8
|
+
index: number;
|
|
9
|
+
case: DatasetCase;
|
|
10
|
+
status: "ready" | "skipped";
|
|
11
|
+
reason?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function nextIdNumber(existingIds: Iterable<string>): number;
|
|
14
|
+
export declare function formatId(n: number): string;
|
|
15
|
+
export type UploadMappingOptions = {
|
|
16
|
+
/** Also map the top-level `title` and `tags` targets (agent datasets). */
|
|
17
|
+
caseMeta?: boolean;
|
|
18
|
+
/** Extra check on the built case; a returned text skips the row with that reason. */
|
|
19
|
+
check?: (draft: DatasetCase) => string | undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare function applyUploadMapping(rows: ReadonlyArray<Record<string, string>>, mapping: UploadMapping, inputSchema: unknown, existingIds: Iterable<string>, options?: UploadMappingOptions): UploadPreviewRow[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { payloadInstanceMismatch } from "../envelope/payloadSchema.js";
|
|
2
|
+
const REASON_MAX = 160;
|
|
3
|
+
const ID_NUMBER_RE = /^c(\d+)$/;
|
|
4
|
+
function trimReason(message) {
|
|
5
|
+
const flat = message.replace(/\s+/g, " ").trim();
|
|
6
|
+
return flat.length > REASON_MAX ? flat.slice(0, REASON_MAX) : flat;
|
|
7
|
+
}
|
|
8
|
+
export function nextIdNumber(existingIds) {
|
|
9
|
+
let max = 0;
|
|
10
|
+
for (const id of existingIds) {
|
|
11
|
+
const m = ID_NUMBER_RE.exec(id);
|
|
12
|
+
if (m)
|
|
13
|
+
max = Math.max(max, Number(m[1]));
|
|
14
|
+
}
|
|
15
|
+
return max + 1;
|
|
16
|
+
}
|
|
17
|
+
export function formatId(n) {
|
|
18
|
+
return `c${String(n).padStart(3, "0")}`;
|
|
19
|
+
}
|
|
20
|
+
function schemaPropertyType(inputSchema, name) {
|
|
21
|
+
if (inputSchema === null || typeof inputSchema !== "object")
|
|
22
|
+
return undefined;
|
|
23
|
+
const props = inputSchema.properties;
|
|
24
|
+
if (props === null || typeof props !== "object")
|
|
25
|
+
return undefined;
|
|
26
|
+
const prop = props[name];
|
|
27
|
+
if (prop === null || typeof prop !== "object")
|
|
28
|
+
return undefined;
|
|
29
|
+
const type = prop.type;
|
|
30
|
+
return typeof type === "string" ? type : undefined;
|
|
31
|
+
}
|
|
32
|
+
function parseCell(cell, schemaType, split, name) {
|
|
33
|
+
if (split !== undefined) {
|
|
34
|
+
return {
|
|
35
|
+
value: cell
|
|
36
|
+
.split(split)
|
|
37
|
+
.map((part) => part.trim())
|
|
38
|
+
.filter((part) => part !== ""),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (schemaType === "number" || schemaType === "integer") {
|
|
42
|
+
const trimmed = cell.trim();
|
|
43
|
+
const n = trimmed === "" ? Number.NaN : Number(trimmed);
|
|
44
|
+
if (!Number.isFinite(n) || (schemaType === "integer" && !Number.isInteger(n))) {
|
|
45
|
+
return { error: `${name} is not ${schemaType === "integer" ? "an integer" : "a number"}` };
|
|
46
|
+
}
|
|
47
|
+
return { value: n };
|
|
48
|
+
}
|
|
49
|
+
if (schemaType === "boolean") {
|
|
50
|
+
const lowered = cell.trim().toLowerCase();
|
|
51
|
+
if (lowered === "true")
|
|
52
|
+
return { value: true };
|
|
53
|
+
if (lowered === "false")
|
|
54
|
+
return { value: false };
|
|
55
|
+
return { error: `${name} is not a boolean` };
|
|
56
|
+
}
|
|
57
|
+
return { value: cell };
|
|
58
|
+
}
|
|
59
|
+
export function applyUploadMapping(rows, mapping, inputSchema, existingIds, options = {}) {
|
|
60
|
+
let nextId = nextIdNumber(existingIds);
|
|
61
|
+
const entries = Object.entries(mapping).filter((e) => e[1] !== null);
|
|
62
|
+
return rows.map((row, index) => {
|
|
63
|
+
const input = {};
|
|
64
|
+
const expected = {};
|
|
65
|
+
let parseError;
|
|
66
|
+
let title;
|
|
67
|
+
let tags;
|
|
68
|
+
for (const [target, entry] of entries) {
|
|
69
|
+
if (options.caseMeta === true && (target === "title" || target === "tags")) {
|
|
70
|
+
const raw = row[entry.column];
|
|
71
|
+
if (raw === undefined || raw.trim() === "")
|
|
72
|
+
continue;
|
|
73
|
+
if (target === "title")
|
|
74
|
+
title = raw.trim();
|
|
75
|
+
else
|
|
76
|
+
tags = (entry.split !== undefined ? raw.split(entry.split) : raw.split(",")).map((t) => t.trim()).filter((t) => t !== "");
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const dot = target.indexOf(".");
|
|
80
|
+
const base = dot === -1 ? target : target.slice(0, dot);
|
|
81
|
+
const name = dot === -1 ? "" : target.slice(dot + 1);
|
|
82
|
+
if ((base !== "input" && base !== "expected") || name === "")
|
|
83
|
+
continue;
|
|
84
|
+
const cell = row[entry.column];
|
|
85
|
+
if (cell === undefined || cell === "")
|
|
86
|
+
continue;
|
|
87
|
+
const schemaType = base === "input" ? schemaPropertyType(inputSchema, name) : undefined;
|
|
88
|
+
const parsed = parseCell(cell, schemaType, entry.split, name);
|
|
89
|
+
if ("error" in parsed) {
|
|
90
|
+
parseError ??= parsed.error;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
(base === "input" ? input : expected)[name] = parsed.value;
|
|
94
|
+
}
|
|
95
|
+
const draft = { id: "", input };
|
|
96
|
+
if (Object.keys(expected).length > 0)
|
|
97
|
+
draft.expected = expected;
|
|
98
|
+
if (title !== undefined)
|
|
99
|
+
draft.title = title;
|
|
100
|
+
if (tags !== undefined && tags.length > 0)
|
|
101
|
+
draft.tags = tags;
|
|
102
|
+
let reason = parseError;
|
|
103
|
+
if (reason === undefined && inputSchema !== undefined) {
|
|
104
|
+
try {
|
|
105
|
+
reason = payloadInstanceMismatch(input, inputSchema);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
reason = err instanceof Error ? err.message : String(err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (reason === undefined && options.check)
|
|
112
|
+
reason = options.check(draft);
|
|
113
|
+
if (reason !== undefined) {
|
|
114
|
+
return { index, case: draft, status: "skipped", reason: trimReason(reason) };
|
|
115
|
+
}
|
|
116
|
+
draft.id = formatId(nextId);
|
|
117
|
+
nextId += 1;
|
|
118
|
+
return { index, case: draft, status: "ready" };
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type DatasetStageCheckResult = {
|
|
2
|
+
caseId: string;
|
|
3
|
+
valid: boolean;
|
|
4
|
+
message?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function checkDatasetAgainstStage(cases: ReadonlyArray<{
|
|
7
|
+
id: string;
|
|
8
|
+
input: Record<string, unknown>;
|
|
9
|
+
}>, inputSchema: unknown): DatasetStageCheckResult[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { payloadInstanceMismatch } from "../envelope/payloadSchema.js";
|
|
2
|
+
export function checkDatasetAgainstStage(cases, inputSchema) {
|
|
3
|
+
return cases.map((c) => {
|
|
4
|
+
if (inputSchema === undefined)
|
|
5
|
+
return { caseId: c.id, valid: true };
|
|
6
|
+
let message;
|
|
7
|
+
try {
|
|
8
|
+
message = payloadInstanceMismatch(c.input, inputSchema);
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
message = err instanceof Error ? err.message : String(err);
|
|
12
|
+
}
|
|
13
|
+
return message === undefined
|
|
14
|
+
? { caseId: c.id, valid: true }
|
|
15
|
+
: { caseId: c.id, valid: false, message };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ValidationFinding } from "./validateCatalog.js";
|
|
2
|
+
export type DatasetFormat = "jsonl" | "json" | "csv";
|
|
3
|
+
export type DatasetCase = {
|
|
4
|
+
id: string;
|
|
5
|
+
input: Record<string, unknown>;
|
|
6
|
+
title?: string;
|
|
7
|
+
expected?: unknown;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
goal?: string;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
export type DatasetLimits = {
|
|
13
|
+
maxCases: number;
|
|
14
|
+
maxCaseBytes: number;
|
|
15
|
+
maxFileBytes: number;
|
|
16
|
+
};
|
|
17
|
+
export type ParseDatasetResult = {
|
|
18
|
+
cases: DatasetCase[];
|
|
19
|
+
findings: ValidationFinding[];
|
|
20
|
+
contentHash: string;
|
|
21
|
+
};
|
|
22
|
+
export type DatasetLoadResult = ParseDatasetResult & {
|
|
23
|
+
path: string;
|
|
24
|
+
relPath: string;
|
|
25
|
+
format: DatasetFormat | undefined;
|
|
26
|
+
fileBytes: number;
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -14,4 +14,5 @@ export declare function catalogScanRootForDirectory(directory: string): string;
|
|
|
14
14
|
* Make sure `stageflow.yaml` scans `directory` for both pipelines and tasks.
|
|
15
15
|
* Returns true when the manifest changed. Missing or unreadable manifests are left alone.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
17
|
+
export type CatalogScanKey = "pipelines" | "tasks" | "evals" | "datasets";
|
|
18
|
+
export declare function ensureCatalogScanRoot(projectRoot: string, directory: string, keys?: readonly CatalogScanKey[]): Promise<boolean>;
|
|
@@ -38,11 +38,7 @@ function isCovered(entries, root) {
|
|
|
38
38
|
return root === normalized || root.startsWith(`${normalized}/`);
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
* Make sure `stageflow.yaml` scans `directory` for both pipelines and tasks.
|
|
43
|
-
* Returns true when the manifest changed. Missing or unreadable manifests are left alone.
|
|
44
|
-
*/
|
|
45
|
-
export async function ensureCatalogScanRoot(projectRoot, directory) {
|
|
41
|
+
export async function ensureCatalogScanRoot(projectRoot, directory, keys = ["pipelines", "tasks"]) {
|
|
46
42
|
const root = catalogScanRootForDirectory(directory);
|
|
47
43
|
if (!root || root.split("/").includes(".."))
|
|
48
44
|
return false;
|
|
@@ -56,7 +52,7 @@ export async function ensureCatalogScanRoot(projectRoot, directory) {
|
|
|
56
52
|
}
|
|
57
53
|
const doc = parseDocument(text);
|
|
58
54
|
let changed = false;
|
|
59
|
-
for (const key of
|
|
55
|
+
for (const key of keys) {
|
|
60
56
|
const seq = doc.getIn(["catalog", key]);
|
|
61
57
|
if (!isSeq(seq))
|
|
62
58
|
continue;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LoadedManifest } from "../types/stageflowManifest.js";
|
|
2
|
+
import { type LoadedEval } from "./loadEval.js";
|
|
3
|
+
import type { LoadOutcome } from "./loadOutcome.js";
|
|
4
|
+
import type { ValidationFinding } from "./validateCatalog.js";
|
|
5
|
+
import { type DatasetReport } from "./validateEval.js";
|
|
6
|
+
export type CatalogFileRef = {
|
|
7
|
+
absPath: string;
|
|
8
|
+
relPath: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function listEvalFiles(manifest: LoadedManifest): Promise<CatalogFileRef[]>;
|
|
11
|
+
export declare function listDatasetFiles(manifest: LoadedManifest): Promise<CatalogFileRef[]>;
|
|
12
|
+
export type EvalCatalogEntry = {
|
|
13
|
+
absPath: string;
|
|
14
|
+
relPath: string;
|
|
15
|
+
outcome: LoadOutcome<LoadedEval>;
|
|
16
|
+
findings: ValidationFinding[];
|
|
17
|
+
};
|
|
18
|
+
export type LoadEvalCatalogOptions = {
|
|
19
|
+
isKnownModel?: (id: string) => Promise<boolean>;
|
|
20
|
+
datasetReportFor?: (loaded: LoadedEval) => Promise<DatasetReport | undefined>;
|
|
21
|
+
};
|
|
22
|
+
export declare function loadEvalCatalog(manifest: LoadedManifest, options?: LoadEvalCatalogOptions): Promise<EvalCatalogEntry[]>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getModelCatalog } from "../models/catalog.js";
|
|
4
|
+
import { loadDatasetFile } from "./loadDataset.js";
|
|
5
|
+
import { loadEvalOutcome, resolveEvalPaths } from "./loadEval.js";
|
|
6
|
+
import { scanCatalogPaths } from "./scanCatalogPaths.js";
|
|
7
|
+
import { evalFindingsFromIssues, validateEval, } from "./validateEval.js";
|
|
8
|
+
async function listFiles(manifest, kind) {
|
|
9
|
+
const absPaths = await scanCatalogPaths(manifest, kind);
|
|
10
|
+
return absPaths.map((absPath) => ({
|
|
11
|
+
absPath,
|
|
12
|
+
relPath: path.relative(manifest.projectRoot, absPath).split(path.sep).join("/"),
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
export function listEvalFiles(manifest) {
|
|
16
|
+
return listFiles(manifest, "eval");
|
|
17
|
+
}
|
|
18
|
+
export function listDatasetFiles(manifest) {
|
|
19
|
+
return listFiles(manifest, "dataset");
|
|
20
|
+
}
|
|
21
|
+
export async function loadEvalCatalog(manifest, options = {}) {
|
|
22
|
+
const refs = await listEvalFiles(manifest);
|
|
23
|
+
const { projectRoot } = manifest;
|
|
24
|
+
const isKnownModel = options.isKnownModel ??
|
|
25
|
+
(async (id) => {
|
|
26
|
+
try {
|
|
27
|
+
const { models } = await getModelCatalog(projectRoot).snapshot();
|
|
28
|
+
return models.length === 0 || models.includes(id);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const datasetReportFor = options.datasetReportFor ??
|
|
35
|
+
(async (evalFile) => {
|
|
36
|
+
const { datasetPath } = resolveEvalPaths(evalFile.file, evalFile.path);
|
|
37
|
+
try {
|
|
38
|
+
await stat(datasetPath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return loadDatasetFile(datasetPath, { root: projectRoot });
|
|
44
|
+
});
|
|
45
|
+
const loaded = await Promise.all(refs.map(async (ref) => ({ ref, outcome: await loadEvalOutcome(ref.absPath, projectRoot) })));
|
|
46
|
+
const evals = loaded.flatMap(({ ref, outcome }) => outcome.ok ? [{ id: outcome.value.id, absPath: ref.absPath }] : []);
|
|
47
|
+
const entries = [];
|
|
48
|
+
for (const { ref, outcome } of loaded) {
|
|
49
|
+
let findings;
|
|
50
|
+
if (outcome.ok) {
|
|
51
|
+
const datasetReport = await datasetReportFor(outcome.value);
|
|
52
|
+
findings = await validateEval(outcome.value, {
|
|
53
|
+
projectRoot,
|
|
54
|
+
evals,
|
|
55
|
+
isKnownModel,
|
|
56
|
+
...(datasetReport !== undefined ? { datasetReport } : {}),
|
|
57
|
+
});
|
|
58
|
+
if (datasetReport !== undefined)
|
|
59
|
+
findings.push(...datasetReport.findings);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
findings = evalFindingsFromIssues(ref.relPath, outcome.issues);
|
|
63
|
+
}
|
|
64
|
+
entries.push({ absPath: ref.absPath, relPath: ref.relPath, outcome, findings });
|
|
65
|
+
}
|
|
66
|
+
return entries;
|
|
67
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LoadedManifest } from "../types/stageflowManifest.js";
|
|
2
|
+
import { type CatalogFileRef } from "./evalCatalog.js";
|
|
3
|
+
export type WriteRootKind = "evals" | "datasets" | "pipelines";
|
|
4
|
+
export declare function manifestForWrite(projectRoot: string): Promise<LoadedManifest>;
|
|
5
|
+
export declare function explicitRoots(manifest: LoadedManifest, kind: WriteRootKind): string[] | undefined;
|
|
6
|
+
export type WriteDir = {
|
|
7
|
+
absDir: string;
|
|
8
|
+
relDir: string;
|
|
9
|
+
explicitDirectory: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function resolveWriteDir(projectRoot: string, manifest: LoadedManifest, kind: WriteRootKind, directory?: string): Promise<WriteDir>;
|
|
12
|
+
export declare function assertInsideProject(projectRoot: string, absPath: string, what: string): void;
|
|
13
|
+
export declare function evalStem(fileName: string): string;
|
|
14
|
+
export declare function datasetStem(fileName: string): string;
|
|
15
|
+
export declare function findEvalFile(projectRoot: string, id: string, manifest?: LoadedManifest): Promise<CatalogFileRef | undefined>;
|
|
16
|
+
export declare function datasetIdsFor(refs: readonly CatalogFileRef[]): Map<string, string>;
|
|
17
|
+
export declare function findDatasetFile(projectRoot: string, id: string, manifest?: LoadedManifest): Promise<CatalogFileRef | undefined>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { CatalogPathError, isInsideProjectRoot } from "./catalogRelativePath.js";
|
|
5
|
+
import { normalizeCatalogDir } from "./ensureCatalogScanRoot.js";
|
|
6
|
+
import { listDatasetFiles, listEvalFiles } from "./evalCatalog.js";
|
|
7
|
+
import { datasetFormatFromPath } from "./loadDataset.js";
|
|
8
|
+
import { loadStageflowManifestOutcome, manifestPathForProject, parseStageflowManifestOutcome, } from "./loadStageflowManifest.js";
|
|
9
|
+
const DEFAULT_DIR = {
|
|
10
|
+
evals: "evals",
|
|
11
|
+
datasets: "datasets",
|
|
12
|
+
pipelines: "pipelines",
|
|
13
|
+
};
|
|
14
|
+
export async function manifestForWrite(projectRoot) {
|
|
15
|
+
const outcome = await loadStageflowManifestOutcome(projectRoot);
|
|
16
|
+
if (outcome.ok)
|
|
17
|
+
return outcome.value;
|
|
18
|
+
const fallback = parseStageflowManifestOutcome("version: 1\ncatalog:\n pipelines: [pipelines]\n tasks: [tasks]\n", manifestPathForProject(projectRoot), projectRoot);
|
|
19
|
+
if (!fallback.ok)
|
|
20
|
+
throw new Error("default manifest failed to parse");
|
|
21
|
+
return fallback.value;
|
|
22
|
+
}
|
|
23
|
+
export function explicitRoots(manifest, kind) {
|
|
24
|
+
const entries = manifest.manifest.catalog[kind];
|
|
25
|
+
return entries === undefined ? undefined : entries.map(normalizeCatalogDir);
|
|
26
|
+
}
|
|
27
|
+
async function isFile(abs) {
|
|
28
|
+
try {
|
|
29
|
+
return (await stat(abs)).isFile();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function containedInProject(projectRoot, absPath) {
|
|
36
|
+
let realRoot = path.resolve(projectRoot);
|
|
37
|
+
try {
|
|
38
|
+
realRoot = realpathSync(realRoot);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// project root missing: fall back to the resolved path
|
|
42
|
+
}
|
|
43
|
+
const rel = path.relative(path.resolve(projectRoot), absPath);
|
|
44
|
+
if (rel.startsWith("..") || path.isAbsolute(rel))
|
|
45
|
+
return false;
|
|
46
|
+
return isInsideProjectRoot(projectRoot, path.resolve(realRoot, rel));
|
|
47
|
+
}
|
|
48
|
+
export async function resolveWriteDir(projectRoot, manifest, kind, directory) {
|
|
49
|
+
let relDir;
|
|
50
|
+
if (directory !== undefined) {
|
|
51
|
+
if (path.isAbsolute(directory)) {
|
|
52
|
+
throw new CatalogPathError("Absolute directory is not allowed; use a path relative to the project root", "absolute_path_not_allowed", [projectRoot]);
|
|
53
|
+
}
|
|
54
|
+
relDir = normalizeCatalogDir(directory) || ".";
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
relDir = DEFAULT_DIR[kind];
|
|
58
|
+
for (const entry of explicitRoots(manifest, kind) ?? []) {
|
|
59
|
+
if (!(await isFile(path.join(projectRoot, entry)))) {
|
|
60
|
+
relDir = entry;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const absDir = path.resolve(projectRoot, relDir);
|
|
66
|
+
if (!containedInProject(projectRoot, absDir)) {
|
|
67
|
+
throw new CatalogPathError(`directory escapes project root ${projectRoot}`, "path_outside_project_root", [projectRoot]);
|
|
68
|
+
}
|
|
69
|
+
return { absDir, relDir, explicitDirectory: directory !== undefined };
|
|
70
|
+
}
|
|
71
|
+
export function assertInsideProject(projectRoot, absPath, what) {
|
|
72
|
+
if (!containedInProject(projectRoot, absPath)) {
|
|
73
|
+
throw new CatalogPathError(`${what} escapes project root ${projectRoot}`, "path_outside_project_root", [
|
|
74
|
+
projectRoot,
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function evalStem(fileName) {
|
|
79
|
+
return fileName.replace(/\.eval\.ya?ml$/, "");
|
|
80
|
+
}
|
|
81
|
+
export function datasetStem(fileName) {
|
|
82
|
+
return fileName.replace(/\.dataset\.(jsonl|json|csv)$/, "");
|
|
83
|
+
}
|
|
84
|
+
export async function findEvalFile(projectRoot, id, manifest) {
|
|
85
|
+
const m = manifest ?? (await manifestForWrite(projectRoot));
|
|
86
|
+
const refs = await listEvalFiles(m);
|
|
87
|
+
return refs.find((ref) => evalStem(path.basename(ref.absPath)) === id);
|
|
88
|
+
}
|
|
89
|
+
export function datasetIdsFor(refs) {
|
|
90
|
+
const byStem = new Map();
|
|
91
|
+
for (const ref of refs) {
|
|
92
|
+
const stem = datasetStem(path.basename(ref.absPath));
|
|
93
|
+
byStem.set(stem, [...(byStem.get(stem) ?? []), ref]);
|
|
94
|
+
}
|
|
95
|
+
const ids = new Map();
|
|
96
|
+
for (const [stem, group] of byStem) {
|
|
97
|
+
for (const ref of group) {
|
|
98
|
+
const format = datasetFormatFromPath(ref.absPath);
|
|
99
|
+
ids.set(ref.absPath, group.length > 1 && format !== "jsonl" ? `${stem}.${format ?? "unknown"}` : stem);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return ids;
|
|
103
|
+
}
|
|
104
|
+
export async function findDatasetFile(projectRoot, id, manifest) {
|
|
105
|
+
const m = manifest ?? (await manifestForWrite(projectRoot));
|
|
106
|
+
const refs = await listDatasetFiles(m);
|
|
107
|
+
const ids = datasetIdsFor(refs);
|
|
108
|
+
return refs.find((ref) => ids.get(ref.absPath) === id);
|
|
109
|
+
}
|
|
@@ -27,6 +27,7 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
|
|
|
27
27
|
"STAGEFLOW_READ_TOKEN",
|
|
28
28
|
"STAGEFLOW_READ_TOKEN_FILE",
|
|
29
29
|
"STAGEFLOW_ALLOW_UNKNOWN_CONFIG",
|
|
30
|
+
"STAGEFLOW_EVALS_KEEP_WORKDIR",
|
|
30
31
|
"STAGEFLOW_MAX_CONCURRENT_RUNS",
|
|
31
32
|
"STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT",
|
|
32
33
|
"STAGEFLOW_MAX_QUEUED",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentDetail } from "../agents/service.js";
|
|
2
|
+
export declare const EVAL_SAMPLE_FILES: {
|
|
3
|
+
readonly pipeline: "eval-sample.pipeline.yaml";
|
|
4
|
+
readonly eval: "critique-sample.eval.yaml";
|
|
5
|
+
readonly dataset: "critique-sample.dataset.jsonl";
|
|
6
|
+
};
|
|
7
|
+
export declare const EVAL_SAMPLE_ID = "critique-sample";
|
|
8
|
+
export declare const AGENT_EVAL_SAMPLE_FILES: {
|
|
9
|
+
readonly agent: "support-sample.agent.yaml";
|
|
10
|
+
readonly eval: "support-sample.eval.yaml";
|
|
11
|
+
readonly dataset: "support-sample.dataset.jsonl";
|
|
12
|
+
};
|
|
13
|
+
export declare const AGENT_EVAL_SAMPLE_ID = "support-sample";
|
|
14
|
+
export declare const AGENT_EVAL_SAMPLE_AGENT_ID = "support_sample";
|
|
15
|
+
export type EvalSampleKind = "stage" | "agent";
|
|
16
|
+
/** The slice of `AgentService` the agent sample install needs. */
|
|
17
|
+
export type EvalSampleAgents = {
|
|
18
|
+
get(projectRoot: string, agentId: string): Promise<AgentDetail>;
|
|
19
|
+
create(projectRoot: string, rawInput: unknown, actor: {
|
|
20
|
+
source: "api";
|
|
21
|
+
}): Promise<AgentDetail>;
|
|
22
|
+
setEnabled(projectRoot: string, agentId: string, enabled: boolean, actor: {
|
|
23
|
+
source: "api";
|
|
24
|
+
}): Promise<AgentDetail>;
|
|
25
|
+
};
|
|
26
|
+
export declare function resolveEvalSampleDir(fromUrl?: string, marker?: string): string | undefined;
|
|
27
|
+
export declare function installEvalSample(input: {
|
|
28
|
+
projectRoot: string;
|
|
29
|
+
sourceDir?: string;
|
|
30
|
+
kind?: EvalSampleKind;
|
|
31
|
+
agents?: EvalSampleAgents;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
evalId: string;
|
|
34
|
+
}>;
|