@springbrand/eval-harness 0.1.0-alpha.1 → 0.1.0-alpha.2

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/dist/index.js DELETED
@@ -1,294 +0,0 @@
1
- // packages/eval-harness/src/index.ts
2
- import { z } from "zod";
3
-
4
- // packages/utils/src/json.ts
5
- function canonicalJsonStringify(value) {
6
- return JSON.stringify(
7
- value,
8
- (_key, item) => item !== null && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) : item
9
- );
10
- }
11
-
12
- // packages/utils/src/content-hash.ts
13
- async function sha256Hex(value) {
14
- const bytes = typeof value === "string" ? new TextEncoder().encode(value) : value;
15
- const buffer = new ArrayBuffer(bytes.byteLength);
16
- new Uint8Array(buffer).set(bytes);
17
- const digest = await crypto.subtle.digest("SHA-256", buffer);
18
- return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
19
- }
20
-
21
- // packages/eval-harness/src/index.ts
22
- var EVAL_SERVICE_HEADER = "X-Eval-Service-Token";
23
- var EVAL_HARNESS_PATH = "/internal/v1/eval-harness";
24
- var id = z.string().trim().min(1);
25
- var uuid = z.uuid();
26
- var evalEnsureChatSchema = z.strictObject({
27
- chatId: uuid,
28
- role: z.enum(["subject", "evaluator"]),
29
- framework: z.literal("sp"),
30
- samplingId: id,
31
- projectId: id,
32
- agentConfig: z.strictObject({ ownerUserId: uuid, userAgentId: uuid }),
33
- /** Only an Evaluator carries it; the Evaluator still owns an ordinary Chat and its own Space. */
34
- evaluationId: id.optional(),
35
- runtime: z.strictObject({ timeoutMs: z.number().finite().positive().optional() }).optional()
36
- }).superRefine((value, context) => {
37
- if (value.role === "evaluator" !== (value.evaluationId !== void 0)) {
38
- context.addIssue({ code: "custom", path: ["evaluationId"], message: "Evaluation ID must match role" });
39
- }
40
- });
41
- var EVAL_INITIAL_FILE_MAX_BYTES = 5e6;
42
- var EVAL_INITIALIZATION_MAX_BYTES = 2e7;
43
- var EVAL_MATERIAL_MAX_FILES = 500;
44
- var EVAL_SESSION_CONTENT_MAX_CHARS = 2e6;
45
- var EVAL_SESSION_MAX_ENTRIES = 1e4;
46
- var EVAL_OUTPUT_SCHEMA_PATH = "/evaluation-output.schema.json";
47
- var EVAL_INITIAL_MATERIALS_ROOT = "/initial-materials";
48
- var EVAL_INITIALIZATION_BODY_MAX_BYTES = Math.max(
49
- Math.ceil(EVAL_INITIALIZATION_MAX_BYTES * 4 / 3) + EVAL_SESSION_CONTENT_MAX_CHARS * 6,
50
- Math.ceil((EVAL_INITIALIZATION_MAX_BYTES - EVAL_INITIAL_FILE_MAX_BYTES) * 4 / 3) + EVAL_INITIAL_FILE_MAX_BYTES * 6
51
- ) + 1e6;
52
- var base64Bytes = (value) => value.length / 4 * 3 - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0);
53
- var utf8Bytes = (value) => new TextEncoder().encode(value).byteLength;
54
- var evalMaterialFilesByteSize = (files) => files.reduce((sum, file) => sum + base64Bytes(file.contentBase64), 0);
55
- var tooBig = (context, maximum, message, path = []) => context.addIssue({ code: "too_big", origin: "file", maximum, inclusive: true, path, message });
56
- var maxFileBytes = (bytes) => (value, context) => {
57
- if (bytes(value) > EVAL_INITIAL_FILE_MAX_BYTES) tooBig(context, EVAL_INITIAL_FILE_MAX_BYTES, "File is too large");
58
- };
59
- var evalMaterialFileSchema = z.strictObject({
60
- path: z.string().refine((path) => evalPathParts(path) !== null, "Invalid initial material path"),
61
- mediaType: id,
62
- contentBase64: z.base64().superRefine(maxFileBytes(base64Bytes))
63
- });
64
- var evalMaterialFilesSchema = z.array(evalMaterialFileSchema).max(EVAL_MATERIAL_MAX_FILES).superRefine((files, context) => {
65
- const seen = /* @__PURE__ */ new Set();
66
- files.forEach(({ path }, index) => {
67
- if (seen.has(path)) context.addIssue({ code: "custom", path: [index, "path"], message: "Duplicate material path" });
68
- seen.add(path);
69
- });
70
- });
71
- var evalInitialMaterialSchema = z.strictObject({
72
- bundleId: id,
73
- materialId: id,
74
- files: evalMaterialFilesSchema
75
- });
76
- var evalInitialContextSchema = z.strictObject({
77
- format: z.enum(["json", "jsonl"]),
78
- content: z.string().min(1)
79
- });
80
- var evalInitializationSchema = z.strictObject({
81
- idempotencyKey: id,
82
- data: z.record(z.string(), z.unknown()).and(z.object({ userPrompt: z.string().trim().min(1) })),
83
- initialSession: z.strictObject({
84
- bundleId: id,
85
- sessionId: id,
86
- context: evalInitialContextSchema.optional()
87
- }).optional(),
88
- initialMaterials: z.array(evalInitialMaterialSchema).min(1).optional(),
89
- evaluationOutputSchema: z.strictObject({
90
- content: z.string().min(1).superRefine(maxFileBytes(utf8Bytes))
91
- }).optional()
92
- }).superRefine((value, context) => {
93
- const session = value.data.initialSession;
94
- const refs = value.data.initialMaterials;
95
- const materials = value.initialMaterials;
96
- if (session ? !value.initialSession || session.bundleId !== value.initialSession.bundleId || session.sessionId !== value.initialSession.sessionId : value.initialSession !== void 0) {
97
- context.addIssue({ code: "custom", path: ["initialSession"], message: "Session content must match the Data reference" });
98
- }
99
- if (refs === void 0 ? materials !== void 0 : !Array.isArray(refs) || refs.length !== materials?.length || materials.some((material, index) => {
100
- const ref = refs[index];
101
- return ref?.bundleId !== material.bundleId || ref?.materialId !== material.materialId;
102
- })) {
103
- context.addIssue({ code: "custom", path: ["initialMaterials"], message: "Material content must match the Data reference" });
104
- }
105
- const seen = /* @__PURE__ */ new Set();
106
- materials?.forEach(({ bundleId, materialId }, index) => {
107
- const key = JSON.stringify([bundleId, materialId]);
108
- if (seen.has(key)) context.addIssue({ code: "custom", path: ["initialMaterials", index], message: "Duplicate material" });
109
- seen.add(key);
110
- });
111
- const total = (materials ?? []).reduce(
112
- (sum, material) => sum + evalMaterialFilesByteSize(material.files),
113
- value.evaluationOutputSchema ? utf8Bytes(value.evaluationOutputSchema.content) : 0
114
- );
115
- if (total > EVAL_INITIALIZATION_MAX_BYTES) {
116
- tooBig(context, EVAL_INITIALIZATION_MAX_BYTES, "Initialization files are too large");
117
- }
118
- });
119
- var evalInitializationResultSchema = z.strictObject({
120
- manifestHash: id,
121
- target: z.literal("workspace"),
122
- materials: z.array(z.strictObject({ bundleId: id, materialId: id, rootPath: z.string().startsWith("/") })),
123
- evaluationOutputSchemaPath: z.literal(EVAL_OUTPUT_SCHEMA_PATH).exactOptional()
124
- });
125
- var evalSubmitSchema = z.strictObject({
126
- idempotencyKey: id,
127
- prompt: z.string().trim().min(1),
128
- unattended: z.literal(true),
129
- runtime: z.strictObject({ timeoutMs: z.number().finite().positive().optional() }).optional()
130
- });
131
- var evalBillingResourcesSchema = z.strictObject({
132
- usageEvents: z.array(z.record(z.string(), z.unknown())),
133
- billingSettlements: z.array(z.record(z.string(), z.unknown()))
134
- });
135
- function projectEvalSubmission(input) {
136
- const normalizedStatus = {
137
- pending: "queued",
138
- running: "running",
139
- completed: "completed",
140
- error: "failed",
141
- skipped: "failed",
142
- aborted: "interrupted"
143
- }[input.status];
144
- if (!normalizedStatus) throw new Error("Unknown SP Submission status");
145
- return { ...input, normalizedStatus };
146
- }
147
- function readEvalCanonicalContext(input) {
148
- if (input.content.length > EVAL_SESSION_CONTENT_MAX_CHARS) throw new Error("Initial context is too large");
149
- const parsed = input.format === "json" ? JSON.parse(input.content) : { entries: input.content.split(/\r?\n/u).filter((line) => line.trim()).map((line) => JSON.parse(line)) };
150
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.entries)) {
151
- throw new Error("Initial context must contain canonical entries");
152
- }
153
- const entries = parsed.entries;
154
- if (entries.length > EVAL_SESSION_MAX_ENTRIES) throw new Error("Initial context has too many entries");
155
- const seen = /* @__PURE__ */ new Set();
156
- for (const entry of entries) {
157
- if (!entry || typeof entry !== "object") throw new Error("Invalid canonical context entry");
158
- const row = entry;
159
- if (typeof row.id !== "string" || !row.id || seen.has(row.id) || !Number.isSafeInteger(row.createdAt) || Number(row.createdAt) < 0 || !row.message || typeof row.message !== "object" || Array.isArray(row.message) || !["user", "assistant", "toolResult"].includes(row.message.role ?? "")) {
160
- throw new Error("Invalid or duplicate canonical context entry");
161
- }
162
- seen.add(row.id);
163
- }
164
- if (entries.length > 0 && entries[0].message.role !== "user") {
165
- throw new Error("Initial context must begin with a user message");
166
- }
167
- return { entries };
168
- }
169
- var EVAL_RESULT_FILE = "result.json";
170
- var EVAL_RESULT_PATH = `/output/${EVAL_RESULT_FILE}`;
171
- var EVAL_RESULT_MAX_BYTES = 1e6;
172
- async function readEvalStructuredOutput(path, bytes) {
173
- if (bytes.byteLength > EVAL_RESULT_MAX_BYTES) throw new Error(`${EVAL_RESULT_FILE} exceeds ${EVAL_RESULT_MAX_BYTES} bytes`);
174
- let content;
175
- try {
176
- content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
177
- } catch {
178
- throw new Error(`${EVAL_RESULT_FILE} is not valid UTF-8`);
179
- }
180
- const contentHash = `sha256:${await sha256Hex(bytes)}`;
181
- return { path, content, contentHash };
182
- }
183
- function evalMaterialRoot(bundleId, materialId) {
184
- return `${EVAL_INITIAL_MATERIALS_ROOT}/${evalPathSegment(bundleId)}/${evalPathSegment(materialId)}`;
185
- }
186
- function evalPathSegment(value) {
187
- return encodeURIComponent(value).replaceAll(".", "%2E");
188
- }
189
- function evalPathParts(path) {
190
- const parts = path.split("/").slice(1);
191
- if (!path.startsWith("/") || parts.length === 0 || parts.some((part) => !part || part === "." || part === ".." || part === ".git" || part.includes("\\") || part.includes("\0"))) {
192
- return null;
193
- }
194
- return parts;
195
- }
196
- function evalMaterialPath(root, path) {
197
- const parts = evalPathParts(path);
198
- if (!parts) throw new Error("Invalid initial material path");
199
- return `${root}/${parts.join("/")}`;
200
- }
201
- var EVAL_EVIDENCE_ROOT = "/evidence";
202
- var EVAL_EVIDENCE_PATHS = {
203
- subjectInputs: "/subject-inputs",
204
- artifacts: "/artifacts",
205
- trajectory: "/trajectory",
206
- manifest: "/manifest.json"
207
- };
208
- var EVAL_EVIDENCE_FILE_MAX_BYTES = 25e6;
209
- var EVAL_EVIDENCE_TRAJECTORY_PAGE_MAX = 200;
210
- var EVAL_EVIDENCE_SHA256_HEADER = "X-Eval-Content-Sha256";
211
- var evalEvidenceArtifactSchema = z.strictObject({
212
- path: z.string().startsWith("/").min(2),
213
- size: z.number().int().nonnegative(),
214
- mediaType: id
215
- });
216
- var evalEvidenceArtifactsSchema = z.strictObject({
217
- artifacts: z.array(evalEvidenceArtifactSchema)
218
- });
219
- function readEvalTrajectoryPageQuery(params) {
220
- const afterSeq = Number(params.get("afterSeq") ?? "0");
221
- const limit = Number(params.get("limit") ?? String(EVAL_EVIDENCE_TRAJECTORY_PAGE_MAX));
222
- if (!Number.isSafeInteger(afterSeq) || afterSeq < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > EVAL_EVIDENCE_TRAJECTORY_PAGE_MAX) {
223
- throw new Error("Invalid trajectory page");
224
- }
225
- return { afterSeq, limit };
226
- }
227
- function evalEvidencePath(relative) {
228
- const parts = evalPathParts(relative);
229
- if (!parts || relative !== EVAL_EVIDENCE_PATHS.manifest && ![EVAL_EVIDENCE_PATHS.subjectInputs, EVAL_EVIDENCE_PATHS.artifacts, EVAL_EVIDENCE_PATHS.trajectory].some((root) => relative.startsWith(`${root}/`))) throw new Error("Invalid evidence path");
230
- return `${EVAL_EVIDENCE_ROOT}/${parts.join("/")}`;
231
- }
232
- function evalSubjectInputEvidencePath(bundleId, materialId, path) {
233
- return evalMaterialPath(
234
- `${EVAL_EVIDENCE_PATHS.subjectInputs}/${evalPathSegment(bundleId)}/${evalPathSegment(materialId)}`,
235
- path
236
- );
237
- }
238
- function evalArtifactEvidencePath(path) {
239
- return evalMaterialPath(EVAL_EVIDENCE_PATHS.artifacts, path);
240
- }
241
- function evalTrajectoryEvidencePath(part) {
242
- if (!Number.isSafeInteger(part) || part < 1) throw new Error("Invalid trajectory part");
243
- return `${EVAL_EVIDENCE_PATHS.trajectory}/events-${String(part).padStart(6, "0")}.jsonl`;
244
- }
245
- function isEvalSha256(value) {
246
- return value !== null && /^[0-9a-f]{64}$/u.test(value);
247
- }
248
- function stableEvalFacts(value) {
249
- const facts = canonicalJsonStringify(value);
250
- if (facts === void 0) throw new TypeError("Eval facts must be JSON");
251
- return facts;
252
- }
253
- export {
254
- EVAL_EVIDENCE_FILE_MAX_BYTES,
255
- EVAL_EVIDENCE_PATHS,
256
- EVAL_EVIDENCE_ROOT,
257
- EVAL_EVIDENCE_SHA256_HEADER,
258
- EVAL_EVIDENCE_TRAJECTORY_PAGE_MAX,
259
- EVAL_HARNESS_PATH,
260
- EVAL_INITIALIZATION_BODY_MAX_BYTES,
261
- EVAL_INITIALIZATION_MAX_BYTES,
262
- EVAL_INITIAL_FILE_MAX_BYTES,
263
- EVAL_INITIAL_MATERIALS_ROOT,
264
- EVAL_MATERIAL_MAX_FILES,
265
- EVAL_OUTPUT_SCHEMA_PATH,
266
- EVAL_RESULT_FILE,
267
- EVAL_RESULT_MAX_BYTES,
268
- EVAL_RESULT_PATH,
269
- EVAL_SERVICE_HEADER,
270
- EVAL_SESSION_CONTENT_MAX_CHARS,
271
- EVAL_SESSION_MAX_ENTRIES,
272
- evalArtifactEvidencePath,
273
- evalBillingResourcesSchema,
274
- evalEnsureChatSchema,
275
- evalEvidenceArtifactSchema,
276
- evalEvidenceArtifactsSchema,
277
- evalEvidencePath,
278
- evalInitialContextSchema,
279
- evalInitializationResultSchema,
280
- evalInitializationSchema,
281
- evalMaterialFilesByteSize,
282
- evalMaterialFilesSchema,
283
- evalMaterialPath,
284
- evalMaterialRoot,
285
- evalSubjectInputEvidencePath,
286
- evalSubmitSchema,
287
- evalTrajectoryEvidencePath,
288
- isEvalSha256,
289
- projectEvalSubmission,
290
- readEvalCanonicalContext,
291
- readEvalStructuredOutput,
292
- readEvalTrajectoryPageQuery,
293
- stableEvalFacts
294
- };