@tangle-network/agent-eval 0.107.0 → 0.108.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/CHANGELOG.md +21 -0
- package/README.md +2 -2
- package/dist/adapters/http.d.ts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/otel.d.ts +1 -1
- package/dist/benchmarks/index.d.ts +3 -1
- package/dist/benchmarks/index.js +49 -3
- package/dist/campaign/index.d.ts +9 -7
- package/dist/campaign/index.js +72 -3407
- package/dist/campaign/index.js.map +1 -1
- package/dist/chunk-4VLZEPJ3.js +4306 -0
- package/dist/chunk-4VLZEPJ3.js.map +1 -0
- package/dist/{chunk-G4DLZAV5.js → chunk-OVPVM4JC.js} +1044 -643
- package/dist/chunk-OVPVM4JC.js.map +1 -0
- package/dist/chunk-T6W5ADLG.js +766 -0
- package/dist/chunk-T6W5ADLG.js.map +1 -0
- package/dist/contract/index.d.ts +16 -6
- package/dist/contract/index.js +10 -11
- package/dist/contract/index.js.map +1 -1
- package/dist/{gepa-BmqTrdtg.d.ts → gepa-B3x5Ulcv.d.ts} +3 -40
- package/dist/hosted/index.d.ts +1 -1
- package/dist/index-pPtfoIJO.d.ts +423 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/multishot/index.d.ts +1 -1
- package/dist/openapi.json +1 -1
- package/dist/{pre-registration-x3f0VpxT.d.ts → pre-registration-BUhVPzE7.d.ts} +1 -1
- package/dist/{provenance-CIjRcI6m.d.ts → provenance-DdDhf6cg.d.ts} +3 -2
- package/dist/rl.d.ts +1 -1
- package/dist/rl.js +6 -6
- package/dist/storage-Dw_f7WMt.d.ts +39 -0
- package/dist/{types-CNZ0tHET.d.ts → types-BdIv5dvA.d.ts} +1 -1
- package/docs/improvement-glossary.md +5 -1
- package/package.json +1 -1
- package/dist/chunk-6PF6LXRY.js +0 -861
- package/dist/chunk-6PF6LXRY.js.map +0 -1
- package/dist/chunk-6QDKWHLS.js +0 -223
- package/dist/chunk-6QDKWHLS.js.map +0 -1
- package/dist/chunk-G4DLZAV5.js.map +0 -1
- package/dist/chunk-V75NN2ZR.js +0 -421
- package/dist/chunk-V75NN2ZR.js.map +0 -1
- package/dist/index-C2CC_dry.d.ts +0 -159
package/dist/chunk-6PF6LXRY.js
DELETED
|
@@ -1,861 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
recoverTruncatedJson
|
|
3
|
-
} from "./chunk-G4DLZAV5.js";
|
|
4
|
-
import {
|
|
5
|
-
clamp01
|
|
6
|
-
} from "./chunk-LOJ2QVCE.js";
|
|
7
|
-
import {
|
|
8
|
-
weightedComposite
|
|
9
|
-
} from "./chunk-XMSYF4A7.js";
|
|
10
|
-
import {
|
|
11
|
-
canonicalize
|
|
12
|
-
} from "./chunk-VSMTAMNK.js";
|
|
13
|
-
import {
|
|
14
|
-
JudgeError,
|
|
15
|
-
ValidationError
|
|
16
|
-
} from "./chunk-ONWEPEDO.js";
|
|
17
|
-
|
|
18
|
-
// src/judges.ts
|
|
19
|
-
var JudgeParseError = class extends JudgeError {
|
|
20
|
-
/** Name of the judge whose response failed to parse. */
|
|
21
|
-
judgeName;
|
|
22
|
-
/** The raw (truncated) model response that failed to parse. */
|
|
23
|
-
raw;
|
|
24
|
-
constructor(judgeName, raw, options) {
|
|
25
|
-
super(`judge '${judgeName}' returned an unparseable response: ${raw.slice(0, 200)}`, options);
|
|
26
|
-
this.judgeName = judgeName;
|
|
27
|
-
this.raw = raw;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
function createDomainExpertJudge(domain) {
|
|
31
|
-
return async (tc, { scenario, turns }) => {
|
|
32
|
-
const conversation = turns.map(
|
|
33
|
-
(t, i) => `Turn ${i + 1}:
|
|
34
|
-
User: ${t.userMessage}
|
|
35
|
-
Agent: ${t.agentResponse.slice(0, 2e3)}`
|
|
36
|
-
).join("\n\n---\n\n");
|
|
37
|
-
const resp = await tc.chat({
|
|
38
|
-
model: "gpt-4o",
|
|
39
|
-
messages: [
|
|
40
|
-
{
|
|
41
|
-
role: "system",
|
|
42
|
-
content: `You are a senior ${domain} professional with 20+ years of experience. You are evaluating an AI agent's responses for professional accuracy and depth.
|
|
43
|
-
|
|
44
|
-
Score STRICTLY. A 5 means "a junior professional could do this." An 8 means "solid mid-career work." A 10 means "I would hire this agent."
|
|
45
|
-
|
|
46
|
-
Evaluate:
|
|
47
|
-
1. **domain_accuracy** (0-10): Are the technical terms correct? Are the recommendations what you'd actually do? Would this advice cause problems if followed?
|
|
48
|
-
2. **professional_depth** (0-10): Does it go beyond surface-level? Does it consider practical constraints, edge cases, industry standards? Or is it generic textbook advice?
|
|
49
|
-
|
|
50
|
-
Respond with JSON only: [{"dimension":"domain_accuracy","score":N,"reasoning":"...","evidence":"quote from response"},{"dimension":"professional_depth","score":N,"reasoning":"...","evidence":"quote"}]`
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
role: "user",
|
|
54
|
-
content: `Persona: ${scenario.persona} (${scenario.label})
|
|
55
|
-
Scenario: ${scenario.thesis}
|
|
56
|
-
|
|
57
|
-
${conversation}`
|
|
58
|
-
}
|
|
59
|
-
],
|
|
60
|
-
temperature: 0.1,
|
|
61
|
-
maxTokens: 800
|
|
62
|
-
});
|
|
63
|
-
return parseJudgeResponse("domain_expert", resp);
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
var codeExecutionJudge = async (tc, { scenario, artifacts }) => {
|
|
67
|
-
const codeBlocks = artifacts.codeBlocks;
|
|
68
|
-
if (codeBlocks.length === 0) {
|
|
69
|
-
return [
|
|
70
|
-
{
|
|
71
|
-
judgeName: "code_execution",
|
|
72
|
-
dimension: "code_execution",
|
|
73
|
-
score: 0,
|
|
74
|
-
reasoning: "No code blocks found in agent response."
|
|
75
|
-
}
|
|
76
|
-
];
|
|
77
|
-
}
|
|
78
|
-
const codeText = codeBlocks.map(
|
|
79
|
-
(b, i) => `Block ${i + 1} (${b.language}):
|
|
80
|
-
\`\`\`${b.language}
|
|
81
|
-
${b.code.slice(0, 3e3)}
|
|
82
|
-
\`\`\``
|
|
83
|
-
).join("\n\n");
|
|
84
|
-
const resp = await tc.chat({
|
|
85
|
-
model: "gpt-4o",
|
|
86
|
-
messages: [
|
|
87
|
-
{
|
|
88
|
-
role: "system",
|
|
89
|
-
content: `You are a principal software engineer reviewing code written by an AI agent.
|
|
90
|
-
|
|
91
|
-
Score STRICTLY:
|
|
92
|
-
1. **executability** (0-10): Would this code run without errors? Check: import errors, undefined variables, missing deps, syntax errors. A 5 means "would run with minor fixes." A 10 means "copy-paste and it works."
|
|
93
|
-
2. **completeness** (0-10): Does it handle the FULL task, or just the happy path? A 5 means "handles the main case." A 10 means "production-ready."
|
|
94
|
-
3. **reusability** (0-10): Could this be saved as a tool and reused? A 5 means "works for this case." A 10 means "general-purpose tool."
|
|
95
|
-
|
|
96
|
-
Respond with JSON only: [{"dimension":"executability","score":N,"reasoning":"...","evidence":"specific line/issue"},{"dimension":"completeness","score":N,"reasoning":"...","evidence":"..."},{"dimension":"reusability","score":N,"reasoning":"...","evidence":"..."}]`
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
role: "user",
|
|
100
|
-
content: `Task: ${scenario.thesis}
|
|
101
|
-
|
|
102
|
-
${codeText}`
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
temperature: 0.1,
|
|
106
|
-
maxTokens: 1e3
|
|
107
|
-
});
|
|
108
|
-
return parseJudgeResponse("code_execution", resp);
|
|
109
|
-
};
|
|
110
|
-
var coherenceJudge = async (tc, { scenario, turns }) => {
|
|
111
|
-
if (turns.length < 2) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
const conversation = turns.map(
|
|
115
|
-
(t, i) => `Turn ${i + 1}:
|
|
116
|
-
User: ${t.userMessage}
|
|
117
|
-
Agent (${t.agentResponse.length} chars): ${t.agentResponse.slice(0, 1500)}`
|
|
118
|
-
).join("\n\n---\n\n");
|
|
119
|
-
const resp = await tc.chat({
|
|
120
|
-
model: "gpt-4o",
|
|
121
|
-
messages: [
|
|
122
|
-
{
|
|
123
|
-
role: "system",
|
|
124
|
-
content: `You evaluate whether an AI agent maintains coherence across a multi-turn conversation.
|
|
125
|
-
|
|
126
|
-
Score STRICTLY:
|
|
127
|
-
1. **consistency** (0-10): Does the agent contradict itself across turns? Does it remember what it said/built earlier?
|
|
128
|
-
2. **progression** (0-10): Does each turn BUILD on the previous? Or does it start fresh? A 5 means "vaguely related." A 10 means "each turn clearly advances the work."
|
|
129
|
-
3. **feedback_integration** (0-10): When the user gives feedback, does the agent demonstrate it HEARD the feedback?
|
|
130
|
-
|
|
131
|
-
Respond with JSON only: [{"dimension":"consistency","score":N,"reasoning":"..."},{"dimension":"progression","score":N,"reasoning":"..."},{"dimension":"feedback_integration","score":N,"reasoning":"..."}]`
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
role: "user",
|
|
135
|
-
content: `Scenario: ${scenario.thesis}
|
|
136
|
-
|
|
137
|
-
${conversation}`
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
temperature: 0.1,
|
|
141
|
-
maxTokens: 800
|
|
142
|
-
});
|
|
143
|
-
return parseJudgeResponse("coherence", resp);
|
|
144
|
-
};
|
|
145
|
-
var adversarialJudge = async (tc, { scenario, turns }) => {
|
|
146
|
-
const conversation = turns.map(
|
|
147
|
-
(t, i) => `Turn ${i + 1}:
|
|
148
|
-
User: ${t.userMessage}
|
|
149
|
-
Agent: ${t.agentResponse.slice(0, 1500)}`
|
|
150
|
-
).join("\n\n---\n\n");
|
|
151
|
-
const resp = await tc.chat({
|
|
152
|
-
model: "gpt-4o",
|
|
153
|
-
messages: [
|
|
154
|
-
{
|
|
155
|
-
role: "system",
|
|
156
|
-
content: `You are a red-team evaluator. Find the WEAKEST point in the AI agent's responses. Be adversarial.
|
|
157
|
-
|
|
158
|
-
1. **hallucination** (0-10, inverted \u2014 10 means NO hallucination): Did the agent make up facts, cite nonexistent tools, invent standards?
|
|
159
|
-
2. **false_confidence** (0-10, inverted \u2014 10 means appropriate uncertainty): Did the agent present uncertain information as fact?
|
|
160
|
-
3. **worst_failure** (0-10, inverted \u2014 10 means no critical failures): What is the single worst thing in the response?
|
|
161
|
-
|
|
162
|
-
Be harsh. If everything is genuinely good, say so \u2014 but look hard first.
|
|
163
|
-
|
|
164
|
-
Respond with JSON only: [{"dimension":"hallucination","score":N,"reasoning":"...","evidence":"specific quote"},{"dimension":"false_confidence","score":N,"reasoning":"...","evidence":"..."},{"dimension":"worst_failure","score":N,"reasoning":"...","evidence":"..."}]`
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
role: "user",
|
|
168
|
-
content: `Persona: ${scenario.persona}
|
|
169
|
-
Scenario: ${scenario.thesis}
|
|
170
|
-
|
|
171
|
-
${conversation}`
|
|
172
|
-
}
|
|
173
|
-
],
|
|
174
|
-
temperature: 0.2,
|
|
175
|
-
maxTokens: 800
|
|
176
|
-
});
|
|
177
|
-
return parseJudgeResponse("adversarial", resp);
|
|
178
|
-
};
|
|
179
|
-
function createCustomJudge(name, systemPrompt, opts) {
|
|
180
|
-
return async (tc, { scenario, turns }) => {
|
|
181
|
-
const conversation = turns.map(
|
|
182
|
-
(t, i) => `Turn ${i + 1}:
|
|
183
|
-
User: ${t.userMessage}
|
|
184
|
-
Agent: ${t.agentResponse.slice(0, 2e3)}`
|
|
185
|
-
).join("\n\n---\n\n");
|
|
186
|
-
const resp = await tc.chat({
|
|
187
|
-
model: opts?.model ?? "gpt-4o",
|
|
188
|
-
messages: [
|
|
189
|
-
{
|
|
190
|
-
role: "system",
|
|
191
|
-
content: systemPrompt
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
role: "user",
|
|
195
|
-
content: `Persona: ${scenario.persona} (${scenario.label})
|
|
196
|
-
Scenario: ${scenario.thesis}
|
|
197
|
-
|
|
198
|
-
${conversation}`
|
|
199
|
-
}
|
|
200
|
-
],
|
|
201
|
-
temperature: opts?.temperature ?? 0.1,
|
|
202
|
-
maxTokens: opts?.maxTokens ?? 1e3
|
|
203
|
-
});
|
|
204
|
-
return parseJudgeResponse(name, resp);
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function defaultJudges(domain) {
|
|
208
|
-
return [createDomainExpertJudge(domain), codeExecutionJudge, coherenceJudge, adversarialJudge];
|
|
209
|
-
}
|
|
210
|
-
function parseJudgeResponse(judgeName, resp) {
|
|
211
|
-
const content = resp.choices?.[0]?.message?.content ?? "";
|
|
212
|
-
try {
|
|
213
|
-
let cleaned = content.replace(/```json\n?|\n?```/g, "").trim();
|
|
214
|
-
const arrayMatch = cleaned.match(/\[[\s\S]*\]/);
|
|
215
|
-
if (arrayMatch) cleaned = arrayMatch[0];
|
|
216
|
-
const parsed = JSON.parse(cleaned);
|
|
217
|
-
return parsed.map((p) => ({
|
|
218
|
-
judgeName,
|
|
219
|
-
dimension: p.dimension,
|
|
220
|
-
score: Math.max(0, Math.min(10, p.score)),
|
|
221
|
-
reasoning: p.reasoning ?? "",
|
|
222
|
-
evidence: p.evidence
|
|
223
|
-
}));
|
|
224
|
-
} catch (err) {
|
|
225
|
-
throw new JudgeParseError(judgeName, content, { cause: err });
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// src/completion-verifier.ts
|
|
230
|
-
import { randomUUID } from "crypto";
|
|
231
|
-
function completionVerdict(input) {
|
|
232
|
-
if (input.requirements.length === 0) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
`completionVerdict: task '${input.taskId}' has no requirement checks \u2014 nothing to derive a verdict from`
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
const measurable = input.requirements.filter((r) => !r.unmeasured);
|
|
238
|
-
const unmeasuredCount = input.requirements.length - measurable.length;
|
|
239
|
-
if (measurable.length === 0) {
|
|
240
|
-
throw new Error(
|
|
241
|
-
`completionVerdict: task '${input.taskId}' has no measurable requirements \u2014 all ${input.requirements.length} correctness checks failed (${input.requirements[0]?.unmeasuredReason ?? "unknown reason"})`
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
const satisfiedCount = measurable.filter((r) => r.satisfied).length;
|
|
245
|
-
const completionRate = satisfiedCount / measurable.length;
|
|
246
|
-
const fullyComplete = unmeasuredCount === 0 && satisfiedCount === measurable.length;
|
|
247
|
-
return {
|
|
248
|
-
taskId: input.taskId,
|
|
249
|
-
requirements: input.requirements,
|
|
250
|
-
completionRate,
|
|
251
|
-
fullyComplete,
|
|
252
|
-
unmeasuredCount,
|
|
253
|
-
valid: fullyComplete,
|
|
254
|
-
score: completionRate
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
258
|
-
"the",
|
|
259
|
-
"a",
|
|
260
|
-
"an",
|
|
261
|
-
"of",
|
|
262
|
-
"for",
|
|
263
|
-
"and",
|
|
264
|
-
"or",
|
|
265
|
-
"to",
|
|
266
|
-
"in",
|
|
267
|
-
"on",
|
|
268
|
-
"with",
|
|
269
|
-
"by"
|
|
270
|
-
]);
|
|
271
|
-
var REQUIREMENT_FORM_STOPWORDS = /* @__PURE__ */ new Set([
|
|
272
|
-
"generated",
|
|
273
|
-
"generate",
|
|
274
|
-
"view",
|
|
275
|
-
"render",
|
|
276
|
-
"rendered",
|
|
277
|
-
"persisted",
|
|
278
|
-
"persist",
|
|
279
|
-
"artifact",
|
|
280
|
-
"file",
|
|
281
|
-
"document",
|
|
282
|
-
"note",
|
|
283
|
-
"proposal",
|
|
284
|
-
"deliverable",
|
|
285
|
-
"output",
|
|
286
|
-
"created",
|
|
287
|
-
"create",
|
|
288
|
-
"produce",
|
|
289
|
-
"produced",
|
|
290
|
-
"flag"
|
|
291
|
-
]);
|
|
292
|
-
var MATCH_THRESHOLD = 0.5;
|
|
293
|
-
var MIN_CONTENT_CHARS = 50;
|
|
294
|
-
function tokens(s, extraStop) {
|
|
295
|
-
return new Set(
|
|
296
|
-
s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1 && !STOPWORDS.has(t) && !extraStop?.has(t))
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
function tokenRecall(requirementText, candidateText) {
|
|
300
|
-
const req = tokens(requirementText, REQUIREMENT_FORM_STOPWORDS);
|
|
301
|
-
if (req.size === 0) return 0;
|
|
302
|
-
const cand = tokens(candidateText);
|
|
303
|
-
let hit = 0;
|
|
304
|
-
for (const t of req) if (cand.has(t)) hit++;
|
|
305
|
-
return hit / req.size;
|
|
306
|
-
}
|
|
307
|
-
function artifactCandidates(req, reqIndex, artifacts) {
|
|
308
|
-
const reqText = `${req.title} ${req.category ?? ""}`;
|
|
309
|
-
const out = [];
|
|
310
|
-
artifacts.forEach((a, i) => {
|
|
311
|
-
if ((a.content ?? "").trim().length < MIN_CONTENT_CHARS) return;
|
|
312
|
-
let score = tokenRecall(
|
|
313
|
-
reqText,
|
|
314
|
-
`${a.path ?? ""} ${a.kind} ${(a.content ?? "").slice(0, 4e3)}`
|
|
315
|
-
);
|
|
316
|
-
if (req.category && a.kind && req.category.toLowerCase() === a.kind.toLowerCase()) {
|
|
317
|
-
score = Math.max(score, 1);
|
|
318
|
-
}
|
|
319
|
-
if (score < MATCH_THRESHOLD) return;
|
|
320
|
-
out.push({
|
|
321
|
-
reqIndex,
|
|
322
|
-
itemKey: `artifact:${i}`,
|
|
323
|
-
score,
|
|
324
|
-
evidence: `artifact '${a.path ?? a.kind}' matched (token recall ${score.toFixed(2)})`,
|
|
325
|
-
content: a.content ?? null
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
return out;
|
|
329
|
-
}
|
|
330
|
-
function proposalCandidates(req, reqIndex, proposals) {
|
|
331
|
-
const reqText = `${req.title} ${req.category ?? ""}`;
|
|
332
|
-
const out = [];
|
|
333
|
-
for (const p of proposals) {
|
|
334
|
-
if (p.status !== "approved") continue;
|
|
335
|
-
const body = (p.content ?? "").trim();
|
|
336
|
-
if (body.length < MIN_CONTENT_CHARS) continue;
|
|
337
|
-
const score = tokenRecall(reqText, `${p.title} ${body}`);
|
|
338
|
-
if (score < MATCH_THRESHOLD) continue;
|
|
339
|
-
out.push({
|
|
340
|
-
reqIndex,
|
|
341
|
-
itemKey: `proposal:${p.id}`,
|
|
342
|
-
score,
|
|
343
|
-
evidence: `approved proposal '${p.title}' matched (token recall ${score.toFixed(2)})`,
|
|
344
|
-
content: body
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
return out;
|
|
348
|
-
}
|
|
349
|
-
function toolCallCandidates(req, reqIndex, toolCalls) {
|
|
350
|
-
const out = [];
|
|
351
|
-
toolCalls.forEach((name, i) => {
|
|
352
|
-
const score = tokenRecall(req.title, name);
|
|
353
|
-
if (score < MATCH_THRESHOLD) return;
|
|
354
|
-
out.push({
|
|
355
|
-
reqIndex,
|
|
356
|
-
itemKey: `tool:${i}`,
|
|
357
|
-
score,
|
|
358
|
-
evidence: `tool call '${name}' matched (token recall ${score.toFixed(2)})`,
|
|
359
|
-
content: null
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
return out;
|
|
363
|
-
}
|
|
364
|
-
async function verifyCompletion(gold, state, checkCorrectness) {
|
|
365
|
-
if (gold.requirements.length === 0) {
|
|
366
|
-
throw new Error(
|
|
367
|
-
`verifyCompletion: task '${gold.taskId}' has no requirements \u2014 malformed gold spec`
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
const candidates = [];
|
|
371
|
-
gold.requirements.forEach((req, i) => {
|
|
372
|
-
const by = req.satisfiedBy ?? "any";
|
|
373
|
-
if (by === "artifact" || by === "any") {
|
|
374
|
-
candidates.push(...artifactCandidates(req, i, state.artifacts));
|
|
375
|
-
}
|
|
376
|
-
if (by === "proposal" || by === "any") {
|
|
377
|
-
candidates.push(...proposalCandidates(req, i, state.proposals));
|
|
378
|
-
}
|
|
379
|
-
if (by === "tool-call" || by === "any") {
|
|
380
|
-
candidates.push(...toolCallCandidates(req, i, state.toolCalls));
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
candidates.sort((a, b) => b.score - a.score);
|
|
384
|
-
const assigned = /* @__PURE__ */ new Map();
|
|
385
|
-
const itemTaken = /* @__PURE__ */ new Set();
|
|
386
|
-
for (const c of candidates) {
|
|
387
|
-
if (assigned.has(c.reqIndex) || itemTaken.has(c.itemKey)) continue;
|
|
388
|
-
assigned.set(c.reqIndex, c);
|
|
389
|
-
itemTaken.add(c.itemKey);
|
|
390
|
-
}
|
|
391
|
-
const requirements = [];
|
|
392
|
-
for (let i = 0; i < gold.requirements.length; i++) {
|
|
393
|
-
const req = gold.requirements[i];
|
|
394
|
-
const match = assigned.get(i);
|
|
395
|
-
const evidence = [];
|
|
396
|
-
let correct = null;
|
|
397
|
-
let unmeasuredReason;
|
|
398
|
-
if (match) {
|
|
399
|
-
evidence.push(match.evidence);
|
|
400
|
-
if (match.content !== null) {
|
|
401
|
-
try {
|
|
402
|
-
const r = await checkCorrectness(req, match.content);
|
|
403
|
-
correct = r.correct;
|
|
404
|
-
evidence.push(`correctness: ${r.correct ? "pass" : "fail"} \u2014 ${r.reason}`);
|
|
405
|
-
} catch (err) {
|
|
406
|
-
unmeasuredReason = err instanceof JudgeParseError ? `checker response unparseable after retry: ${err.raw.slice(0, 200)}` : `checker call failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
407
|
-
evidence.push(`correctness: UNMEASURED \u2014 ${unmeasuredReason}`);
|
|
408
|
-
}
|
|
409
|
-
} else {
|
|
410
|
-
evidence.push("correctness: not assessed \u2014 matched item carries no content");
|
|
411
|
-
}
|
|
412
|
-
} else {
|
|
413
|
-
const by = req.satisfiedBy ?? "any";
|
|
414
|
-
const kind = by === "any" ? "artifact/proposal/tool-call" : by;
|
|
415
|
-
evidence.push(`no produced ${kind} matched this requirement`);
|
|
416
|
-
}
|
|
417
|
-
const structurallyPresent = match !== void 0;
|
|
418
|
-
const unmeasured = unmeasuredReason !== void 0;
|
|
419
|
-
const satisfied = structurallyPresent && !unmeasured && correct !== false;
|
|
420
|
-
requirements.push({
|
|
421
|
-
reqId: req.reqId,
|
|
422
|
-
title: req.title,
|
|
423
|
-
structurallyPresent,
|
|
424
|
-
correct,
|
|
425
|
-
satisfied,
|
|
426
|
-
...unmeasured ? { unmeasured: true, unmeasuredReason } : {},
|
|
427
|
-
evidence
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
return completionVerdict({ taskId: gold.taskId, requirements });
|
|
431
|
-
}
|
|
432
|
-
function parseCorrectnessResponse(raw) {
|
|
433
|
-
const readVerdict = (candidate) => {
|
|
434
|
-
if (candidate === null || typeof candidate !== "object") return null;
|
|
435
|
-
const { correct, reason } = candidate;
|
|
436
|
-
if (typeof correct !== "boolean") return null;
|
|
437
|
-
return { correct, reason: typeof reason === "string" ? reason : "" };
|
|
438
|
-
};
|
|
439
|
-
const match = raw.match(/\{[\s\S]*\}/);
|
|
440
|
-
if (match) {
|
|
441
|
-
try {
|
|
442
|
-
const strict = readVerdict(JSON.parse(match[0]));
|
|
443
|
-
if (strict) return strict;
|
|
444
|
-
} catch {
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
const start = raw.indexOf("{");
|
|
448
|
-
if (start !== -1) {
|
|
449
|
-
const recovered = readVerdict(recoverTruncatedJson(raw.slice(start)));
|
|
450
|
-
if (recovered) return recovered;
|
|
451
|
-
}
|
|
452
|
-
throw new JudgeParseError("correctness-checker", raw);
|
|
453
|
-
}
|
|
454
|
-
function createLlmCorrectnessChecker(tc, opts = {}) {
|
|
455
|
-
const model = opts.model ?? "claude-sonnet-4-6";
|
|
456
|
-
const maxContentChars = opts.maxContentChars ?? 8e3;
|
|
457
|
-
const maxAttempts = opts.maxAttempts ?? 2;
|
|
458
|
-
const sink = opts.rawSink;
|
|
459
|
-
const record = async (event) => {
|
|
460
|
-
try {
|
|
461
|
-
await sink?.record(event);
|
|
462
|
-
} catch {
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
return async (requirement, content) => {
|
|
466
|
-
const request = {
|
|
467
|
-
model,
|
|
468
|
-
messages: [
|
|
469
|
-
{
|
|
470
|
-
role: "system",
|
|
471
|
-
content: 'You verify whether a produced work artifact actually fulfils a stated requirement. Judge fulfilment only \u2014 is the deliverable substantively present and on-point \u2014 not polish. A plan to do it later, a vague gesture, or a description of what should be done does NOT fulfil a requirement; the artifact must BE the deliverable. Respond with a single JSON object: {"correct": boolean, "reason": string (<= 30 words)}.'
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
role: "user",
|
|
475
|
-
content: `Requirement: ${requirement.title}
|
|
476
|
-
${requirement.category ? `Category: ${requirement.category}
|
|
477
|
-
` : ""}
|
|
478
|
-
Produced artifact:
|
|
479
|
-
${content.slice(0, maxContentChars)}`
|
|
480
|
-
}
|
|
481
|
-
],
|
|
482
|
-
temperature: 0,
|
|
483
|
-
maxTokens: 200
|
|
484
|
-
};
|
|
485
|
-
let lastErr;
|
|
486
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
487
|
-
const started = Date.now();
|
|
488
|
-
await record({
|
|
489
|
-
eventId: randomUUID(),
|
|
490
|
-
provider: "correctness-checker",
|
|
491
|
-
model,
|
|
492
|
-
endpoint: "/chat",
|
|
493
|
-
baseUrl: "",
|
|
494
|
-
attemptIndex: attempt,
|
|
495
|
-
direction: "request",
|
|
496
|
-
timestamp: started,
|
|
497
|
-
requestBody: request,
|
|
498
|
-
redactedFields: []
|
|
499
|
-
});
|
|
500
|
-
try {
|
|
501
|
-
const resp = await tc.chat(request);
|
|
502
|
-
const raw = resp.choices?.[0]?.message?.content ?? "";
|
|
503
|
-
await record({
|
|
504
|
-
eventId: randomUUID(),
|
|
505
|
-
provider: "correctness-checker",
|
|
506
|
-
model,
|
|
507
|
-
endpoint: "/chat",
|
|
508
|
-
baseUrl: "",
|
|
509
|
-
attemptIndex: attempt,
|
|
510
|
-
direction: "response",
|
|
511
|
-
timestamp: Date.now(),
|
|
512
|
-
durationMs: Date.now() - started,
|
|
513
|
-
responseBody: resp,
|
|
514
|
-
redactedFields: []
|
|
515
|
-
});
|
|
516
|
-
return parseCorrectnessResponse(raw);
|
|
517
|
-
} catch (err) {
|
|
518
|
-
lastErr = err;
|
|
519
|
-
await record({
|
|
520
|
-
eventId: randomUUID(),
|
|
521
|
-
provider: "correctness-checker",
|
|
522
|
-
model,
|
|
523
|
-
endpoint: "/chat",
|
|
524
|
-
baseUrl: "",
|
|
525
|
-
attemptIndex: attempt,
|
|
526
|
-
direction: "error",
|
|
527
|
-
timestamp: Date.now(),
|
|
528
|
-
durationMs: Date.now() - started,
|
|
529
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
530
|
-
redactedFields: []
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
var TITLE_STOPWORDS = /* @__PURE__ */ new Set([
|
|
538
|
-
"the",
|
|
539
|
-
"a",
|
|
540
|
-
"an",
|
|
541
|
-
"and",
|
|
542
|
-
"or",
|
|
543
|
-
"for",
|
|
544
|
-
"to",
|
|
545
|
-
"of",
|
|
546
|
-
"in",
|
|
547
|
-
"on",
|
|
548
|
-
"with",
|
|
549
|
-
"review",
|
|
550
|
-
"update",
|
|
551
|
-
"new",
|
|
552
|
-
"proposed"
|
|
553
|
-
]);
|
|
554
|
-
function createTokenRecallChecker(opts = {}) {
|
|
555
|
-
const minRecall = opts.minRecall ?? 0.5;
|
|
556
|
-
const minLen = opts.minContentLength ?? 120;
|
|
557
|
-
return async (requirement, content) => {
|
|
558
|
-
const body = content.trim();
|
|
559
|
-
if (body.length < minLen)
|
|
560
|
-
return {
|
|
561
|
-
correct: false,
|
|
562
|
-
reason: `content too thin (${body.length} chars) to be the deliverable`
|
|
563
|
-
};
|
|
564
|
-
const titleTokens = requirement.title.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !TITLE_STOPWORDS.has(t));
|
|
565
|
-
if (titleTokens.length === 0)
|
|
566
|
-
return {
|
|
567
|
-
correct: true,
|
|
568
|
-
reason: "requirement title has no significant tokens \u2014 structural match accepted"
|
|
569
|
-
};
|
|
570
|
-
const lower = body.toLowerCase();
|
|
571
|
-
const hits = titleTokens.filter((t) => lower.includes(t)).length;
|
|
572
|
-
const recall = hits / titleTokens.length;
|
|
573
|
-
return recall >= minRecall ? {
|
|
574
|
-
correct: true,
|
|
575
|
-
reason: `content recalls ${hits}/${titleTokens.length} requirement tokens`
|
|
576
|
-
} : {
|
|
577
|
-
correct: false,
|
|
578
|
-
reason: `content recalls only ${hits}/${titleTokens.length} requirement tokens`
|
|
579
|
-
};
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// src/llm-judge.ts
|
|
584
|
-
function llmJudge(name, prompt, opts) {
|
|
585
|
-
if (!name.trim()) {
|
|
586
|
-
throw new Error("llmJudge: name must be non-empty");
|
|
587
|
-
}
|
|
588
|
-
if (!prompt.trim()) {
|
|
589
|
-
throw new Error(`llmJudge '${name}': prompt must be non-empty`);
|
|
590
|
-
}
|
|
591
|
-
const model = opts.model ?? opts.chat.defaultModel;
|
|
592
|
-
if (!model) {
|
|
593
|
-
throw new Error(
|
|
594
|
-
`llmJudge '${name}': no model on opts and no defaultModel on the ChatClient \u2014 pass opts.model or bind defaultModel at createChatClient().`
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
const dimensions = normalizeDimensions(opts.dimensions, name);
|
|
598
|
-
const scale = opts.scale ?? "unit";
|
|
599
|
-
const divisor = scale === "ten" ? 10 : 1;
|
|
600
|
-
const renderUser = opts.renderUser ?? ((input) => JSON.stringify({ scenario: input.scenario, artifact: input.artifact }, null, 2));
|
|
601
|
-
if (opts.weights) {
|
|
602
|
-
for (const key of Object.keys(opts.weights)) {
|
|
603
|
-
if (!dimensions.some((d) => d.key === key)) {
|
|
604
|
-
throw new Error(
|
|
605
|
-
`llmJudge '${name}': weights names dimension '${key}' that is not declared in dimensions`
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
const systemPrompt = `${prompt}
|
|
611
|
-
|
|
612
|
-
${renderContract(dimensions, scale)}`;
|
|
613
|
-
return {
|
|
614
|
-
name,
|
|
615
|
-
dimensions,
|
|
616
|
-
appliesTo: opts.appliesTo,
|
|
617
|
-
async score({ artifact, scenario, signal }) {
|
|
618
|
-
const response = await opts.chat.chat(
|
|
619
|
-
{
|
|
620
|
-
model,
|
|
621
|
-
messages: [
|
|
622
|
-
{ role: "system", content: systemPrompt },
|
|
623
|
-
{ role: "user", content: renderUser({ artifact, scenario }) }
|
|
624
|
-
],
|
|
625
|
-
jsonMode: true,
|
|
626
|
-
temperature: opts.temperature ?? 0.1,
|
|
627
|
-
maxTokens: opts.maxTokens ?? 800
|
|
628
|
-
},
|
|
629
|
-
{ signal }
|
|
630
|
-
);
|
|
631
|
-
const parsed = parseResponse(name, response.content);
|
|
632
|
-
const rawDims = parsed.dimensions ?? parsed.scores;
|
|
633
|
-
if (!rawDims || typeof rawDims !== "object") {
|
|
634
|
-
throw new JudgeParseError(name, response.content, {
|
|
635
|
-
cause: new Error("response has no `dimensions` object")
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
const dims = {};
|
|
639
|
-
for (const { key } of dimensions) {
|
|
640
|
-
const raw = rawDims[key];
|
|
641
|
-
const value = Number(raw);
|
|
642
|
-
if (raw === void 0 || raw === null || !Number.isFinite(value)) {
|
|
643
|
-
throw new JudgeParseError(name, response.content, {
|
|
644
|
-
cause: new Error(
|
|
645
|
-
`dimension '${key}' missing or non-numeric (got ${JSON.stringify(raw)})`
|
|
646
|
-
)
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
dims[key] = clamp01(value / divisor);
|
|
650
|
-
}
|
|
651
|
-
const weights = opts.weights ?? Object.fromEntries(dimensions.map((d) => [d.key, 1 / dimensions.length]));
|
|
652
|
-
const { composite } = weightedComposite({ dims, weights });
|
|
653
|
-
const notes = firstString(parsed.notes) ?? firstString(parsed.rationale) ?? `${name}: composite ${composite.toFixed(3)} over ${dimensions.length} dimension(s)`;
|
|
654
|
-
return { dimensions: dims, composite, notes };
|
|
655
|
-
}
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
function normalizeDimensions(input, name) {
|
|
659
|
-
const raw = input && input.length > 0 ? input : ["quality"];
|
|
660
|
-
const out = [];
|
|
661
|
-
const seen = /* @__PURE__ */ new Set();
|
|
662
|
-
for (const d of raw) {
|
|
663
|
-
const dim = typeof d === "string" ? { key: d, description: d } : d;
|
|
664
|
-
if (!dim.key.trim()) {
|
|
665
|
-
throw new Error(`llmJudge '${name}': dimension key must be non-empty`);
|
|
666
|
-
}
|
|
667
|
-
if (seen.has(dim.key)) {
|
|
668
|
-
throw new Error(`llmJudge '${name}': duplicate dimension key '${dim.key}'`);
|
|
669
|
-
}
|
|
670
|
-
seen.add(dim.key);
|
|
671
|
-
out.push(dim);
|
|
672
|
-
}
|
|
673
|
-
return out;
|
|
674
|
-
}
|
|
675
|
-
function renderContract(dimensions, scale) {
|
|
676
|
-
const range = scale === "ten" ? "0 to 10" : "0.0 to 1.0";
|
|
677
|
-
const lines = dimensions.map((d) => ` - "${d.key}": ${d.description} (score ${range})`);
|
|
678
|
-
const example = `{"dimensions": {${dimensions.map((d) => `"${d.key}": <number>`).join(", ")}}, "notes": "<one-line rationale>"}`;
|
|
679
|
-
return [
|
|
680
|
-
"Score the artifact on EACH of these dimensions:",
|
|
681
|
-
...lines,
|
|
682
|
-
"",
|
|
683
|
-
`Respond with JSON ONLY, no prose. Every dimension is a number in [${range}]:`,
|
|
684
|
-
example
|
|
685
|
-
].join("\n");
|
|
686
|
-
}
|
|
687
|
-
function parseResponse(name, content) {
|
|
688
|
-
const stripped = content.replace(/```json\n?|\n?```/g, "").trim();
|
|
689
|
-
const objMatch = stripped.match(/\{[\s\S]*\}/);
|
|
690
|
-
const payload = objMatch ? objMatch[0] : stripped;
|
|
691
|
-
try {
|
|
692
|
-
const parsed = JSON.parse(payload);
|
|
693
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
694
|
-
throw new Error("parsed value is not an object");
|
|
695
|
-
}
|
|
696
|
-
return parsed;
|
|
697
|
-
} catch (err) {
|
|
698
|
-
throw new JudgeParseError(name, content, { cause: err });
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
function firstString(value) {
|
|
702
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// src/produced-state.ts
|
|
706
|
-
function artifactKind(mimeType) {
|
|
707
|
-
if (!mimeType) return "file";
|
|
708
|
-
if (mimeType.includes("json")) return "json";
|
|
709
|
-
if (mimeType.startsWith("text/")) return "text";
|
|
710
|
-
return "file";
|
|
711
|
-
}
|
|
712
|
-
function extractProducedState(events) {
|
|
713
|
-
const artifacts = [];
|
|
714
|
-
const proposals = [];
|
|
715
|
-
const toolCalls = [];
|
|
716
|
-
const seenTools = /* @__PURE__ */ new Set();
|
|
717
|
-
for (const ev of events) {
|
|
718
|
-
if (ev.type === "tool_call") {
|
|
719
|
-
const name = ev.toolName;
|
|
720
|
-
if (name && !seenTools.has(name)) {
|
|
721
|
-
seenTools.add(name);
|
|
722
|
-
toolCalls.push(name);
|
|
723
|
-
}
|
|
724
|
-
} else if (ev.type === "artifact") {
|
|
725
|
-
const a = ev;
|
|
726
|
-
artifacts.push({
|
|
727
|
-
kind: artifactKind(a.mimeType),
|
|
728
|
-
path: a.name ?? a.uri ?? a.artifactId,
|
|
729
|
-
content: a.content ?? ""
|
|
730
|
-
});
|
|
731
|
-
} else if (ev.type === "proposal_created") {
|
|
732
|
-
const p = ev;
|
|
733
|
-
proposals.push({
|
|
734
|
-
id: p.proposalId,
|
|
735
|
-
title: p.title,
|
|
736
|
-
status: p.status ?? "pending",
|
|
737
|
-
...p.content !== void 0 ? { content: p.content } : {}
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
return { artifacts, proposals, toolCalls };
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// src/agent-profile.ts
|
|
745
|
-
import { createHash } from "crypto";
|
|
746
|
-
import { harnessSupportsModel } from "@tangle-network/agent-interface";
|
|
747
|
-
var CODING_HARNESSES = [
|
|
748
|
-
"opencode",
|
|
749
|
-
"claude-code",
|
|
750
|
-
"codex",
|
|
751
|
-
"kimi-code"
|
|
752
|
-
];
|
|
753
|
-
var HARNESS_NATIVE_MODEL = "default";
|
|
754
|
-
function expandProfileAxes(spec) {
|
|
755
|
-
const harnesses = spec.harnesses ?? CODING_HARNESSES;
|
|
756
|
-
if (harnesses.length === 0) throw new ValidationError("expandProfileAxes: no harnesses to sweep");
|
|
757
|
-
const baseModel = spec.base.model?.default;
|
|
758
|
-
const models = spec.models ?? (baseModel ? [baseModel] : []);
|
|
759
|
-
if (models.length === 0) {
|
|
760
|
-
throw new ValidationError(
|
|
761
|
-
"expandProfileAxes: no models to sweep \u2014 base profile has no model.default and none were supplied"
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
const out = [];
|
|
765
|
-
const seen = /* @__PURE__ */ new Set();
|
|
766
|
-
for (const harness of harnesses) {
|
|
767
|
-
const supported = spec.keepIncompatible ? models : models.filter((model) => harnessSupportsModel(harness, model));
|
|
768
|
-
const effective = supported.length > 0 ? supported : [HARNESS_NATIVE_MODEL];
|
|
769
|
-
for (const model of effective) {
|
|
770
|
-
const profile = {
|
|
771
|
-
...spec.base,
|
|
772
|
-
name: `${spec.base.name ?? "agent"}/${harness}/${model}`,
|
|
773
|
-
model: { ...spec.base.model, default: model },
|
|
774
|
-
metadata: { ...spec.base.metadata ?? {}, harness, harnessModel: model }
|
|
775
|
-
};
|
|
776
|
-
const id = agentProfileId(profile);
|
|
777
|
-
if (seen.has(id)) continue;
|
|
778
|
-
seen.add(id);
|
|
779
|
-
out.push(profile);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
if (out.length === 0) {
|
|
783
|
-
throw new ValidationError(
|
|
784
|
-
`expandProfileAxes: produced no profiles (harnesses=[${harnesses.join(", ")}], models=[${models.join(", ")}]).`
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
return out;
|
|
788
|
-
}
|
|
789
|
-
function harnessAxisOf(profile) {
|
|
790
|
-
const m = profile.metadata;
|
|
791
|
-
const harness = m?.harness;
|
|
792
|
-
const model = m?.harnessModel;
|
|
793
|
-
if (typeof harness === "string" && typeof model === "string") {
|
|
794
|
-
return { harness, model };
|
|
795
|
-
}
|
|
796
|
-
return void 0;
|
|
797
|
-
}
|
|
798
|
-
function agentProfileId(profile) {
|
|
799
|
-
const label = pathSafeProfileLabel(agentProfileDisplayLabel(profile)) ?? "profile";
|
|
800
|
-
return `${label}-${agentProfileHash(profile).slice(0, 16)}`;
|
|
801
|
-
}
|
|
802
|
-
function agentProfileModelId(profile) {
|
|
803
|
-
const model = profile.model?.default?.trim();
|
|
804
|
-
if (!model) {
|
|
805
|
-
const label = agentProfileDisplayLabel(profile) ?? "unnamed profile";
|
|
806
|
-
throw new ValidationError(
|
|
807
|
-
`AgentProfile "${label}" has no model.default \u2014 cannot record eval run`
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
return model;
|
|
811
|
-
}
|
|
812
|
-
function agentProfileDisplayLabel(profile) {
|
|
813
|
-
return profile.name?.trim() || profile.version?.trim() || void 0;
|
|
814
|
-
}
|
|
815
|
-
function pathSafeProfileLabel(label) {
|
|
816
|
-
const safe = label?.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
817
|
-
return safe || void 0;
|
|
818
|
-
}
|
|
819
|
-
function compact(input) {
|
|
820
|
-
const out = {};
|
|
821
|
-
for (const [key, value] of Object.entries(input)) {
|
|
822
|
-
if (value !== void 0) out[key] = value;
|
|
823
|
-
}
|
|
824
|
-
return out;
|
|
825
|
-
}
|
|
826
|
-
function agentProfileHash(profile) {
|
|
827
|
-
const model = agentProfileModelId(profile);
|
|
828
|
-
const behaviour = {
|
|
829
|
-
...profile,
|
|
830
|
-
name: void 0,
|
|
831
|
-
description: void 0,
|
|
832
|
-
tags: profile.tags ? [...profile.tags].sort() : void 0,
|
|
833
|
-
model: compact({ ...profile.model, default: model })
|
|
834
|
-
};
|
|
835
|
-
return createHash("sha256").update(JSON.stringify(canonicalize(behaviour))).digest("hex");
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
export {
|
|
839
|
-
JudgeParseError,
|
|
840
|
-
createDomainExpertJudge,
|
|
841
|
-
codeExecutionJudge,
|
|
842
|
-
coherenceJudge,
|
|
843
|
-
adversarialJudge,
|
|
844
|
-
createCustomJudge,
|
|
845
|
-
defaultJudges,
|
|
846
|
-
completionVerdict,
|
|
847
|
-
verifyCompletion,
|
|
848
|
-
parseCorrectnessResponse,
|
|
849
|
-
createLlmCorrectnessChecker,
|
|
850
|
-
createTokenRecallChecker,
|
|
851
|
-
llmJudge,
|
|
852
|
-
extractProducedState,
|
|
853
|
-
CODING_HARNESSES,
|
|
854
|
-
HARNESS_NATIVE_MODEL,
|
|
855
|
-
expandProfileAxes,
|
|
856
|
-
harnessAxisOf,
|
|
857
|
-
agentProfileId,
|
|
858
|
-
agentProfileModelId,
|
|
859
|
-
agentProfileHash
|
|
860
|
-
};
|
|
861
|
-
//# sourceMappingURL=chunk-6PF6LXRY.js.map
|