@velum-labs/routekit-eval-setup 1.0.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/LICENSE +201 -0
- package/README.md +23 -0
- package/dist/effect-api.d.ts +12 -0
- package/dist/effect-api.js +9 -0
- package/dist/errors.d.ts +91 -0
- package/dist/errors.js +46 -0
- package/dist/host-metadata.d.ts +32 -0
- package/dist/host-metadata.js +46 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +13 -0
- package/dist/inspection.d.ts +24 -0
- package/dist/inspection.js +261 -0
- package/dist/model-selection.d.ts +6 -0
- package/dist/model-selection.js +37 -0
- package/dist/ori-authoring.d.ts +16 -0
- package/dist/ori-authoring.js +17 -0
- package/dist/ori-result.d.ts +45 -0
- package/dist/ori-result.js +1 -0
- package/dist/project-artifacts.d.ts +31 -0
- package/dist/project-artifacts.js +353 -0
- package/dist/project-authoring.d.ts +68 -0
- package/dist/project-authoring.js +431 -0
- package/dist/project-contracts.d.ts +1197 -0
- package/dist/project-contracts.js +396 -0
- package/dist/project-store.d.ts +13 -0
- package/dist/project-store.js +53 -0
- package/dist/project-workflow.d.ts +33 -0
- package/dist/project-workflow.js +904 -0
- package/dist/questions.d.ts +7 -0
- package/dist/questions.js +67 -0
- package/dist/runner.d.ts +8 -0
- package/dist/runner.js +16 -0
- package/dist/service.d.ts +24 -0
- package/dist/service.js +279 -0
- package/dist/state-store.d.ts +21 -0
- package/dist/state-store.js +86 -0
- package/dist/test/inspection.test.d.ts +1 -0
- package/dist/test/inspection.test.js +68 -0
- package/dist/test/model-selection.test.d.ts +1 -0
- package/dist/test/model-selection.test.js +15 -0
- package/dist/test/project-authoring.test.d.ts +1 -0
- package/dist/test/project-authoring.test.js +67 -0
- package/dist/test/project-workflow.test.d.ts +1 -0
- package/dist/test/project-workflow.test.js +516 -0
- package/dist/test/questions.test.d.ts +1 -0
- package/dist/test/questions.test.js +48 -0
- package/dist/test/skill.test.d.ts +1 -0
- package/dist/test/skill.test.js +31 -0
- package/dist/test/state-store.test.d.ts +1 -0
- package/dist/test/state-store.test.js +30 -0
- package/dist/test/workflow.test.d.ts +1 -0
- package/dist/test/workflow.test.js +167 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.js +1 -0
- package/package.json +52 -0
- package/skills/setup-eval-routing/SKILL.md +149 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, rm, stat } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { after, test } from "node:test";
|
|
6
|
+
import { layer as NodeServicesLayer } from "@effect/platform-node/NodeServices";
|
|
7
|
+
import { Effect } from "effect";
|
|
8
|
+
import { initialSetupState, makeFileEvalSetupStateStore } from "../state-store.js";
|
|
9
|
+
const roots = [];
|
|
10
|
+
after(async () => Promise.all(roots.map((root) => rm(root, { recursive: true, force: true }))));
|
|
11
|
+
test("setup state and run checkpoints survive process interruption", async () => {
|
|
12
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "routekit-eval-setup-state-"));
|
|
13
|
+
roots.push(root);
|
|
14
|
+
await Effect.runPromise(Effect.gen(function* () {
|
|
15
|
+
const store = yield* makeFileEvalSetupStateStore;
|
|
16
|
+
const state = {
|
|
17
|
+
...initialSetupState({
|
|
18
|
+
profileId: "support",
|
|
19
|
+
repositoryRoot: root,
|
|
20
|
+
now: "2026-08-15T00:00:00.000Z"
|
|
21
|
+
}),
|
|
22
|
+
openQuestion: "Which workflow?"
|
|
23
|
+
};
|
|
24
|
+
yield* store.save(state);
|
|
25
|
+
assert.deepEqual(yield* store.load(root, "support"), state);
|
|
26
|
+
}).pipe(Effect.provide(NodeServicesLayer)));
|
|
27
|
+
const mode = (await stat(path.join(root, ".routekit", "eval-setup", "support", "state.json")))
|
|
28
|
+
.mode;
|
|
29
|
+
assert.equal(mode & 0o777, 0o600);
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { after, test } from "node:test";
|
|
6
|
+
import { layer as NodeServicesLayer } from "@effect/platform-node/NodeServices";
|
|
7
|
+
import { Effect, Layer } from "effect";
|
|
8
|
+
import { EvalSetup, EvalSetupLive, EvalSetupRunner, OriEvalAuthoring, oriAuthoringFromApi } from "../index.js";
|
|
9
|
+
const roots = [];
|
|
10
|
+
after(async () => Promise.all(roots.map((root) => rm(root, { recursive: true, force: true }))));
|
|
11
|
+
const waiting = (tag, prompt) => ({
|
|
12
|
+
ok: true,
|
|
13
|
+
status: "waiting",
|
|
14
|
+
runDirectory: "/tmp/ori-run",
|
|
15
|
+
tag,
|
|
16
|
+
prompt,
|
|
17
|
+
question: prompt,
|
|
18
|
+
options: ["Support replies", "Documentation", "Routing"],
|
|
19
|
+
context: "Inspected the repository."
|
|
20
|
+
});
|
|
21
|
+
const fakeAuthoring = (turns) => {
|
|
22
|
+
let index = 0;
|
|
23
|
+
const next = () => turns[Math.min(index++, turns.length - 1)] ?? waiting("surface", "Which surface?");
|
|
24
|
+
return {
|
|
25
|
+
prepare: async () => ({ ok: true, status: "prepared", runDirectory: "/tmp/ori-run" }),
|
|
26
|
+
run: async () => next(),
|
|
27
|
+
answer: async (input) => {
|
|
28
|
+
if (/clarif|what do you mean/iu.test(input.answer)) {
|
|
29
|
+
return { ...waiting("surface", "Which surface?"), accepted: false };
|
|
30
|
+
}
|
|
31
|
+
return next();
|
|
32
|
+
},
|
|
33
|
+
status: async () => turns[Math.max(0, index - 1)] ?? {
|
|
34
|
+
ok: true,
|
|
35
|
+
status: "prepared",
|
|
36
|
+
runDirectory: "/tmp/ori-run"
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const makeLayer = (api, publishes) => EvalSetupLive.pipe(Layer.provide(Layer.mergeAll(OriEvalAuthoring.layer(oriAuthoringFromApi(api)), EvalSetupRunner.layer({
|
|
41
|
+
validate: () => Effect.void,
|
|
42
|
+
estimate: () => Effect.succeed({ callCount: 12, maximumCostUsd: 0.42, pricingKnown: true }),
|
|
43
|
+
publish: () => Effect.sync(() => {
|
|
44
|
+
publishes.count += 1;
|
|
45
|
+
return {
|
|
46
|
+
comparison: {
|
|
47
|
+
version: 1,
|
|
48
|
+
comparisonId: "comparison-1",
|
|
49
|
+
profileId: "support",
|
|
50
|
+
suiteDigest: "suite",
|
|
51
|
+
judgeModel: "openai/judge",
|
|
52
|
+
startedAt: "2026-08-16T00:00:00.000Z",
|
|
53
|
+
finishedAt: "2026-08-16T00:01:00.000Z",
|
|
54
|
+
models: []
|
|
55
|
+
},
|
|
56
|
+
activation: {
|
|
57
|
+
version: 2,
|
|
58
|
+
generatedAt: "2026-08-16T00:01:00.000Z",
|
|
59
|
+
basisDigest: "basis",
|
|
60
|
+
evidenceDigest: "evidence",
|
|
61
|
+
classifierModel: "openai/classifier",
|
|
62
|
+
objective: { kind: "lowest-cost", minimumQuality: 0.8 },
|
|
63
|
+
maximumUnknownWeight: 0.2,
|
|
64
|
+
dimensions: [
|
|
65
|
+
{
|
|
66
|
+
id: "support",
|
|
67
|
+
description: "Support requests",
|
|
68
|
+
includes: ["support"],
|
|
69
|
+
excludes: ["other"]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
candidateModels: ["openai/cheap", "anthropic/strong"],
|
|
73
|
+
evidence: [
|
|
74
|
+
{
|
|
75
|
+
model: "openai/cheap",
|
|
76
|
+
dimensionId: "support",
|
|
77
|
+
suiteDigest: "suite",
|
|
78
|
+
evidenceDigest: "cheap-evidence",
|
|
79
|
+
quality: {
|
|
80
|
+
passRate: 1,
|
|
81
|
+
lowerConfidenceBound: 0.8,
|
|
82
|
+
sampleCount: 1
|
|
83
|
+
},
|
|
84
|
+
failureRate: 0,
|
|
85
|
+
unpricedCalls: 1
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
model: "anthropic/strong",
|
|
89
|
+
dimensionId: "support",
|
|
90
|
+
suiteDigest: "suite",
|
|
91
|
+
evidenceDigest: "strong-evidence",
|
|
92
|
+
quality: {
|
|
93
|
+
passRate: 1,
|
|
94
|
+
lowerConfidenceBound: 0.8,
|
|
95
|
+
sampleCount: 1
|
|
96
|
+
},
|
|
97
|
+
failureRate: 0,
|
|
98
|
+
unpricedCalls: 1
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
})
|
|
104
|
+
}))), Layer.provide(NodeServicesLayer));
|
|
105
|
+
test("prepare, run, answer, and status relay Ori questions and resume durable host metadata", async () => {
|
|
106
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "routekit-eval-setup-ori-"));
|
|
107
|
+
roots.push(root);
|
|
108
|
+
await writeFile(path.join(root, "source.txt"), "unchanged\n");
|
|
109
|
+
const publishes = { count: 0 };
|
|
110
|
+
const api = fakeAuthoring([
|
|
111
|
+
waiting("surface", "Which model call should we evaluate?"),
|
|
112
|
+
{
|
|
113
|
+
ok: true,
|
|
114
|
+
status: "completed",
|
|
115
|
+
runDirectory: "/tmp/ori-run",
|
|
116
|
+
scratchWorkspace: path.join(root, "scratch"),
|
|
117
|
+
evalRuns: []
|
|
118
|
+
}
|
|
119
|
+
]);
|
|
120
|
+
const first = await Effect.runPromise(Effect.gen(function* () {
|
|
121
|
+
const setup = yield* EvalSetup;
|
|
122
|
+
const prepared = yield* setup.prepare(root, "support");
|
|
123
|
+
assert.equal(prepared.state.stage, "prepared");
|
|
124
|
+
const running = yield* setup.runApproved(root, "support");
|
|
125
|
+
assert.equal(running.state.stage, "surface");
|
|
126
|
+
assert.equal(running.question?.prompt, "Which model call should we evaluate?");
|
|
127
|
+
const clarification = yield* setup.answer(root, "support", "Can you clarify?");
|
|
128
|
+
assert.equal(clarification.state.stage, "surface");
|
|
129
|
+
return yield* setup.answer(root, "support", "1");
|
|
130
|
+
}).pipe(Effect.provide(makeLayer(api, publishes))));
|
|
131
|
+
assert.equal(first.state.stage, "completed");
|
|
132
|
+
assert.equal(first.state.answers.surface, "1");
|
|
133
|
+
const resumed = await Effect.runPromise(Effect.gen(function* () {
|
|
134
|
+
const setup = yield* EvalSetup;
|
|
135
|
+
return yield* setup.prepare(root, "support");
|
|
136
|
+
}).pipe(Effect.provide(makeLayer(api, publishes))));
|
|
137
|
+
assert.equal(resumed.state.answers.surface, "1");
|
|
138
|
+
assert.equal(await readFile(path.join(root, "source.txt"), "utf8"), "unchanged\n");
|
|
139
|
+
});
|
|
140
|
+
test("publication requires a completed Ori run and runs exactly once", async () => {
|
|
141
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "routekit-eval-setup-publish-"));
|
|
142
|
+
roots.push(root);
|
|
143
|
+
const publishes = { count: 0 };
|
|
144
|
+
const completed = {
|
|
145
|
+
ok: true,
|
|
146
|
+
status: "completed",
|
|
147
|
+
runDirectory: "/tmp/ori-run",
|
|
148
|
+
scratchWorkspace: path.join(root, "scratch"),
|
|
149
|
+
evalRuns: [{ ok: true, model: "openai/cheap" }]
|
|
150
|
+
};
|
|
151
|
+
const api = fakeAuthoring([waiting("surface", "Which surface?"), completed]);
|
|
152
|
+
const outcome = await Effect.runPromise(Effect.gen(function* () {
|
|
153
|
+
const setup = yield* EvalSetup;
|
|
154
|
+
yield* setup.prepare(root, "support");
|
|
155
|
+
yield* setup.runApproved(root, "support");
|
|
156
|
+
const early = yield* Effect.exit(setup.publishApproved(root, "support"));
|
|
157
|
+
assert.equal(early._tag, "Failure");
|
|
158
|
+
yield* setup.answer(root, "support", "1");
|
|
159
|
+
const published = yield* setup.publishApproved(root, "support");
|
|
160
|
+
const duplicateRun = yield* Effect.exit(setup.runApproved(root, "support"));
|
|
161
|
+
assert.equal(duplicateRun._tag, "Failure");
|
|
162
|
+
return published;
|
|
163
|
+
}).pipe(Effect.provide(makeLayer(api, publishes))));
|
|
164
|
+
assert.equal(outcome.state.stage, "completed");
|
|
165
|
+
assert.deepEqual(outcome.activation?.candidateModels, ["openai/cheap", "anthropic/strong"]);
|
|
166
|
+
assert.equal(publishes.count, 1);
|
|
167
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { EvalComparisonResult, EvalSetupEvent, EvalSetupRunMode, PublishedRoutingActivation, RoutingObjective } from "@velum-labs/routekit-eval-contracts";
|
|
2
|
+
import type { OriEvalResult } from "./ori-result.js";
|
|
3
|
+
export type SetupQuestion = {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly prompt: string;
|
|
6
|
+
readonly context?: string;
|
|
7
|
+
readonly options: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
export type RepositorySurface = {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly model?: string;
|
|
13
|
+
};
|
|
14
|
+
export type RepositoryMaterial = {
|
|
15
|
+
readonly kind: "doc" | "prompt" | "dataset" | "fixture" | "test" | "schema";
|
|
16
|
+
readonly path: string;
|
|
17
|
+
};
|
|
18
|
+
export type RepositoryInspection = {
|
|
19
|
+
readonly repositoryRoot: string;
|
|
20
|
+
readonly surfaces: readonly RepositorySurface[];
|
|
21
|
+
readonly materials: readonly RepositoryMaterial[];
|
|
22
|
+
readonly summary: {
|
|
23
|
+
readonly entriesVisited: number;
|
|
24
|
+
readonly textFilesConsidered: number;
|
|
25
|
+
readonly filesRead: number;
|
|
26
|
+
readonly bytesRead: number;
|
|
27
|
+
readonly skippedOversizedFiles: number;
|
|
28
|
+
readonly truncated: boolean;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export type SetupEstimate = {
|
|
32
|
+
readonly callCount: number;
|
|
33
|
+
readonly maximumCostUsd?: number;
|
|
34
|
+
readonly pricingKnown: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type SetupStateView = {
|
|
37
|
+
readonly profileId: string;
|
|
38
|
+
readonly repositoryRoot: string;
|
|
39
|
+
readonly stage: string;
|
|
40
|
+
readonly revision: number;
|
|
41
|
+
readonly updatedAt: string;
|
|
42
|
+
readonly answers: Record<string, string>;
|
|
43
|
+
readonly runDirectory?: string;
|
|
44
|
+
readonly scratchWorkspace?: string;
|
|
45
|
+
readonly publishApproved?: boolean;
|
|
46
|
+
};
|
|
47
|
+
export type SetupStatus = {
|
|
48
|
+
readonly state: SetupStateView;
|
|
49
|
+
readonly question?: SetupQuestion;
|
|
50
|
+
readonly result?: OriEvalResult;
|
|
51
|
+
};
|
|
52
|
+
export type SetupAnswerResult = SetupStatus & {
|
|
53
|
+
readonly events: readonly EvalSetupEvent[];
|
|
54
|
+
};
|
|
55
|
+
export type SetupRunResult = SetupAnswerResult & {
|
|
56
|
+
readonly comparison?: EvalComparisonResult;
|
|
57
|
+
readonly activation?: PublishedRoutingActivation;
|
|
58
|
+
};
|
|
59
|
+
export type EvalSetupRunnerShape = {
|
|
60
|
+
readonly validate: (result: OriEvalResult) => import("effect").Effect.Effect<void, import("./errors.js").EvalSetupRunnerError>;
|
|
61
|
+
readonly estimate: (result: OriEvalResult, mode: EvalSetupRunMode) => import("effect").Effect.Effect<SetupEstimate, import("./errors.js").EvalSetupRunnerError>;
|
|
62
|
+
readonly publish: (input: {
|
|
63
|
+
readonly profileId: string;
|
|
64
|
+
readonly description: string;
|
|
65
|
+
readonly repositoryRoot: string;
|
|
66
|
+
readonly objective: RoutingObjective;
|
|
67
|
+
readonly result: OriEvalResult;
|
|
68
|
+
}) => import("effect").Effect.Effect<{
|
|
69
|
+
readonly comparison: EvalComparisonResult;
|
|
70
|
+
readonly activation: PublishedRoutingActivation;
|
|
71
|
+
}, import("./errors.js").EvalSetupRunnerError>;
|
|
72
|
+
};
|
|
73
|
+
export type EvalSetupRunCheckpoint = {
|
|
74
|
+
readonly comparison: EvalComparisonResult;
|
|
75
|
+
readonly activation: PublishedRoutingActivation;
|
|
76
|
+
};
|
|
77
|
+
export type { EvalSetupRunMode };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@velum-labs/routekit-eval-setup",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/velum-labs/routekit.git",
|
|
8
|
+
"directory": "packages/eval-setup"
|
|
9
|
+
},
|
|
10
|
+
"description": "Durable Effect onboarding for compositional RouteKit eval routing.",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./effect": {
|
|
19
|
+
"types": "./dist/effect-api.d.ts",
|
|
20
|
+
"default": "./dist/effect-api.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"skills",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"registry": "https://registry.npmjs.org",
|
|
30
|
+
"access": "public",
|
|
31
|
+
"provenance": true
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"effect": "4.0.0-rc.108",
|
|
35
|
+
"@velum-labs/routekit-eval-contracts": "1.0.0",
|
|
36
|
+
"@velum-labs/routekit-runtime": "1.0.0"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"routekit",
|
|
40
|
+
"eval",
|
|
41
|
+
"onboarding",
|
|
42
|
+
"routing"
|
|
43
|
+
],
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@effect/platform-node": "4.0.0-rc.108"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -b",
|
|
49
|
+
"clean": "tsc -b --clean",
|
|
50
|
+
"test": "node --test \"dist/test/*.test.js\""
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-eval-routing
|
|
3
|
+
description: >-
|
|
4
|
+
Onboard or maintain a repository's compositional RouteKit eval routing through
|
|
5
|
+
the public routekit eval CLI. Use when the user wants to define a routing
|
|
6
|
+
basis, review workload dimensions, author or approve evaluations, validate or
|
|
7
|
+
estimate a billed plan, run model evidence, inspect results, activate
|
|
8
|
+
model:auto routing, or resume an interrupted eval project.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Set Up Eval Routing
|
|
12
|
+
|
|
13
|
+
Use the public `routekit eval` CLI as the product boundary. Do not substitute
|
|
14
|
+
internal services, a standalone eval executable, or testkit qualification
|
|
15
|
+
commands for missing CLI functionality.
|
|
16
|
+
|
|
17
|
+
Inside the RouteKit source checkout, build first and use:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
node packages/cli/dist/index.js
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For an installed release, use `routekit`. Refer to either form as `$ROUTEKIT`.
|
|
24
|
+
|
|
25
|
+
## Discover the available interface
|
|
26
|
+
|
|
27
|
+
Run `$ROUTEKIT eval --help` and the relevant subcommand help before acting. Use
|
|
28
|
+
only commands and flags exposed by that CLI version. Prefer `--json` for state,
|
|
29
|
+
plans, estimates, and results.
|
|
30
|
+
|
|
31
|
+
Normal model-backed commands use RouteKit's standard target resolution:
|
|
32
|
+
|
|
33
|
+
1. an explicit global `--remote <name>` or `--local` selection;
|
|
34
|
+
2. the active remote, when configured; otherwise
|
|
35
|
+
3. the local daemon.
|
|
36
|
+
|
|
37
|
+
Do not ask for a gateway URL or credential when that configured target works.
|
|
38
|
+
An explicitly supported external gateway mode may use `--gateway-url` and one
|
|
39
|
+
private credential source, but it is for qualification only and must never
|
|
40
|
+
publish a routing activation.
|
|
41
|
+
|
|
42
|
+
## Resume or initialize
|
|
43
|
+
|
|
44
|
+
1. Run `$ROUTEKIT --json eval status` from the repository root.
|
|
45
|
+
2. If no eval project exists, run `$ROUTEKIT --json eval setup`.
|
|
46
|
+
3. Follow `nextAction` and the returned artifact paths. Durable state lives
|
|
47
|
+
under `.routekit/evals`; resume it rather than starting over after an
|
|
48
|
+
interruption.
|
|
49
|
+
4. When setup returns a question, relay exactly that question and its context.
|
|
50
|
+
Ask one question per turn and never answer it for the user.
|
|
51
|
+
5. Submit the answer unchanged with `eval answer`. Prefer a private temporary
|
|
52
|
+
answer file for multiline text when the CLI supports one, then remove it.
|
|
53
|
+
|
|
54
|
+
Candidate, classifier, author, and judge roles must use explicit
|
|
55
|
+
`provider/model` IDs. Eval traffic must never use `model: auto`.
|
|
56
|
+
|
|
57
|
+
## Build and review the routing basis
|
|
58
|
+
|
|
59
|
+
Use the CLI workflow in this order, following the current `nextAction`:
|
|
60
|
+
|
|
61
|
+
1. `eval propose dimensions`
|
|
62
|
+
2. review the generated routing basis and every workload dimension;
|
|
63
|
+
3. `eval approve dimensions`
|
|
64
|
+
4. `eval propose evaluations`
|
|
65
|
+
5. review every dimension suite, case identity, rubric, manifest, and source
|
|
66
|
+
boundary;
|
|
67
|
+
6. `eval approve evaluations`
|
|
68
|
+
7. `eval validate`
|
|
69
|
+
|
|
70
|
+
Treat proposals as review material, not activation evidence. Approval is bound
|
|
71
|
+
to the exact artifact digest. If an artifact changes, validate and approve the
|
|
72
|
+
new digest rather than reusing an old approval.
|
|
73
|
+
|
|
74
|
+
A useful routing basis normally contains 5–10 separable workload dimensions.
|
|
75
|
+
Each definition should include positive scope, exclusions, and boundary
|
|
76
|
+
examples. Request-envelope capabilities such as tools, vision, context, and
|
|
77
|
+
maximum output are hard requirements, not semantic workload dimensions.
|
|
78
|
+
|
|
79
|
+
Keep generated evaluations and sanitized structured results reviewable in the
|
|
80
|
+
repository when the user approves committing them. Do not hand-edit immutable
|
|
81
|
+
plans or measured run records.
|
|
82
|
+
|
|
83
|
+
## Preserve the classifier boundary
|
|
84
|
+
|
|
85
|
+
The decomposition classifier receives only:
|
|
86
|
+
|
|
87
|
+
- the request; and
|
|
88
|
+
- the reviewed workload-dimension definitions.
|
|
89
|
+
|
|
90
|
+
It emits one weight per dimension plus an unknown weight, normalized to sum to
|
|
91
|
+
one. It must not receive candidate models, evidence, prices, objectives,
|
|
92
|
+
selected models, fallbacks, or previous routing decisions.
|
|
93
|
+
|
|
94
|
+
Model selection is deterministic. It combines the request decomposition, hard
|
|
95
|
+
requirements, the approved objective and constraints, and the published
|
|
96
|
+
model-by-dimension evidence matrix.
|
|
97
|
+
|
|
98
|
+
## Estimate and run
|
|
99
|
+
|
|
100
|
+
Before every billed step:
|
|
101
|
+
|
|
102
|
+
1. explain what will call models and show the resolved target and explicit model
|
|
103
|
+
roles;
|
|
104
|
+
2. run `eval estimate` for the intended scope;
|
|
105
|
+
3. report exact call and token limits and the CLI's pricing status—missing
|
|
106
|
+
pricing is unknown, never zero; and
|
|
107
|
+
4. obtain explicit user approval for that plan.
|
|
108
|
+
|
|
109
|
+
Run the immutable plan with `eval run` using the plan identifier returned by
|
|
110
|
+
the CLI. Do not silently reduce case counts, change candidates, replace failed
|
|
111
|
+
rows, or retry a completed paid plan. After an interruption or ambiguous
|
|
112
|
+
result, use `eval status` and `eval results` before deciding whether work
|
|
113
|
+
remains.
|
|
114
|
+
|
|
115
|
+
Never expose credentials, prompts, responses, headers, or raw child output.
|
|
116
|
+
Never recursively evaluate through `model: auto`.
|
|
117
|
+
|
|
118
|
+
## Review results and activate
|
|
119
|
+
|
|
120
|
+
Use `eval results` to review the decomposition benchmark, every dimension
|
|
121
|
+
suite, the composition benchmark, the complete evidence matrix, accounting,
|
|
122
|
+
and cleanup outcome.
|
|
123
|
+
|
|
124
|
+
Run `eval publish` only when all of these are true:
|
|
125
|
+
|
|
126
|
+
- dimensions and evaluations were approved at their current digests;
|
|
127
|
+
- validation passed and the immutable plan is still fresh;
|
|
128
|
+
- every configured candidate has exactly one judged result for every expected
|
|
129
|
+
case in every dimension;
|
|
130
|
+
- the decomposition and composition benchmarks passed;
|
|
131
|
+
- the run has zero active reservations and zero unknown measurements;
|
|
132
|
+
- the user reviewed the results and explicitly approved activation; and
|
|
133
|
+
- the run used a configured local or remote RouteKit target, not an external
|
|
134
|
+
gateway.
|
|
135
|
+
|
|
136
|
+
Publication installs already-measured evidence atomically; it must not perform
|
|
137
|
+
another billed run. After publication, check `eval status`, then verify an
|
|
138
|
+
ordinary headerless `model: auto` request. Use `routekit calls inspect <call-id>`
|
|
139
|
+
to inspect sanitized routing provenance when needed.
|
|
140
|
+
|
|
141
|
+
## Safety
|
|
142
|
+
|
|
143
|
+
- Never spend or publish silently.
|
|
144
|
+
- Never send repository material before approval for the model-backed step.
|
|
145
|
+
- Never log, echo, or commit credentials.
|
|
146
|
+
- Never describe unknown cost as zero.
|
|
147
|
+
- Never publish incomplete, stale, mismatched, duplicated, or cutoff evidence.
|
|
148
|
+
- Never claim that a passing pilot or classifier-only run is production routing
|
|
149
|
+
qualification.
|