@rulvar/evals 1.209.0 → 1.211.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/dist/index.js +257 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2804,6 +2804,263 @@ const SCENARIOS = [
|
|
|
2804
2804
|
]
|
|
2805
2805
|
};
|
|
2806
2806
|
}
|
|
2807
|
+
},
|
|
2808
|
+
{
|
|
2809
|
+
name: "parity-quiescence-deadlock",
|
|
2810
|
+
doctrine: "the parity deadlock shape ends in an exhausted terminal, never a silent exit: drained children die typed 'exposure-drained' at zero provider attempts (RV2001/RV2002), the root forced-finishes partial (RV1902), and run_settle seals a one-denominator journal (RV2003); the parity rerun exited mid-run with none of these",
|
|
2811
|
+
async run() {
|
|
2812
|
+
const agentTypeOfReq = (req) => req.providerOptions?.rulvar?.agentType ?? "";
|
|
2813
|
+
let orchTurn = 0;
|
|
2814
|
+
const calls = [];
|
|
2815
|
+
const toolEvents = (name, args, id) => [
|
|
2816
|
+
{
|
|
2817
|
+
type: "tool-call-start",
|
|
2818
|
+
id,
|
|
2819
|
+
name
|
|
2820
|
+
},
|
|
2821
|
+
{
|
|
2822
|
+
type: "tool-call-delta",
|
|
2823
|
+
id,
|
|
2824
|
+
argsTextDelta: JSON.stringify(args)
|
|
2825
|
+
},
|
|
2826
|
+
{
|
|
2827
|
+
type: "tool-call-end",
|
|
2828
|
+
id,
|
|
2829
|
+
args
|
|
2830
|
+
}
|
|
2831
|
+
];
|
|
2832
|
+
const adapter = {
|
|
2833
|
+
id: "fake",
|
|
2834
|
+
calls,
|
|
2835
|
+
caps: () => ({
|
|
2836
|
+
contextWindow: 2e5,
|
|
2837
|
+
maxOutputTokens: 16e3,
|
|
2838
|
+
structuredOutput: "native",
|
|
2839
|
+
supportsTemperature: true,
|
|
2840
|
+
supportsParallelTools: true,
|
|
2841
|
+
reasoningEfforts: [],
|
|
2842
|
+
pricing: {
|
|
2843
|
+
inputUsdPerMTok: 1,
|
|
2844
|
+
outputUsdPerMTok: 10
|
|
2845
|
+
}
|
|
2846
|
+
}),
|
|
2847
|
+
async *stream(req) {
|
|
2848
|
+
const call = calls.length;
|
|
2849
|
+
calls.push(req);
|
|
2850
|
+
await Promise.resolve();
|
|
2851
|
+
if (agentTypeOfReq(req) !== "") {
|
|
2852
|
+
yield {
|
|
2853
|
+
type: "text-delta",
|
|
2854
|
+
text: "unreachable worker"
|
|
2855
|
+
};
|
|
2856
|
+
yield {
|
|
2857
|
+
type: "finish",
|
|
2858
|
+
finish: { reason: "stop" },
|
|
2859
|
+
usage: {
|
|
2860
|
+
inputTokens: 10,
|
|
2861
|
+
outputTokens: 5,
|
|
2862
|
+
cacheReadTokens: 0,
|
|
2863
|
+
cacheWriteTokens: 0
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
orchTurn += 1;
|
|
2869
|
+
const turn = orchTurn === 1 ? toolEvents("spawn_agent", {
|
|
2870
|
+
agentType: "worker",
|
|
2871
|
+
prompt: "research A"
|
|
2872
|
+
}, `id-${String(call)}-0`).concat(toolEvents("spawn_agent", {
|
|
2873
|
+
agentType: "worker",
|
|
2874
|
+
prompt: "research B"
|
|
2875
|
+
}, `id-${String(call)}-1`), toolEvents("spawn_agent", {
|
|
2876
|
+
agentType: "worker",
|
|
2877
|
+
prompt: "research C"
|
|
2878
|
+
}, `id-${String(call)}-2`)) : toolEvents("finish", { result: "unreachable" }, `id-${String(call)}-0`);
|
|
2879
|
+
for (const event of turn) yield event;
|
|
2880
|
+
yield {
|
|
2881
|
+
type: "finish",
|
|
2882
|
+
finish: { reason: "tool-calls" },
|
|
2883
|
+
usage: {
|
|
2884
|
+
inputTokens: 10,
|
|
2885
|
+
outputTokens: orchTurn === 1 ? 2e3 : 5,
|
|
2886
|
+
cacheReadTokens: 0,
|
|
2887
|
+
cacheWriteTokens: 0
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
};
|
|
2892
|
+
const store = new InMemoryStore();
|
|
2893
|
+
const engine = createEngine({
|
|
2894
|
+
adapters: [adapter],
|
|
2895
|
+
stores: { journal: store },
|
|
2896
|
+
defaults: {
|
|
2897
|
+
routing: {
|
|
2898
|
+
loop: "fake:model",
|
|
2899
|
+
orchestrate: "fake:model"
|
|
2900
|
+
},
|
|
2901
|
+
profiles: { worker: {
|
|
2902
|
+
description: "the oversized worker",
|
|
2903
|
+
limits: { maxOutputTokensPerTurn: 1e4 }
|
|
2904
|
+
} }
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
const wf = makeOrchestratorWorkflow("the parity shape", { limits: { maxOutputTokensPerTurn: 2500 } });
|
|
2908
|
+
const waits = [];
|
|
2909
|
+
const handle = engine.run(wf, void 0, {
|
|
2910
|
+
runId: "fault-parity-quiescence",
|
|
2911
|
+
budgetUsd: 10,
|
|
2912
|
+
maxInFlightExposureUsd: .04
|
|
2913
|
+
});
|
|
2914
|
+
handle.on("budget:exposure-wait", (event) => waits.push(event));
|
|
2915
|
+
const outcome = await handle.result;
|
|
2916
|
+
const entries = await store.load("fault-parity-quiescence");
|
|
2917
|
+
const drained = entries.filter((entry) => entry.kind === "agent" && entry.status === "error" && (entry.error?.data)?.reason === "exposure-drained");
|
|
2918
|
+
const workerCalls = calls.filter((req) => agentTypeOfReq(req) !== "");
|
|
2919
|
+
const settle = [...entries].reverse().find((entry) => entry.kind === "decision" && entry.value?.decisionType === "run_settle");
|
|
2920
|
+
const agentEntries = entries.filter((entry) => entry.kind === "agent");
|
|
2921
|
+
const rosterClosed = agentEntries.filter((entry) => entry.status === "running").map((entry) => entry.seq).every((seq) => agentEntries.some((entry) => entry.ref === seq && entry.status !== "running"));
|
|
2922
|
+
const invoice = invoiceFromJournal(entries, (servedBy, usage) => usage.inputTokens / 1e6 * 1 + usage.outputTokens / 1e6 * 10);
|
|
2923
|
+
const envelope = outcome.value;
|
|
2924
|
+
return {
|
|
2925
|
+
observation: {
|
|
2926
|
+
matched: outcome.status === "exhausted" && envelope?.forcedFinishFallback === true && envelope.completion === "partial" && drained.length === 3 && workerCalls.length === 0 && waits.some((event) => event.scope === "child" && event.willWait === false) && settle !== void 0 && rosterClosed && agentEntries.every((entry) => entry.seq < settle.seq) && typeof outcome.envelope.wireRequests === "number" && outcome.envelope.wireRequests === invoice.cardinality.wireRequests && invoice.unsettled === void 0,
|
|
2927
|
+
detail: `run '${outcome.status}' (forcedFinishFallback=${String(envelope?.forcedFinishFallback)}, completion=${String(envelope?.completion)}); ${String(drained.length)} drained child terminal(s) at ${String(workerCalls.length)} worker call(s); roster closed=${String(rosterClosed)} before settle seq ${String(settle?.seq)}; wires ${String(outcome.envelope.wireRequests)} == invoice ${String(invoice.cardinality.wireRequests)}, unsettled lane absent=${String(invoice.unsettled === void 0)}`
|
|
2928
|
+
},
|
|
2929
|
+
artifacts: [
|
|
2930
|
+
jsonArtifact("outcome.json", {
|
|
2931
|
+
status: outcome.status,
|
|
2932
|
+
value: outcome.value ?? null,
|
|
2933
|
+
envelope: outcome.envelope
|
|
2934
|
+
}),
|
|
2935
|
+
jsonArtifact("events.json", { waits }),
|
|
2936
|
+
jsonArtifact("journal.json", entries)
|
|
2937
|
+
]
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
},
|
|
2941
|
+
{
|
|
2942
|
+
name: "parity-sequential-roster-floor",
|
|
2943
|
+
doctrine: "a seat-by-seat roster under an unreachable acceptance floor refuses its FIRST seat typed 'roster_floor' with the whole-roster arithmetic journaled and zero paid children (RV2005): the parity arm paid three seats the settle verdict was bound to reject",
|
|
2944
|
+
async run() {
|
|
2945
|
+
const agentTypeOfReq = (req) => req.providerOptions?.rulvar?.agentType ?? "";
|
|
2946
|
+
let orchTurn = 0;
|
|
2947
|
+
const calls = [];
|
|
2948
|
+
const toolEvents = (name, args, id) => [
|
|
2949
|
+
{
|
|
2950
|
+
type: "tool-call-start",
|
|
2951
|
+
id,
|
|
2952
|
+
name
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
type: "tool-call-delta",
|
|
2956
|
+
id,
|
|
2957
|
+
argsTextDelta: JSON.stringify(args)
|
|
2958
|
+
},
|
|
2959
|
+
{
|
|
2960
|
+
type: "tool-call-end",
|
|
2961
|
+
id,
|
|
2962
|
+
args
|
|
2963
|
+
}
|
|
2964
|
+
];
|
|
2965
|
+
const adapter = {
|
|
2966
|
+
id: "fake",
|
|
2967
|
+
calls,
|
|
2968
|
+
caps: () => ({
|
|
2969
|
+
contextWindow: 2e5,
|
|
2970
|
+
maxOutputTokens: 16e3,
|
|
2971
|
+
structuredOutput: "native",
|
|
2972
|
+
supportsTemperature: true,
|
|
2973
|
+
supportsParallelTools: true,
|
|
2974
|
+
reasoningEfforts: [],
|
|
2975
|
+
pricing: {
|
|
2976
|
+
inputUsdPerMTok: 1,
|
|
2977
|
+
outputUsdPerMTok: 10
|
|
2978
|
+
}
|
|
2979
|
+
}),
|
|
2980
|
+
async *stream(req) {
|
|
2981
|
+
const call = calls.length;
|
|
2982
|
+
calls.push(req);
|
|
2983
|
+
await Promise.resolve();
|
|
2984
|
+
if (agentTypeOfReq(req) !== "") {
|
|
2985
|
+
yield {
|
|
2986
|
+
type: "text-delta",
|
|
2987
|
+
text: "seated worker"
|
|
2988
|
+
};
|
|
2989
|
+
yield {
|
|
2990
|
+
type: "finish",
|
|
2991
|
+
finish: { reason: "stop" },
|
|
2992
|
+
usage: {
|
|
2993
|
+
inputTokens: 10,
|
|
2994
|
+
outputTokens: 5,
|
|
2995
|
+
cacheReadTokens: 0,
|
|
2996
|
+
cacheWriteTokens: 0
|
|
2997
|
+
}
|
|
2998
|
+
};
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
orchTurn += 1;
|
|
3002
|
+
const turn = orchTurn === 1 ? toolEvents("spawn_agent", {
|
|
3003
|
+
agentType: "worker",
|
|
3004
|
+
prompt: "seat 1"
|
|
3005
|
+
}, `id-${String(call)}-0`) : toolEvents("finish", { result: "stopped early" }, `id-${String(call)}-0`);
|
|
3006
|
+
for (const event of turn) yield event;
|
|
3007
|
+
yield {
|
|
3008
|
+
type: "finish",
|
|
3009
|
+
finish: { reason: "tool-calls" },
|
|
3010
|
+
usage: {
|
|
3011
|
+
inputTokens: 10,
|
|
3012
|
+
outputTokens: 5,
|
|
3013
|
+
cacheReadTokens: 0,
|
|
3014
|
+
cacheWriteTokens: 0
|
|
3015
|
+
}
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
};
|
|
3019
|
+
const store = new InMemoryStore();
|
|
3020
|
+
const engine = createEngine({
|
|
3021
|
+
adapters: [adapter],
|
|
3022
|
+
stores: { journal: store },
|
|
3023
|
+
defaults: {
|
|
3024
|
+
routing: {
|
|
3025
|
+
loop: "fake:model",
|
|
3026
|
+
orchestrate: "fake:model"
|
|
3027
|
+
},
|
|
3028
|
+
profiles: { worker: {
|
|
3029
|
+
description: "the estimated worker",
|
|
3030
|
+
estCost: .7
|
|
3031
|
+
} }
|
|
3032
|
+
}
|
|
3033
|
+
});
|
|
3034
|
+
const wf = makeOrchestratorWorkflow("the seat-by-seat parity roster", { acceptance: {
|
|
3035
|
+
childPolicy: "all-ok",
|
|
3036
|
+
minSpawnedChildren: 4
|
|
3037
|
+
} });
|
|
3038
|
+
const rejects = [];
|
|
3039
|
+
const handle = engine.run(wf, void 0, {
|
|
3040
|
+
runId: "fault-parity-roster",
|
|
3041
|
+
budgetUsd: 2.5
|
|
3042
|
+
});
|
|
3043
|
+
handle.on("spawn:rejected", (event) => rejects.push(event));
|
|
3044
|
+
const outcome = await handle.result;
|
|
3045
|
+
const entries = await store.load("fault-parity-roster");
|
|
3046
|
+
const decision = entries.find((entry) => entry.kind === "decision" && entry.value?.decisionType === "spawn-admission" && entry.value.decision?.verdict?.reason?.code === "roster_floor");
|
|
3047
|
+
const reason = (decision?.value)?.decision.verdict.reason;
|
|
3048
|
+
const workerCalls = calls.filter((req) => agentTypeOfReq(req) !== "");
|
|
3049
|
+
return {
|
|
3050
|
+
observation: {
|
|
3051
|
+
matched: workerCalls.length === 0 && rejects.some((event) => event.code === "roster_floor") && decision !== void 0 && reason?.floor === 4 && reason.seatsRemaining === 4 && Math.abs((reason.perSeatProjectionUsd ?? 0) - .7) < 1e-9 && outcome.status === "error",
|
|
3052
|
+
detail: `run '${outcome.status}' with ${String(workerCalls.length)} worker call(s); roster_floor rejected=${String(rejects.some((e) => e.code === "roster_floor"))}, journaled arithmetic floor=${String(reason?.floor)} seatsRemaining=${String(reason?.seatsRemaining)} perSeat=${String(reason?.perSeatProjectionUsd)}`
|
|
3053
|
+
},
|
|
3054
|
+
artifacts: [
|
|
3055
|
+
jsonArtifact("outcome.json", {
|
|
3056
|
+
status: outcome.status,
|
|
3057
|
+
error: outcome.error ?? null
|
|
3058
|
+
}),
|
|
3059
|
+
jsonArtifact("events.json", { rejects }),
|
|
3060
|
+
jsonArtifact("journal.json", entries)
|
|
3061
|
+
]
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
2807
3064
|
}
|
|
2808
3065
|
];
|
|
2809
3066
|
/** The scenario names in run order. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/evals",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.211.0",
|
|
4
4
|
"description": "Rulvar evals: eval cases, golden outputs, rubric and judge graders, matrix sweeps, canary fingerprint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/anthropic": "1.
|
|
26
|
-
"@rulvar/core": "1.
|
|
27
|
-
"@rulvar/
|
|
28
|
-
"@rulvar/openai": "1.
|
|
29
|
-
"@rulvar/
|
|
25
|
+
"@rulvar/anthropic": "1.211.0",
|
|
26
|
+
"@rulvar/core": "1.211.0",
|
|
27
|
+
"@rulvar/plan": "1.211.0",
|
|
28
|
+
"@rulvar/openai": "1.211.0",
|
|
29
|
+
"@rulvar/testing": "1.211.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.20.1",
|