@memberjunction/testing-engine 6.1.0-edge.5 → 6.1.0-edge.7
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/drivers/AgentPromptComposer.d.ts +50 -0
- package/dist/drivers/AgentPromptComposer.d.ts.map +1 -0
- package/dist/drivers/AgentPromptComposer.js +60 -0
- package/dist/drivers/AgentPromptComposer.js.map +1 -0
- package/dist/drivers/PinnedVendorPromptRunner.d.ts +30 -0
- package/dist/drivers/PinnedVendorPromptRunner.d.ts.map +1 -0
- package/dist/drivers/PinnedVendorPromptRunner.js +30 -0
- package/dist/drivers/PinnedVendorPromptRunner.js.map +1 -0
- package/dist/drivers/PromptEvalDriver.d.ts +158 -0
- package/dist/drivers/PromptEvalDriver.d.ts.map +1 -0
- package/dist/drivers/PromptEvalDriver.js +365 -0
- package/dist/drivers/PromptEvalDriver.js.map +1 -0
- package/dist/drivers/index.d.ts +1 -0
- package/dist/drivers/index.d.ts.map +1 -1
- package/dist/drivers/index.js +1 -0
- package/dist/drivers/index.js.map +1 -1
- package/dist/engine/TestEngine.d.ts.map +1 -1
- package/dist/engine/TestEngine.js +5 -0
- package/dist/engine/TestEngine.js.map +1 -1
- package/dist/eval/corpus.d.ts +91 -0
- package/dist/eval/corpus.d.ts.map +1 -0
- package/dist/eval/corpus.js +141 -0
- package/dist/eval/corpus.js.map +1 -0
- package/dist/eval/decision.d.ts +156 -0
- package/dist/eval/decision.d.ts.map +1 -0
- package/dist/eval/decision.js +257 -0
- package/dist/eval/decision.js.map +1 -0
- package/dist/eval/expectation.d.ts +76 -0
- package/dist/eval/expectation.d.ts.map +1 -0
- package/dist/eval/expectation.js +160 -0
- package/dist/eval/expectation.js.map +1 -0
- package/dist/eval/history.d.ts +17 -0
- package/dist/eval/history.d.ts.map +1 -0
- package/dist/eval/history.js +19 -0
- package/dist/eval/history.js.map +1 -0
- package/dist/eval/index.d.ts +15 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +15 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/matchers.d.ts +88 -0
- package/dist/eval/matchers.d.ts.map +1 -0
- package/dist/eval/matchers.js +119 -0
- package/dist/eval/matchers.js.map +1 -0
- package/dist/eval/subAgentTrace.d.ts +52 -0
- package/dist/eval/subAgentTrace.d.ts.map +1 -0
- package/dist/eval/subAgentTrace.js +61 -0
- package/dist/eval/subAgentTrace.js.map +1 -0
- package/dist/eval/wellFormed.d.ts +41 -0
- package/dist/eval/wellFormed.d.ts.map +1 -0
- package/dist/eval/wellFormed.js +83 -0
- package/dist/eval/wellFormed.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/oracles/AgentDecisionOracle.d.ts +54 -0
- package/dist/oracles/AgentDecisionOracle.d.ts.map +1 -0
- package/dist/oracles/AgentDecisionOracle.js +107 -0
- package/dist/oracles/AgentDecisionOracle.js.map +1 -0
- package/dist/oracles/TraceSubAgentValidatorOracle.d.ts +44 -0
- package/dist/oracles/TraceSubAgentValidatorOracle.d.ts.map +1 -0
- package/dist/oracles/TraceSubAgentValidatorOracle.js +105 -0
- package/dist/oracles/TraceSubAgentValidatorOracle.js.map +1 -0
- package/dist/oracles/index.d.ts +2 -0
- package/dist/oracles/index.d.ts.map +1 -1
- package/dist/oracles/index.js +2 -0
- package/dist/oracles/index.js.map +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* decision.ts — normalizing what an agent DECIDED, independently of how it said so.
|
|
3
|
+
*
|
|
4
|
+
* This is the load-bearing idea of the whole eval harness (test plan §1). A corpus case states the
|
|
5
|
+
* decision it expects — "invoke `Run Ad-hoc Query` with these params" — and never how the wire
|
|
6
|
+
* encodes it. Two encodings exist today and a third is coming:
|
|
7
|
+
*
|
|
8
|
+
* envelope — a `LoopAgentResponse` JSON object with `nextStep.actions[]`
|
|
9
|
+
* native — provider tool calls on the assistant turn
|
|
10
|
+
*
|
|
11
|
+
* Both collapse here into one {@link ObservedDecision} before anything is scored. That is what
|
|
12
|
+
* makes a baseline run and a native run *directly comparable on identical cases*:
|
|
13
|
+
* the only thing that changed between them is the model's behavior, not the yardstick.
|
|
14
|
+
*
|
|
15
|
+
* FRAMEWORK-FREE (test plan §2.1). Nothing in `src/eval/` imports from the testing engine — not
|
|
16
|
+
* `IOracle`, not `TestEngine`, not the driver types. The oracles in `src/oracles/` are thin
|
|
17
|
+
* wrappers over these functions, and a bespoke runner could call them directly. That rule exists so
|
|
18
|
+
* the documented fallback (§2.1, option B) stays cheap; it also happens to make every line here
|
|
19
|
+
* unit-testable without a database.
|
|
20
|
+
*/
|
|
21
|
+
function asRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Strips a markdown fence so a fenced-but-otherwise-correct envelope still counts as parsed.
|
|
26
|
+
*
|
|
27
|
+
* Fencing is a formatting habit, not a decision error. Counting it as malformed would inflate the
|
|
28
|
+
* exact metric this harness exists to measure honestly — and MJ's own loop parser tolerates it, so
|
|
29
|
+
* scoring it as a failure would also diverge from what production actually does.
|
|
30
|
+
*/
|
|
31
|
+
export function stripJsonFence(text) {
|
|
32
|
+
const trimmed = text.trim();
|
|
33
|
+
if (!trimmed.startsWith('```')) {
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
return trimmed.replace(/^```[a-zA-Z]*\s*\n?/, '').replace(/\n?```\s*$/, '').trim();
|
|
37
|
+
}
|
|
38
|
+
function readActions(nextStep) {
|
|
39
|
+
const raw = nextStep?.actions;
|
|
40
|
+
if (!Array.isArray(raw)) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const actions = [];
|
|
44
|
+
for (const entry of raw) {
|
|
45
|
+
const record = asRecord(entry);
|
|
46
|
+
const name = record?.name;
|
|
47
|
+
if (typeof name === 'string' && name.length > 0) {
|
|
48
|
+
actions.push({ name, params: asRecord(record?.params) ?? {} });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return actions;
|
|
52
|
+
}
|
|
53
|
+
function readSubAgents(nextStep) {
|
|
54
|
+
const found = [];
|
|
55
|
+
const single = asRecord(nextStep?.subAgent);
|
|
56
|
+
const many = Array.isArray(nextStep?.subAgents) ? nextStep?.subAgents : [];
|
|
57
|
+
for (const entry of [single, ...many.map(asRecord)]) {
|
|
58
|
+
const name = entry?.name;
|
|
59
|
+
if (typeof name === 'string' && name.length > 0) {
|
|
60
|
+
found.push({ name, message: typeof entry?.message === 'string' ? entry.message : undefined });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Picks the decision kind from an envelope's contents.
|
|
67
|
+
*
|
|
68
|
+
* Order matters and mirrors what `LoopAgentType.DetermineNextStep` actually does, not what the
|
|
69
|
+
* interface suggests: a Chat step is honored even when `taskComplete` is true, and an explicit
|
|
70
|
+
* `nextStep` outranks the completion flag. Scoring against a different precedence than production
|
|
71
|
+
* uses would measure the harness, not the agent.
|
|
72
|
+
*/
|
|
73
|
+
function classifyEnvelope(stepType, actions, subAgents, taskComplete, hasPayloadChange, hasResponseForm) {
|
|
74
|
+
const lowered = stepType?.toLowerCase().trim();
|
|
75
|
+
if (lowered === 'chat') {
|
|
76
|
+
return 'chat';
|
|
77
|
+
}
|
|
78
|
+
if (lowered === 'actions' || (!lowered && actions.length > 0)) {
|
|
79
|
+
return 'action';
|
|
80
|
+
}
|
|
81
|
+
if (lowered === 'sub-agent' || (!lowered && subAgents.length > 0)) {
|
|
82
|
+
return 'subAgent';
|
|
83
|
+
}
|
|
84
|
+
if (taskComplete) {
|
|
85
|
+
return 'taskComplete';
|
|
86
|
+
}
|
|
87
|
+
// `responseForm` is a first-class field on LoopAgentResponse — it is HOW an MJ agent asks the
|
|
88
|
+
// user a structured question. A turn carrying one is addressing the user, which is a chat
|
|
89
|
+
// decision by every measure the corpus cares about; only the encoding differs from a plain
|
|
90
|
+
// `nextStep: { type: 'Chat' }`. Ranked below an explicit nextStep so a turn that says both
|
|
91
|
+
// still dispatches the way production dispatches it.
|
|
92
|
+
if (hasResponseForm) {
|
|
93
|
+
return 'chat';
|
|
94
|
+
}
|
|
95
|
+
// A turn whose only content is a payload write is a payloadChange decision; the loop's own
|
|
96
|
+
// validator treats that case as an implicit completion rather than an error.
|
|
97
|
+
if (hasPayloadChange) {
|
|
98
|
+
return 'payloadChange';
|
|
99
|
+
}
|
|
100
|
+
return 'other';
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Counts complete top-level JSON objects in a string that failed to parse as one — the
|
|
104
|
+
* "two envelopes back to back" shape. String-aware brace counting; returns 0 unless at least two
|
|
105
|
+
* objects were found, so a single malformed object is left to the ordinary diagnostic.
|
|
106
|
+
*/
|
|
107
|
+
export function countConcatenatedObjects(text) {
|
|
108
|
+
let depth = 0, inString = false, escaped = false, objects = 0;
|
|
109
|
+
for (const ch of text) {
|
|
110
|
+
if (inString) {
|
|
111
|
+
if (escaped) {
|
|
112
|
+
escaped = false;
|
|
113
|
+
}
|
|
114
|
+
else if (ch === '\\') {
|
|
115
|
+
escaped = true;
|
|
116
|
+
}
|
|
117
|
+
else if (ch === '"') {
|
|
118
|
+
inString = false;
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (ch === '"') {
|
|
123
|
+
inString = true;
|
|
124
|
+
}
|
|
125
|
+
else if (ch === '{') {
|
|
126
|
+
depth++;
|
|
127
|
+
}
|
|
128
|
+
else if (ch === '}') {
|
|
129
|
+
depth--;
|
|
130
|
+
if (depth === 0) {
|
|
131
|
+
objects++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return objects >= 2 ? objects : 0;
|
|
136
|
+
}
|
|
137
|
+
/** Reads an envelope out of raw text. Never throws — unparseable output is an observation. */
|
|
138
|
+
function normalizeEnvelope(text) {
|
|
139
|
+
const base = { encoding: 'envelope', actions: [], subAgents: [], taskComplete: false, envelopeParsed: false };
|
|
140
|
+
if (text.trim().length === 0) {
|
|
141
|
+
return { ...base, kind: 'empty', encoding: 'none', envelopeParsed: null, diagnostic: 'no output' };
|
|
142
|
+
}
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = JSON.parse(stripJsonFence(text));
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
const concatenated = countConcatenatedObjects(stripJsonFence(text));
|
|
149
|
+
return {
|
|
150
|
+
...base,
|
|
151
|
+
kind: 'unparseable',
|
|
152
|
+
diagnostic: concatenated > 0
|
|
153
|
+
? `envelope did not parse: ${concatenated} complete JSON objects were concatenated in one turn`
|
|
154
|
+
: `envelope did not parse: ${error.message}`
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const record = asRecord(parsed);
|
|
158
|
+
if (!record) {
|
|
159
|
+
return { ...base, kind: 'unparseable', diagnostic: 'envelope parsed but was not a JSON object' };
|
|
160
|
+
}
|
|
161
|
+
const nextStep = asRecord(record.nextStep);
|
|
162
|
+
const actions = readActions(nextStep);
|
|
163
|
+
const subAgents = readSubAgents(nextStep);
|
|
164
|
+
const payloadChange = asRecord(record.payloadChangeRequest) ?? undefined;
|
|
165
|
+
const taskComplete = record.taskComplete === true;
|
|
166
|
+
const stepType = typeof nextStep?.type === 'string' ? nextStep.type : null;
|
|
167
|
+
return {
|
|
168
|
+
kind: classifyEnvelope(stepType, actions, subAgents, taskComplete, payloadChange !== undefined, asRecord(record.responseForm) !== null),
|
|
169
|
+
encoding: 'envelope',
|
|
170
|
+
stepType,
|
|
171
|
+
actions,
|
|
172
|
+
subAgents,
|
|
173
|
+
message: typeof record.message === 'string' ? record.message : undefined,
|
|
174
|
+
taskComplete,
|
|
175
|
+
payloadChange,
|
|
176
|
+
envelopeParsed: true
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const PLACEHOLDER_VALUES = /^(placeholder|none|n\/a|null|nothing|-)$/i;
|
|
180
|
+
const PLACEHOLDER_PHRASES = /none needed|just checking/i;
|
|
181
|
+
/**
|
|
182
|
+
* Spec §8.2: prose accompanied the call(s) and every call has empty arguments, or every string
|
|
183
|
+
* argument is a placeholder token. The Sonnet 5 pattern of results §13.3, made countable.
|
|
184
|
+
*/
|
|
185
|
+
export function isPlaceholderCall(calls, text) {
|
|
186
|
+
if (!text?.trim() || calls.length === 0)
|
|
187
|
+
return false;
|
|
188
|
+
return calls.every((c) => {
|
|
189
|
+
const values = Object.values(c.arguments ?? {});
|
|
190
|
+
if (values.length === 0)
|
|
191
|
+
return true;
|
|
192
|
+
const strings = values.filter((v) => typeof v === 'string');
|
|
193
|
+
return strings.length === values.length && strings.every((v) => PLACEHOLDER_VALUES.test(v.trim()) || PLACEHOLDER_PHRASES.test(v));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Native-turn classification: control tools first (spec §8.1), then Actions, then payload-only. */
|
|
197
|
+
function normalizeNativeTurn(turn, calls, text) {
|
|
198
|
+
const toActionName = (toolName) => turn.toolNameMap?.[toolName] ?? toolName;
|
|
199
|
+
const role = (toolName) => turn.controlToolMap?.[toolName];
|
|
200
|
+
// The call wins, but if the text is ALSO a valid envelope the model gave
|
|
201
|
+
// two answers. Record it; the scorecard counts it; production does the same on NativeDualChannel.
|
|
202
|
+
const shadow = text ? normalizeEnvelope(text) : null;
|
|
203
|
+
const dualChannel = shadow?.envelopeParsed === true;
|
|
204
|
+
const base = {
|
|
205
|
+
encoding: 'native',
|
|
206
|
+
envelopeParsed: null,
|
|
207
|
+
message: text,
|
|
208
|
+
taskComplete: false,
|
|
209
|
+
dualChannel,
|
|
210
|
+
...(dualChannel ? { shadowEnvelopeKind: shadow.kind } : {}),
|
|
211
|
+
narrationWithCalls: !!text && !dualChannel,
|
|
212
|
+
placeholderCall: isPlaceholderCall(calls, text)
|
|
213
|
+
};
|
|
214
|
+
const ask = calls.find((c) => role(c.name)?.kind === 'chat');
|
|
215
|
+
if (ask) {
|
|
216
|
+
const message = typeof ask.arguments?.message === 'string' ? ask.arguments.message : undefined;
|
|
217
|
+
return { ...base, kind: 'chat', actions: [], subAgents: [], message: message ?? text };
|
|
218
|
+
}
|
|
219
|
+
const payloadCall = calls.find((c) => role(c.name)?.kind === 'payloadChange');
|
|
220
|
+
const payloadChange = payloadCall ? (payloadCall.arguments ?? {}) : undefined;
|
|
221
|
+
const subAgents = calls.flatMap((c) => { const r = role(c.name); return r?.kind === 'subAgent' ? [{ name: r.name }] : []; });
|
|
222
|
+
const actions = calls.filter((c) => !role(c.name)).map((c) => ({ name: toActionName(c.name), params: c.arguments ?? {} }));
|
|
223
|
+
if (subAgents.length > 0) {
|
|
224
|
+
return { ...base, kind: 'subAgent', actions, subAgents, payloadChange };
|
|
225
|
+
}
|
|
226
|
+
if (actions.length > 0) {
|
|
227
|
+
return { ...base, kind: 'action', actions, subAgents: [], payloadChange };
|
|
228
|
+
}
|
|
229
|
+
return { ...base, kind: 'payloadChange', actions: [], subAgents: [], payloadChange };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Normalizes one turn into an {@link ObservedDecision}.
|
|
233
|
+
*
|
|
234
|
+
* Native tool calls win when present: a model that emitted both has made its decision natively and
|
|
235
|
+
* the text is at most a narration of it. This is the rule the corpus specifies, and the provider matrix
|
|
236
|
+
* measured why it matters — Gemini 3.7 Flash under JSON mode answers a tool question natively while
|
|
237
|
+
* ignoring the requested envelope entirely, which an envelope-only reader scores as 100% malformed
|
|
238
|
+
* even though the decision was correct.
|
|
239
|
+
*
|
|
240
|
+
* Under the implicit protocol, plain text with no call is the terminal form: it reads as
|
|
241
|
+
* task completion unless it parses as an envelope, in which case the envelope is honoured (§2.1).
|
|
242
|
+
*/
|
|
243
|
+
export function normalizeDecision(turn) {
|
|
244
|
+
const calls = turn.toolCalls ?? [];
|
|
245
|
+
const text = turn.text?.trim() ? turn.text : undefined;
|
|
246
|
+
if (calls.length > 0) {
|
|
247
|
+
return normalizeNativeTurn(turn, calls, text);
|
|
248
|
+
}
|
|
249
|
+
if (turn.protocol === 'implicit' && text) {
|
|
250
|
+
const asEnvelope = normalizeEnvelope(text);
|
|
251
|
+
if (asEnvelope.envelopeParsed)
|
|
252
|
+
return asEnvelope;
|
|
253
|
+
return { kind: 'taskComplete', encoding: 'text', actions: [], subAgents: [], message: text.trim(), taskComplete: true, envelopeParsed: null };
|
|
254
|
+
}
|
|
255
|
+
return normalizeEnvelope(turn.text ?? '');
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=decision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.js","sourceRoot":"","sources":["../../src/eval/decision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* expectation.ts — the corpus's expectation DSL, and scoring an observation against it.
|
|
3
|
+
*
|
|
4
|
+
* A case states one {@link DecisionExpectation}; {@link evaluateDecision} scores an
|
|
5
|
+
* {@link ObservedDecision} against it and returns the components **separately** rather than only a
|
|
6
|
+
* number. That decomposition is required by test plan §3.2 so the §6.1 metrics — decision accuracy,
|
|
7
|
+
* action accuracy, param fidelity — aggregate straight out of persisted `ResultDetails` without
|
|
8
|
+
* re-running a single model call. A single collapsed score would make every cross-run comparison a
|
|
9
|
+
* re-run.
|
|
10
|
+
*
|
|
11
|
+
* FRAMEWORK-FREE (test plan §2.1) — imports nothing from the testing engine.
|
|
12
|
+
*/
|
|
13
|
+
import type { DecisionKind, ObservedDecision } from './decision.js';
|
|
14
|
+
import { type ParamExpectation, type ParamMatchResult } from './matchers.js';
|
|
15
|
+
/** One action a correct answer invokes, with the checks that make the invocation usable. */
|
|
16
|
+
export interface ExpectedAction {
|
|
17
|
+
name: string;
|
|
18
|
+
params?: ParamExpectation[];
|
|
19
|
+
}
|
|
20
|
+
/** What a correct answer looks like — stated as a decision, never as an encoding. */
|
|
21
|
+
export interface DecisionExpectation {
|
|
22
|
+
kind: DecisionKind;
|
|
23
|
+
/** Required when `kind` is `action`. Compared as a set unless `ordered`. */
|
|
24
|
+
actions?: ExpectedAction[];
|
|
25
|
+
/** Sub-agent names, when `kind` is `subAgent`. */
|
|
26
|
+
subAgents?: string[];
|
|
27
|
+
/** Optional check on the user-facing message — e.g. a `chat` case that must mention a topic. */
|
|
28
|
+
message?: ParamExpectation['matcher'];
|
|
29
|
+
/**
|
|
30
|
+
* Names that must NOT appear, whatever else happens.
|
|
31
|
+
*
|
|
32
|
+
* This is the coherence probe's instrument: with tools declared, a model
|
|
33
|
+
* must *not* reach for one when chat or completion is the right answer. Expressed as a
|
|
34
|
+
* prohibition rather than a kind mismatch so a case can forbid a specific tempting action
|
|
35
|
+
* while still allowing others.
|
|
36
|
+
*/
|
|
37
|
+
forbiddenActions?: string[];
|
|
38
|
+
/** Whether action order is asserted. Defaults to false — parallel calls have no inherent order. */
|
|
39
|
+
ordered?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether actions BEYOND the expected set are tolerated. Defaults to false.
|
|
42
|
+
*
|
|
43
|
+
* False is the right default for a decision corpus: an agent that calls the right action plus
|
|
44
|
+
* two speculative extras has not made the right decision, it has made three. Cases that
|
|
45
|
+
* genuinely do not care set this true.
|
|
46
|
+
*/
|
|
47
|
+
allowAdditionalActions?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Matchers over dotted paths into the observed payload change request, e.g.
|
|
50
|
+
* `newElements.findings`. Scored into `paramFidelity` alongside action parameters.
|
|
51
|
+
*/
|
|
52
|
+
payload?: ParamExpectation[];
|
|
53
|
+
/** Dotted paths into the payload change that must NOT be present (payload scoping). */
|
|
54
|
+
forbiddenPayloadPaths?: string[];
|
|
55
|
+
}
|
|
56
|
+
/** Per-component outcome, so §6.1's metrics fall out of the stored details. */
|
|
57
|
+
export interface DecisionEvaluation {
|
|
58
|
+
/** Metric 3 — did the agent decide the right *kind* of thing? */
|
|
59
|
+
decisionKindMatch: boolean;
|
|
60
|
+
/** Metric 4 — right action name(s). `null` when the case expects no action. */
|
|
61
|
+
actionAccuracy: number | null;
|
|
62
|
+
/** Metric 5 — share of parameter matchers that passed. `null` when the case declares none. */
|
|
63
|
+
paramFidelity: number | null;
|
|
64
|
+
/** Individual matcher outcomes, for reporting which parameter failed and why. */
|
|
65
|
+
paramResults: ParamMatchResult[];
|
|
66
|
+
/** Forbidden names the agent invoked anyway. Non-empty is always a failure. */
|
|
67
|
+
forbiddenViolations: string[];
|
|
68
|
+
/** Overall pass — every applicable component clean. */
|
|
69
|
+
passed: boolean;
|
|
70
|
+
/** Mean of the APPLICABLE components. See the note in {@link evaluateDecision}. */
|
|
71
|
+
score: number;
|
|
72
|
+
/** Human-readable reasons, in the order they were detected. */
|
|
73
|
+
messages: string[];
|
|
74
|
+
}
|
|
75
|
+
export declare function evaluateDecision(expectation: DecisionExpectation, observed: ObservedDecision): DecisionEvaluation;
|
|
76
|
+
//# sourceMappingURL=expectation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expectation.d.ts","sourceRoot":"","sources":["../../src/eval/expectation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAe,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvF,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,mGAAmG;IACnG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,uFAAuF;IACvF,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IAC/B,iEAAiE;IACjE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,+EAA+E;IAC/E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8FAA8F;IAC9F,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iFAAiF;IACjF,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,+EAA+E;IAC/E,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAiGD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,kBAAkB,CAgFjH"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { matchParams } from './matchers.js';
|
|
2
|
+
function sameNameSet(expected, observed, ordered, allowAdditional = false) {
|
|
3
|
+
if (allowAdditional) {
|
|
4
|
+
// Containment, not equality: every expected name appeared somewhere.
|
|
5
|
+
const seen = [...observed];
|
|
6
|
+
return expected.every((name) => {
|
|
7
|
+
const at = seen.indexOf(name);
|
|
8
|
+
if (at < 0) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
seen.splice(at, 1); // consume, so a duplicate expectation needs a duplicate call
|
|
12
|
+
return true;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (expected.length !== observed.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (ordered) {
|
|
19
|
+
return expected.every((name, i) => name === observed[i]);
|
|
20
|
+
}
|
|
21
|
+
const a = [...expected].sort();
|
|
22
|
+
const b = [...observed].sort();
|
|
23
|
+
return a.every((name, i) => name === b[i]);
|
|
24
|
+
}
|
|
25
|
+
function scoreActions(expectation, observed) {
|
|
26
|
+
const expectedActions = expectation.actions ?? [];
|
|
27
|
+
if (expectedActions.length === 0) {
|
|
28
|
+
return { accuracy: null, paramResults: [], messages: [] };
|
|
29
|
+
}
|
|
30
|
+
const messages = [];
|
|
31
|
+
const observedNames = observed.actions.map((a) => a.name);
|
|
32
|
+
const namesMatch = sameNameSet(expectedActions.map((a) => a.name), observedNames, expectation.ordered === true, expectation.allowAdditionalActions === true);
|
|
33
|
+
if (!namesMatch) {
|
|
34
|
+
messages.push(`expected action(s) [${expectedActions.map((a) => a.name).join(', ')}], got [${observedNames.join(', ')}]`);
|
|
35
|
+
}
|
|
36
|
+
const paramResults = [];
|
|
37
|
+
for (const expected of expectedActions) {
|
|
38
|
+
if (!expected.params || expected.params.length === 0) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Score against the first call of that name. A duplicate call is already a name-level
|
|
42
|
+
// failure; charging it again here would double-penalize one mistake across two metrics.
|
|
43
|
+
const match = observed.actions.find((a) => a.name === expected.name);
|
|
44
|
+
if (!match) {
|
|
45
|
+
paramResults.push(...expected.params.map((p) => ({
|
|
46
|
+
param: `${expected.name}.${p.param}`,
|
|
47
|
+
matcherKind: p.matcher.kind,
|
|
48
|
+
passed: false,
|
|
49
|
+
detail: `action '${expected.name}' was not invoked`
|
|
50
|
+
})));
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
paramResults.push(...matchParams(expected.params, match.params).map((r) => ({ ...r, param: `${expected.name}.${r.param}` })));
|
|
54
|
+
}
|
|
55
|
+
for (const failure of paramResults.filter((r) => !r.passed)) {
|
|
56
|
+
messages.push(`param ${failure.param}: ${failure.detail}`);
|
|
57
|
+
}
|
|
58
|
+
return { accuracy: namesMatch ? 1 : 0, paramResults, messages };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Scores one observation against one expectation.
|
|
62
|
+
*
|
|
63
|
+
* **On `score`:** it is the mean of the components that *apply* to this case, not a fixed weighting.
|
|
64
|
+
* A case expecting `chat` has no action or param component, and counting those absent components as
|
|
65
|
+
* either 1 (inflating) or 0 (deflating) would make chat cases and action cases incomparable — which
|
|
66
|
+
* is precisely what a corpus-wide average needs them to be. Weighting across metrics is the
|
|
67
|
+
* scorecard's job at aggregation time, where it can be stated explicitly; §6.1 treats them as
|
|
68
|
+
* separate metrics for the same reason.
|
|
69
|
+
*/
|
|
70
|
+
function getPath(obj, path) {
|
|
71
|
+
return path.split('.').reduce((acc, key) => (acc !== null && typeof acc === 'object' ? acc[key] : undefined), obj);
|
|
72
|
+
}
|
|
73
|
+
/** Payload matchers and forbidden paths, over the observed `payloadChange`. */
|
|
74
|
+
function scorePayload(expectation, observed) {
|
|
75
|
+
const matchers = expectation.payload ?? [];
|
|
76
|
+
const messages = [];
|
|
77
|
+
const flat = Object.fromEntries(matchers.map((m) => [m.param, getPath(observed.payloadChange, m.param)]));
|
|
78
|
+
const paramResults = matchers.length === 0 ? [] : matchParams(matchers, flat).map((r) => ({ ...r, param: `payload ${r.param}` }));
|
|
79
|
+
for (const failure of paramResults.filter((r) => !r.passed)) {
|
|
80
|
+
messages.push(`${failure.param}: ${failure.detail}`);
|
|
81
|
+
}
|
|
82
|
+
const forbidden = (expectation.forbiddenPayloadPaths ?? []).filter((path) => getPath(observed.payloadChange, path) !== undefined);
|
|
83
|
+
if (forbidden.length > 0) {
|
|
84
|
+
messages.push(`wrote forbidden payload path(s): [${forbidden.join(', ')}]`);
|
|
85
|
+
}
|
|
86
|
+
return { paramResults, messages, forbidden };
|
|
87
|
+
}
|
|
88
|
+
export function evaluateDecision(expectation, observed) {
|
|
89
|
+
const messages = [];
|
|
90
|
+
// A payloadChange expectation is met by a mixed turn too (action + payload write, spec §2.1).
|
|
91
|
+
const decisionKindMatch = observed.kind === expectation.kind
|
|
92
|
+
|| (expectation.kind === 'payloadChange' && observed.payloadChange !== undefined);
|
|
93
|
+
if (!decisionKindMatch) {
|
|
94
|
+
const why = observed.diagnostic ? ` (${observed.diagnostic})` : '';
|
|
95
|
+
messages.push(`expected decision kind '${expectation.kind}', got '${observed.kind}'${why}`);
|
|
96
|
+
}
|
|
97
|
+
const actionScore = scoreActions(expectation, observed);
|
|
98
|
+
const payloadScore = scorePayload(expectation, observed);
|
|
99
|
+
const accuracy = actionScore.accuracy;
|
|
100
|
+
const paramResults = [...actionScore.paramResults, ...payloadScore.paramResults];
|
|
101
|
+
messages.push(...actionScore.messages, ...payloadScore.messages);
|
|
102
|
+
const expectedSubAgents = expectation.subAgents ?? [];
|
|
103
|
+
let subAgentsMatch = true;
|
|
104
|
+
if (expectedSubAgents.length > 0) {
|
|
105
|
+
subAgentsMatch = sameNameSet(expectedSubAgents, observed.subAgents.map((s) => s.name), expectation.ordered === true);
|
|
106
|
+
if (!subAgentsMatch) {
|
|
107
|
+
messages.push(`expected sub-agent(s) [${expectedSubAgents.join(', ')}], got [${observed.subAgents.map((s) => s.name).join(', ')}]`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Forbidden names are checked against BOTH channels: the point is that the agent did not reach
|
|
111
|
+
// for the capability, and it is equally wrong whether it did so natively or through the envelope.
|
|
112
|
+
const invoked = new Set([...observed.actions.map((a) => a.name), ...observed.subAgents.map((s) => s.name)]);
|
|
113
|
+
const forbiddenViolations = [
|
|
114
|
+
...(expectation.forbiddenActions ?? []).filter((name) => invoked.has(name)),
|
|
115
|
+
...payloadScore.forbidden.map((path) => `payload:${path}`)
|
|
116
|
+
];
|
|
117
|
+
if (forbiddenViolations.length > 0) {
|
|
118
|
+
messages.push(`invoked forbidden: [${forbiddenViolations.join(', ')}]`);
|
|
119
|
+
}
|
|
120
|
+
let messageMatch = true;
|
|
121
|
+
if (expectation.message) {
|
|
122
|
+
const result = matchParams([{ param: 'message', matcher: expectation.message }], { message: observed.message });
|
|
123
|
+
messageMatch = result[0].passed;
|
|
124
|
+
if (!messageMatch) {
|
|
125
|
+
messages.push(`message: ${result[0].detail}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const paramFidelity = paramResults.length === 0 ? null : paramResults.filter((r) => r.passed).length / paramResults.length;
|
|
129
|
+
const applicable = [decisionKindMatch ? 1 : 0];
|
|
130
|
+
if (accuracy !== null) {
|
|
131
|
+
applicable.push(accuracy);
|
|
132
|
+
}
|
|
133
|
+
if (paramFidelity !== null) {
|
|
134
|
+
applicable.push(paramFidelity);
|
|
135
|
+
}
|
|
136
|
+
if (expectedSubAgents.length > 0) {
|
|
137
|
+
applicable.push(subAgentsMatch ? 1 : 0);
|
|
138
|
+
}
|
|
139
|
+
if (expectation.message) {
|
|
140
|
+
applicable.push(messageMatch ? 1 : 0);
|
|
141
|
+
}
|
|
142
|
+
applicable.push(forbiddenViolations.length === 0 ? 1 : 0);
|
|
143
|
+
const passed = decisionKindMatch
|
|
144
|
+
&& (accuracy === null || accuracy === 1)
|
|
145
|
+
&& (paramFidelity === null || paramFidelity === 1)
|
|
146
|
+
&& subAgentsMatch
|
|
147
|
+
&& messageMatch
|
|
148
|
+
&& forbiddenViolations.length === 0;
|
|
149
|
+
return {
|
|
150
|
+
decisionKindMatch,
|
|
151
|
+
actionAccuracy: accuracy,
|
|
152
|
+
paramFidelity,
|
|
153
|
+
paramResults,
|
|
154
|
+
forbiddenViolations,
|
|
155
|
+
passed,
|
|
156
|
+
score: applicable.reduce((a, b) => a + b, 0) / applicable.length,
|
|
157
|
+
messages
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=expectation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expectation.js","sourceRoot":"","sources":["../../src/eval/expectation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* history.ts — one multi-turn case, two history encodings.
|
|
3
|
+
*
|
|
4
|
+
* A multi-turn case is written ONCE, in the neutral tool form (an assistant turn carrying
|
|
5
|
+
* `toolCalls`, then a `tool` turn of `tool_result` blocks). An arm that returns results natively
|
|
6
|
+
* sends it as is. Every other arm sees what the loop shows those models today — the assistant's
|
|
7
|
+
* call turn dropped (the loop never appends the model's own turn on the envelope path) and each
|
|
8
|
+
* result as the corpus's `[Action Result] <tool> succeeded|failed. <content>` user message.
|
|
9
|
+
*/
|
|
10
|
+
import { type ChatMessage } from '@memberjunction/ai';
|
|
11
|
+
/**
|
|
12
|
+
* The text rendering is Core's `EncodeToolTurnsAsText` — the same encoding the prompt runner's
|
|
13
|
+
* native→envelope fallback applies, so the harness's envelope arms and the runner's fallback can
|
|
14
|
+
* never drift apart.
|
|
15
|
+
*/
|
|
16
|
+
export declare function encodeHistoryForArm(messages: ChatMessage[], nativeResults: boolean): ChatMessage[];
|
|
17
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/eval/history.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE7E;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,OAAO,GAAG,WAAW,EAAE,CAElG"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* history.ts — one multi-turn case, two history encodings.
|
|
3
|
+
*
|
|
4
|
+
* A multi-turn case is written ONCE, in the neutral tool form (an assistant turn carrying
|
|
5
|
+
* `toolCalls`, then a `tool` turn of `tool_result` blocks). An arm that returns results natively
|
|
6
|
+
* sends it as is. Every other arm sees what the loop shows those models today — the assistant's
|
|
7
|
+
* call turn dropped (the loop never appends the model's own turn on the envelope path) and each
|
|
8
|
+
* result as the corpus's `[Action Result] <tool> succeeded|failed. <content>` user message.
|
|
9
|
+
*/
|
|
10
|
+
import { EncodeToolTurnsAsText } from '@memberjunction/ai';
|
|
11
|
+
/**
|
|
12
|
+
* The text rendering is Core's `EncodeToolTurnsAsText` — the same encoding the prompt runner's
|
|
13
|
+
* native→envelope fallback applies, so the harness's envelope arms and the runner's fallback can
|
|
14
|
+
* never drift apart.
|
|
15
|
+
*/
|
|
16
|
+
export function encodeHistoryForArm(messages, nativeResults) {
|
|
17
|
+
return nativeResults ? messages : EncodeToolTurnsAsText(messages);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/eval/history.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,qBAAqB,EAAoB,MAAM,oBAAoB,CAAC;AAE7E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAuB,EAAE,aAAsB;IAC/E,OAAO,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework-free evaluation core (test plan §2.1).
|
|
3
|
+
*
|
|
4
|
+
* Everything exported here is a pure function over plain data. Nothing imports the testing engine,
|
|
5
|
+
* so the same logic runs behind `IOracle` wrappers today and would run unchanged behind a bespoke
|
|
6
|
+
* runner if the framework turns out to be a poor fit — which is the documented fallback the rule
|
|
7
|
+
* exists to keep cheap.
|
|
8
|
+
*/
|
|
9
|
+
export * from './decision.js';
|
|
10
|
+
export * from './matchers.js';
|
|
11
|
+
export * from './expectation.js';
|
|
12
|
+
export * from './wellFormed.js';
|
|
13
|
+
export * from './corpus.js';
|
|
14
|
+
export * from './history.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eval/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework-free evaluation core (test plan §2.1).
|
|
3
|
+
*
|
|
4
|
+
* Everything exported here is a pure function over plain data. Nothing imports the testing engine,
|
|
5
|
+
* so the same logic runs behind `IOracle` wrappers today and would run unchanged behind a bespoke
|
|
6
|
+
* runner if the framework turns out to be a poor fit — which is the documented fallback the rule
|
|
7
|
+
* exists to keep cheap.
|
|
8
|
+
*/
|
|
9
|
+
export * from './decision.js';
|
|
10
|
+
export * from './matchers.js';
|
|
11
|
+
export * from './expectation.js';
|
|
12
|
+
export * from './wellFormed.js';
|
|
13
|
+
export * from './corpus.js';
|
|
14
|
+
export * from './history.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eval/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
|