@kortyx/agent 0.34.0 → 0.35.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/dist/evals.d.ts +22 -1
- package/dist/evals.js +20 -2
- package/dist/evals.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +360 -265
- package/dist/index.js.map +1 -1
- package/dist/{studio-judge-DpRLvf8j.d.ts → studio-judge-0yb-Cf2q.d.ts} +91 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -6938,6 +6938,7 @@ var emitTelemetryEvent = (args) => {
|
|
|
6938
6938
|
|
|
6939
6939
|
// src/telemetry/topology.ts
|
|
6940
6940
|
var import_node_crypto2 = require("crypto");
|
|
6941
|
+
var import_eval_attribution = require("@kortyx/core/eval-attribution");
|
|
6941
6942
|
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6942
6943
|
var stableValue = (value) => {
|
|
6943
6944
|
if (Array.isArray(value)) return value.map(stableValue);
|
|
@@ -7090,6 +7091,8 @@ var projectWorkflowTopology = (args) => {
|
|
|
7090
7091
|
};
|
|
7091
7092
|
};
|
|
7092
7093
|
var prepareWorkflowTelemetry = (args) => {
|
|
7094
|
+
if (args.sessionId)
|
|
7095
|
+
(0, import_eval_attribution.associateEvalExecution)({ sessionId: args.sessionId, runId: args.runId });
|
|
7093
7096
|
const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
|
|
7094
7097
|
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
|
|
7095
7098
|
return args.config;
|
|
@@ -10059,9 +10062,12 @@ var EvalStepResultSchema = import_zod7.z.object({
|
|
|
10059
10062
|
EvalVerdictSchema.extend({ id: import_zod7.z.string(), text: import_zod7.z.string() })
|
|
10060
10063
|
)
|
|
10061
10064
|
}).strict();
|
|
10065
|
+
var runtimeExecution = import_zod7.z.object({ sessionId: import_zod7.z.string().min(1), runId: import_zod7.z.string().min(1) }).strict();
|
|
10062
10066
|
var EvalCaseResultSchema = import_zod7.z.object({
|
|
10063
10067
|
caseId: id2,
|
|
10064
10068
|
repetition: import_zod7.z.number().int().positive(),
|
|
10069
|
+
attemptId: import_zod7.z.uuid().optional(),
|
|
10070
|
+
runtimeExecutions: import_zod7.z.array(runtimeExecution).optional(),
|
|
10065
10071
|
sessionId: import_zod7.z.string(),
|
|
10066
10072
|
status,
|
|
10067
10073
|
durationMs: import_zod7.z.number().nonnegative(),
|
|
@@ -10110,6 +10116,7 @@ var EvalRunResultSchema = import_zod7.z.object({
|
|
|
10110
10116
|
var EvalManifestSchema = import_zod7.z.object({
|
|
10111
10117
|
schemaVersion: import_zod7.z.literal(1),
|
|
10112
10118
|
studioJudging: import_zod7.z.literal(true).optional(),
|
|
10119
|
+
attemptScheduling: import_zod7.z.literal(true).optional(),
|
|
10113
10120
|
suites: import_zod7.z.array(EvalSuiteSchema),
|
|
10114
10121
|
responders: import_zod7.z.array(
|
|
10115
10122
|
import_zod7.z.object({
|
|
@@ -10123,6 +10130,13 @@ var EvalManifestSchema = import_zod7.z.object({
|
|
|
10123
10130
|
judge: judge.optional()
|
|
10124
10131
|
}).strict();
|
|
10125
10132
|
var EvalProgressSchema = import_zod7.z.discriminatedUnion("type", [
|
|
10133
|
+
import_zod7.z.object({
|
|
10134
|
+
type: import_zod7.z.literal("case-runtime-associated"),
|
|
10135
|
+
caseId: id2,
|
|
10136
|
+
repetition: import_zod7.z.number().int().positive(),
|
|
10137
|
+
attemptId: import_zod7.z.uuid(),
|
|
10138
|
+
execution: runtimeExecution
|
|
10139
|
+
}).strict(),
|
|
10126
10140
|
import_zod7.z.object({
|
|
10127
10141
|
type: import_zod7.z.literal("run-started"),
|
|
10128
10142
|
caseIds: import_zod7.z.array(id2).min(1),
|
|
@@ -10138,6 +10152,7 @@ var EvalProgressSchema = import_zod7.z.discriminatedUnion("type", [
|
|
|
10138
10152
|
}).strict(),
|
|
10139
10153
|
import_zod7.z.object({
|
|
10140
10154
|
type: import_zod7.z.literal("case-started"),
|
|
10155
|
+
attemptId: import_zod7.z.uuid().optional(),
|
|
10141
10156
|
caseId: id2,
|
|
10142
10157
|
repetition: import_zod7.z.number().int().positive(),
|
|
10143
10158
|
sessionId: import_zod7.z.string()
|
|
@@ -10157,11 +10172,17 @@ var EvalRemoteRunRequestSchema = import_zod7.z.object({
|
|
|
10157
10172
|
judge: EvalJudgeIdentitySchema.optional(),
|
|
10158
10173
|
caseIds: import_zod7.z.array(id2).min(1).max(100).optional(),
|
|
10159
10174
|
repetitions: import_zod7.z.number().int().min(1).max(20).default(1),
|
|
10160
|
-
concurrency: import_zod7.z.number().int().min(1).max(20).default(1)
|
|
10175
|
+
concurrency: import_zod7.z.number().int().min(1).max(20).default(1),
|
|
10176
|
+
attempt: import_zod7.z.object({
|
|
10177
|
+
caseId: id2,
|
|
10178
|
+
repetition: import_zod7.z.number().int().min(1).max(20),
|
|
10179
|
+
evaluationId: import_zod7.z.uuid()
|
|
10180
|
+
}).strict().optional()
|
|
10161
10181
|
}).strict();
|
|
10162
10182
|
var StudioEvalStartRequestSchema = EvalRemoteRunRequestSchema.omit({
|
|
10163
10183
|
grading: true,
|
|
10164
|
-
judge: true
|
|
10184
|
+
judge: true,
|
|
10185
|
+
attempt: true
|
|
10165
10186
|
}).extend({
|
|
10166
10187
|
targetId: import_zod7.z.string().min(1).max(128),
|
|
10167
10188
|
judge: import_zod7.z.enum(["studio", "app"]).default("studio")
|
|
@@ -10198,6 +10219,7 @@ function parseEvalSuite(value) {
|
|
|
10198
10219
|
|
|
10199
10220
|
// src/evals/create-evals.ts
|
|
10200
10221
|
var import_node_crypto5 = require("crypto");
|
|
10222
|
+
var import_eval_attribution2 = require("@kortyx/core/eval-attribution");
|
|
10201
10223
|
var import_zod8 = require("zod");
|
|
10202
10224
|
|
|
10203
10225
|
// src/evals/evidence.ts
|
|
@@ -10683,6 +10705,7 @@ function createEvals(options) {
|
|
|
10683
10705
|
return {
|
|
10684
10706
|
schemaVersion: 1,
|
|
10685
10707
|
studioJudging: true,
|
|
10708
|
+
attemptScheduling: true,
|
|
10686
10709
|
suites: clone(suites),
|
|
10687
10710
|
responders: Object.entries(responders).map(([name, value]) => ({
|
|
10688
10711
|
name,
|
|
@@ -10727,6 +10750,8 @@ function createEvals(options) {
|
|
|
10727
10750
|
);
|
|
10728
10751
|
if (!Number.isSafeInteger(selected.length * repetitions))
|
|
10729
10752
|
throw new EvalConfigurationError("Too many case repetitions.");
|
|
10753
|
+
if (args.attempt && (!selected.some((item) => item.id === args.attempt?.caseId) || !Number.isSafeInteger(args.attempt.repetition) || args.attempt.repetition < 1 || args.attempt.repetition > repetitions))
|
|
10754
|
+
throw new EvalConfigurationError("Invalid scheduled attempt.");
|
|
10730
10755
|
const studioGrading = args.grading === "studio";
|
|
10731
10756
|
if (!studioGrading && !options.judge && selected.some(
|
|
10732
10757
|
(item) => item.steps.some((step) => step.expect.criteria?.length)
|
|
@@ -10763,7 +10788,10 @@ function createEvals(options) {
|
|
|
10763
10788
|
const emit = async (event) => {
|
|
10764
10789
|
if (!args.onProgress || !reporting) return;
|
|
10765
10790
|
try {
|
|
10766
|
-
await
|
|
10791
|
+
await (0, import_eval_attribution2.withEvalAttribution)(
|
|
10792
|
+
void 0,
|
|
10793
|
+
() => args.onProgress(clone(event))
|
|
10794
|
+
);
|
|
10767
10795
|
} catch {
|
|
10768
10796
|
reporting = false;
|
|
10769
10797
|
result.errors.push(failure("reporting"));
|
|
@@ -10778,138 +10806,220 @@ function createEvals(options) {
|
|
|
10778
10806
|
controller.abort();
|
|
10779
10807
|
}, defaults.caseTimeoutMs);
|
|
10780
10808
|
const signal = args.signal ? AbortSignal.any([args.signal, controller.signal]) : controller.signal;
|
|
10809
|
+
const attemptId = (0, import_node_crypto5.randomUUID)();
|
|
10810
|
+
const runtimeExecutions = [];
|
|
10781
10811
|
const attemptResult = {
|
|
10782
10812
|
caseId: item.id,
|
|
10783
10813
|
repetition,
|
|
10814
|
+
attemptId,
|
|
10815
|
+
runtimeExecutions,
|
|
10784
10816
|
sessionId: `eval-${(0, import_node_crypto5.randomUUID)()}`,
|
|
10785
10817
|
status: "passed",
|
|
10786
10818
|
durationMs: 0,
|
|
10787
10819
|
steps: [],
|
|
10788
10820
|
errors: []
|
|
10789
10821
|
};
|
|
10790
|
-
let
|
|
10791
|
-
let
|
|
10792
|
-
const
|
|
10793
|
-
phase =
|
|
10794
|
-
|
|
10822
|
+
let associationProgress = Promise.resolve();
|
|
10823
|
+
let acceptingExecutions = true;
|
|
10824
|
+
const executeAttempt = async () => {
|
|
10825
|
+
let phase = "params";
|
|
10826
|
+
let stepIndex;
|
|
10827
|
+
const reportPhase = async (next) => {
|
|
10828
|
+
phase = next;
|
|
10829
|
+
if (args.includeActivity)
|
|
10830
|
+
await emit({
|
|
10831
|
+
type: "case-progress",
|
|
10832
|
+
caseId: item.id,
|
|
10833
|
+
repetition,
|
|
10834
|
+
phase,
|
|
10835
|
+
...stepIndex !== void 0 ? { stepIndex } : {}
|
|
10836
|
+
});
|
|
10837
|
+
};
|
|
10838
|
+
let context;
|
|
10839
|
+
let continuation;
|
|
10840
|
+
let activeStep;
|
|
10841
|
+
let activeStepReported = false;
|
|
10842
|
+
const history = [];
|
|
10843
|
+
const call = async (readyContext, command, activeSignal) => {
|
|
10844
|
+
const run = (overrides) => executeEvalChat({
|
|
10845
|
+
agent: options.agent,
|
|
10846
|
+
command,
|
|
10847
|
+
continuation,
|
|
10848
|
+
history: clone(history),
|
|
10849
|
+
sessionId: attemptResult.sessionId,
|
|
10850
|
+
clientTurnId: (0, import_node_crypto5.randomUUID)(),
|
|
10851
|
+
signal: activeSignal,
|
|
10852
|
+
...item.workflowId ? { workflowId: item.workflowId } : {},
|
|
10853
|
+
...overrides
|
|
10854
|
+
});
|
|
10855
|
+
const execution = options.execute ? await options.execute({
|
|
10856
|
+
...readyContext,
|
|
10857
|
+
signal: activeSignal,
|
|
10858
|
+
command,
|
|
10859
|
+
continuation,
|
|
10860
|
+
history: clone(history),
|
|
10861
|
+
run
|
|
10862
|
+
}) : await run();
|
|
10863
|
+
continuation = execution.continuation;
|
|
10864
|
+
const observation = EvalObservationSchema.parse(
|
|
10865
|
+
execution.observation
|
|
10866
|
+
);
|
|
10867
|
+
if (observation.type === "interrupt" && execution.continuation === void 0)
|
|
10868
|
+
throw new Error(
|
|
10869
|
+
"A waiting interrupt requires a private continuation."
|
|
10870
|
+
);
|
|
10871
|
+
return {
|
|
10872
|
+
observation,
|
|
10873
|
+
...continuation !== void 0 ? { continuation } : {}
|
|
10874
|
+
};
|
|
10875
|
+
};
|
|
10876
|
+
try {
|
|
10795
10877
|
await emit({
|
|
10796
|
-
type: "case-
|
|
10878
|
+
type: "case-started",
|
|
10879
|
+
attemptId,
|
|
10797
10880
|
caseId: item.id,
|
|
10798
10881
|
repetition,
|
|
10799
|
-
|
|
10800
|
-
...stepIndex !== void 0 ? { stepIndex } : {}
|
|
10882
|
+
sessionId: attemptResult.sessionId
|
|
10801
10883
|
});
|
|
10802
|
-
};
|
|
10803
|
-
let context;
|
|
10804
|
-
let continuation;
|
|
10805
|
-
let activeStep;
|
|
10806
|
-
let activeStepReported = false;
|
|
10807
|
-
const history = [];
|
|
10808
|
-
const call = async (readyContext, command, activeSignal) => {
|
|
10809
|
-
const run = (overrides) => executeEvalChat({
|
|
10810
|
-
agent: options.agent,
|
|
10811
|
-
command,
|
|
10812
|
-
continuation,
|
|
10813
|
-
history: clone(history),
|
|
10814
|
-
sessionId: attemptResult.sessionId,
|
|
10815
|
-
clientTurnId: (0, import_node_crypto5.randomUUID)(),
|
|
10816
|
-
signal: activeSignal,
|
|
10817
|
-
...item.workflowId ? { workflowId: item.workflowId } : {},
|
|
10818
|
-
...overrides
|
|
10819
|
-
});
|
|
10820
|
-
const execution = options.execute ? await options.execute({
|
|
10821
|
-
...readyContext,
|
|
10822
|
-
signal: activeSignal,
|
|
10823
|
-
command,
|
|
10824
|
-
continuation,
|
|
10825
|
-
history: clone(history),
|
|
10826
|
-
run
|
|
10827
|
-
}) : await run();
|
|
10828
|
-
continuation = execution.continuation;
|
|
10829
|
-
const observation = EvalObservationSchema.parse(
|
|
10830
|
-
execution.observation
|
|
10831
|
-
);
|
|
10832
|
-
if (observation.type === "interrupt" && execution.continuation === void 0)
|
|
10833
|
-
throw new Error(
|
|
10834
|
-
"A waiting interrupt requires a private continuation."
|
|
10835
|
-
);
|
|
10836
|
-
return {
|
|
10837
|
-
observation,
|
|
10838
|
-
...continuation !== void 0 ? { continuation } : {}
|
|
10839
|
-
};
|
|
10840
|
-
};
|
|
10841
|
-
try {
|
|
10842
|
-
await emit({
|
|
10843
|
-
type: "case-started",
|
|
10844
|
-
caseId: item.id,
|
|
10845
|
-
repetition,
|
|
10846
|
-
sessionId: attemptResult.sessionId
|
|
10847
|
-
});
|
|
10848
|
-
signal.throwIfAborted();
|
|
10849
|
-
await reportPhase("params");
|
|
10850
|
-
const params = options.paramsSchema ? await options.paramsSchema.parseAsync(item.params) : item.params;
|
|
10851
|
-
const base = {
|
|
10852
|
-
suiteId: suite.id,
|
|
10853
|
-
case: item,
|
|
10854
|
-
repetition,
|
|
10855
|
-
sessionId: attemptResult.sessionId,
|
|
10856
|
-
params,
|
|
10857
|
-
signal
|
|
10858
|
-
};
|
|
10859
|
-
await reportPhase("setup");
|
|
10860
|
-
const prepared = options.setup ? await options.setup(base) : void 0;
|
|
10861
|
-
context = { ...base, prepared };
|
|
10862
|
-
signal.throwIfAborted();
|
|
10863
|
-
for (const [index, step] of item.steps.entries()) {
|
|
10864
|
-
stepIndex = index;
|
|
10865
|
-
activeStep = void 0;
|
|
10866
|
-
activeStepReported = false;
|
|
10867
10884
|
signal.throwIfAborted();
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
if (typeof responder !== "function" && (responder.schemaId !== interrupt.schemaId || responder.schemaVersion && responder.schemaVersion !== interrupt.schemaVersion))
|
|
10878
|
-
throw new Error("Incompatible responder.");
|
|
10879
|
-
response = await (typeof responder === "function" ? responder : responder.respond)({
|
|
10880
|
-
...context,
|
|
10881
|
-
interrupt: clone(interrupt),
|
|
10882
|
-
...response.params !== void 0 ? { args: response.params } : {}
|
|
10883
|
-
});
|
|
10884
|
-
}
|
|
10885
|
-
command = {
|
|
10886
|
-
type: "resume",
|
|
10887
|
-
// z.json() is inferred as optional under the package's relaxed
|
|
10888
|
-
// declaration build. Runtime validation still requires value.
|
|
10889
|
-
response: EvalResumeResponseSchema.parse(
|
|
10890
|
-
response
|
|
10891
|
-
)
|
|
10892
|
-
};
|
|
10893
|
-
}
|
|
10894
|
-
signal.throwIfAborted();
|
|
10895
|
-
await reportPhase("execute");
|
|
10896
|
-
const { observation } = await call(context, command, signal);
|
|
10897
|
-
const input = command.type === "message" ? { message: command.message } : { resume: command.response };
|
|
10898
|
-
const evaluated = {
|
|
10899
|
-
index,
|
|
10900
|
-
input,
|
|
10901
|
-
expectation: clone(step.expect),
|
|
10902
|
-
observation,
|
|
10903
|
-
status: "passed",
|
|
10904
|
-
criteria: []
|
|
10885
|
+
await reportPhase("params");
|
|
10886
|
+
const params = options.paramsSchema ? await options.paramsSchema.parseAsync(item.params) : item.params;
|
|
10887
|
+
const base = {
|
|
10888
|
+
suiteId: suite.id,
|
|
10889
|
+
case: item,
|
|
10890
|
+
repetition,
|
|
10891
|
+
sessionId: attemptResult.sessionId,
|
|
10892
|
+
params,
|
|
10893
|
+
signal
|
|
10905
10894
|
};
|
|
10906
|
-
|
|
10907
|
-
|
|
10895
|
+
await reportPhase("setup");
|
|
10896
|
+
const prepared = options.setup ? await options.setup(base) : void 0;
|
|
10897
|
+
context = { ...base, prepared };
|
|
10908
10898
|
signal.throwIfAborted();
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10899
|
+
for (const [index, step] of item.steps.entries()) {
|
|
10900
|
+
stepIndex = index;
|
|
10901
|
+
activeStep = void 0;
|
|
10902
|
+
activeStepReported = false;
|
|
10903
|
+
signal.throwIfAborted();
|
|
10904
|
+
let command;
|
|
10905
|
+
if (step.message !== void 0)
|
|
10906
|
+
command = { type: "message", message: step.message };
|
|
10907
|
+
else {
|
|
10908
|
+
await reportPhase("responder");
|
|
10909
|
+
const interrupt = attemptResult.steps.at(-1)?.observation.interrupt;
|
|
10910
|
+
let response = step.resume;
|
|
10911
|
+
if (handlerRef(response)) {
|
|
10912
|
+
const responder = responders[response.using];
|
|
10913
|
+
if (typeof responder !== "function" && (responder.schemaId !== interrupt.schemaId || responder.schemaVersion && responder.schemaVersion !== interrupt.schemaVersion))
|
|
10914
|
+
throw new Error("Incompatible responder.");
|
|
10915
|
+
response = await (typeof responder === "function" ? responder : responder.respond)({
|
|
10916
|
+
...context,
|
|
10917
|
+
interrupt: clone(interrupt),
|
|
10918
|
+
...response.params !== void 0 ? { args: response.params } : {}
|
|
10919
|
+
});
|
|
10920
|
+
}
|
|
10921
|
+
command = {
|
|
10922
|
+
type: "resume",
|
|
10923
|
+
// z.json() is inferred as optional under the package's relaxed
|
|
10924
|
+
// declaration build. Runtime validation still requires value.
|
|
10925
|
+
response: EvalResumeResponseSchema.parse(
|
|
10926
|
+
response
|
|
10927
|
+
)
|
|
10928
|
+
};
|
|
10929
|
+
}
|
|
10930
|
+
signal.throwIfAborted();
|
|
10931
|
+
await reportPhase("execute");
|
|
10932
|
+
const { observation } = await call(context, command, signal);
|
|
10933
|
+
const input = command.type === "message" ? { message: command.message } : { resume: command.response };
|
|
10934
|
+
const evaluated = {
|
|
10935
|
+
index,
|
|
10936
|
+
input,
|
|
10937
|
+
expectation: clone(step.expect),
|
|
10938
|
+
observation,
|
|
10939
|
+
status: "passed",
|
|
10940
|
+
criteria: []
|
|
10941
|
+
};
|
|
10942
|
+
attemptResult.steps.push(evaluated);
|
|
10943
|
+
activeStep = evaluated;
|
|
10944
|
+
signal.throwIfAborted();
|
|
10945
|
+
if (observation.type === "error")
|
|
10946
|
+
throw new Error("Agent failed.");
|
|
10947
|
+
if (observation.type === "cancelled") {
|
|
10948
|
+
evaluated.status = "cancelled";
|
|
10949
|
+
attemptResult.status = "cancelled";
|
|
10950
|
+
await emit({
|
|
10951
|
+
type: "step-completed",
|
|
10952
|
+
caseId: item.id,
|
|
10953
|
+
repetition,
|
|
10954
|
+
step: evaluated
|
|
10955
|
+
});
|
|
10956
|
+
activeStepReported = true;
|
|
10957
|
+
break;
|
|
10958
|
+
}
|
|
10959
|
+
if (observation.type !== step.expect.type || step.expect.schemaId && step.expect.schemaId !== observation.interrupt?.schemaId || step.expect.schemaVersion && step.expect.schemaVersion !== observation.interrupt?.schemaVersion) {
|
|
10960
|
+
evaluated.status = "failed";
|
|
10961
|
+
evaluated.reason = "The observed interaction did not match the expected answer or interrupt contract.";
|
|
10962
|
+
} else {
|
|
10963
|
+
const outputReason = missingOutputReason(
|
|
10964
|
+
step.expect.outputs,
|
|
10965
|
+
observation
|
|
10966
|
+
);
|
|
10967
|
+
if (outputReason) {
|
|
10968
|
+
evaluated.status = "failed";
|
|
10969
|
+
evaluated.reason = outputReason;
|
|
10970
|
+
} else {
|
|
10971
|
+
const reference = step.expect.reference;
|
|
10972
|
+
if (reference !== void 0) {
|
|
10973
|
+
await reportPhase("reference");
|
|
10974
|
+
evaluated.reference = handlerRef(reference) ? import_zod8.z.json().parse(
|
|
10975
|
+
await references[reference.using]({
|
|
10976
|
+
...context,
|
|
10977
|
+
observation: clone(observation),
|
|
10978
|
+
...reference.params !== void 0 ? { args: reference.params } : {}
|
|
10979
|
+
})
|
|
10980
|
+
) : clone(reference);
|
|
10981
|
+
}
|
|
10982
|
+
await reportPhase("grading");
|
|
10983
|
+
evaluated.evidence = createEvalJudgeEvidence(
|
|
10984
|
+
observation,
|
|
10985
|
+
suite.evidence,
|
|
10986
|
+
evidenceFilters
|
|
10987
|
+
);
|
|
10988
|
+
if (studioGrading && step.expect.criteria?.length) {
|
|
10989
|
+
evaluated.status = "ungraded";
|
|
10990
|
+
attemptResult.status = "ungraded";
|
|
10991
|
+
}
|
|
10992
|
+
for (const [criterionIndex, value] of (studioGrading ? [] : step.expect.criteria ?? []).entries()) {
|
|
10993
|
+
signal.throwIfAborted();
|
|
10994
|
+
const criterion = typeof value === "string" ? { id: String(criterionIndex), text: value } : value;
|
|
10995
|
+
evaluated.judgeCalls = (evaluated.judgeCalls ?? 0) + 1;
|
|
10996
|
+
const verdict = EvalVerdictSchema.parse(
|
|
10997
|
+
await (0, import_eval_attribution2.withEvalAttribution)(
|
|
10998
|
+
void 0,
|
|
10999
|
+
() => options.judge.grade({
|
|
11000
|
+
criterion,
|
|
11001
|
+
onUsage: (usage) => {
|
|
11002
|
+
if (!signal.aborted) {
|
|
11003
|
+
evaluated.judgeUsage ??= [];
|
|
11004
|
+
evaluated.judgeUsage.push(clone(usage));
|
|
11005
|
+
}
|
|
11006
|
+
},
|
|
11007
|
+
input: clone(input),
|
|
11008
|
+
...getEvalGradeEvidence(
|
|
11009
|
+
evaluated,
|
|
11010
|
+
attemptResult.steps.slice(0, -1)
|
|
11011
|
+
),
|
|
11012
|
+
signal,
|
|
11013
|
+
...evaluated.reference !== void 0 ? { reference: clone(evaluated.reference) } : {}
|
|
11014
|
+
})
|
|
11015
|
+
)
|
|
11016
|
+
);
|
|
11017
|
+
signal.throwIfAborted();
|
|
11018
|
+
evaluated.criteria.push({ ...criterion, ...verdict });
|
|
11019
|
+
if (!verdict.passed) evaluated.status = "failed";
|
|
11020
|
+
}
|
|
11021
|
+
}
|
|
11022
|
+
}
|
|
10913
11023
|
await emit({
|
|
10914
11024
|
type: "step-completed",
|
|
10915
11025
|
caseId: item.id,
|
|
@@ -10917,161 +11027,116 @@ function createEvals(options) {
|
|
|
10917
11027
|
step: evaluated
|
|
10918
11028
|
});
|
|
10919
11029
|
activeStepReported = true;
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
evaluated.status = "failed";
|
|
10924
|
-
evaluated.reason = "The observed interaction did not match the expected answer or interrupt contract.";
|
|
10925
|
-
} else {
|
|
10926
|
-
const outputReason = missingOutputReason(
|
|
10927
|
-
step.expect.outputs,
|
|
10928
|
-
observation
|
|
10929
|
-
);
|
|
10930
|
-
if (outputReason) {
|
|
10931
|
-
evaluated.status = "failed";
|
|
10932
|
-
evaluated.reason = outputReason;
|
|
10933
|
-
} else {
|
|
10934
|
-
const reference = step.expect.reference;
|
|
10935
|
-
if (reference !== void 0) {
|
|
10936
|
-
await reportPhase("reference");
|
|
10937
|
-
evaluated.reference = handlerRef(reference) ? import_zod8.z.json().parse(
|
|
10938
|
-
await references[reference.using]({
|
|
10939
|
-
...context,
|
|
10940
|
-
observation: clone(observation),
|
|
10941
|
-
...reference.params !== void 0 ? { args: reference.params } : {}
|
|
10942
|
-
})
|
|
10943
|
-
) : clone(reference);
|
|
10944
|
-
}
|
|
10945
|
-
await reportPhase("grading");
|
|
10946
|
-
evaluated.evidence = createEvalJudgeEvidence(
|
|
10947
|
-
observation,
|
|
10948
|
-
suite.evidence,
|
|
10949
|
-
evidenceFilters
|
|
10950
|
-
);
|
|
10951
|
-
if (studioGrading && step.expect.criteria?.length) {
|
|
10952
|
-
evaluated.status = "ungraded";
|
|
10953
|
-
attemptResult.status = "ungraded";
|
|
10954
|
-
}
|
|
10955
|
-
for (const [criterionIndex, value] of (studioGrading ? [] : step.expect.criteria ?? []).entries()) {
|
|
10956
|
-
signal.throwIfAborted();
|
|
10957
|
-
const criterion = typeof value === "string" ? { id: String(criterionIndex), text: value } : value;
|
|
10958
|
-
evaluated.judgeCalls = (evaluated.judgeCalls ?? 0) + 1;
|
|
10959
|
-
const verdict = EvalVerdictSchema.parse(
|
|
10960
|
-
await options.judge.grade({
|
|
10961
|
-
criterion,
|
|
10962
|
-
onUsage: (usage) => {
|
|
10963
|
-
if (!signal.aborted) {
|
|
10964
|
-
evaluated.judgeUsage ??= [];
|
|
10965
|
-
evaluated.judgeUsage.push(clone(usage));
|
|
10966
|
-
}
|
|
10967
|
-
},
|
|
10968
|
-
input: clone(input),
|
|
10969
|
-
...getEvalGradeEvidence(
|
|
10970
|
-
evaluated,
|
|
10971
|
-
attemptResult.steps.slice(0, -1)
|
|
10972
|
-
),
|
|
10973
|
-
signal,
|
|
10974
|
-
...evaluated.reference !== void 0 ? { reference: clone(evaluated.reference) } : {}
|
|
10975
|
-
})
|
|
10976
|
-
);
|
|
10977
|
-
signal.throwIfAborted();
|
|
10978
|
-
evaluated.criteria.push({ ...criterion, ...verdict });
|
|
10979
|
-
if (!verdict.passed) evaluated.status = "failed";
|
|
10980
|
-
}
|
|
11030
|
+
if (evaluated.status === "failed") {
|
|
11031
|
+
attemptResult.status = "failed";
|
|
11032
|
+
break;
|
|
10981
11033
|
}
|
|
11034
|
+
history.push(
|
|
11035
|
+
{
|
|
11036
|
+
role: "user",
|
|
11037
|
+
content: command.type === "message" ? command.message : JSON.stringify(command.response)
|
|
11038
|
+
},
|
|
11039
|
+
{ role: "assistant", content: observation.text }
|
|
11040
|
+
);
|
|
10982
11041
|
}
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
11042
|
+
} catch {
|
|
11043
|
+
if (signal.aborted) {
|
|
11044
|
+
attemptResult.status = timedOut ? "error" : "cancelled";
|
|
11045
|
+
if (timedOut)
|
|
11046
|
+
attemptResult.errors.push({
|
|
11047
|
+
phase,
|
|
11048
|
+
code: "EVAL_TIMEOUT",
|
|
11049
|
+
message: "Case exceeded its configured time limit."
|
|
11050
|
+
});
|
|
11051
|
+
} else {
|
|
11052
|
+
attemptResult.status = "error";
|
|
11053
|
+
attemptResult.errors.push(failure(phase));
|
|
10993
11054
|
}
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
} catch {
|
|
11003
|
-
if (signal.aborted) {
|
|
11004
|
-
attemptResult.status = timedOut ? "error" : "cancelled";
|
|
11005
|
-
if (timedOut)
|
|
11006
|
-
attemptResult.errors.push({
|
|
11007
|
-
phase,
|
|
11008
|
-
code: "EVAL_TIMEOUT",
|
|
11009
|
-
message: "Case exceeded its configured time limit."
|
|
11055
|
+
if (activeStep && !activeStepReported) {
|
|
11056
|
+
activeStep.status = attemptResult.status;
|
|
11057
|
+
activeStep.reason = attemptResult.errors.at(-1)?.message ?? "Case was cancelled.";
|
|
11058
|
+
await emit({
|
|
11059
|
+
type: "step-completed",
|
|
11060
|
+
caseId: item.id,
|
|
11061
|
+
repetition,
|
|
11062
|
+
step: activeStep
|
|
11010
11063
|
});
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
if (continuation !== void 0) {
|
|
11036
|
-
try {
|
|
11037
|
-
const cancelled = await call(
|
|
11038
|
-
context,
|
|
11039
|
-
{ type: "cancel" },
|
|
11040
|
-
cleanup.signal
|
|
11041
|
-
);
|
|
11042
|
-
if (cancelled.observation.type === "error" || cancelled.continuation !== void 0)
|
|
11064
|
+
}
|
|
11065
|
+
} finally {
|
|
11066
|
+
clearTimeout(timer);
|
|
11067
|
+
if (context) {
|
|
11068
|
+
await reportPhase("cleanup");
|
|
11069
|
+
const cleanup = new AbortController();
|
|
11070
|
+
const cleanupTimer = setTimeout(
|
|
11071
|
+
() => cleanup.abort(),
|
|
11072
|
+
defaults.cleanupTimeoutMs
|
|
11073
|
+
);
|
|
11074
|
+
try {
|
|
11075
|
+
if (continuation !== void 0) {
|
|
11076
|
+
try {
|
|
11077
|
+
const cancelled = await call(
|
|
11078
|
+
context,
|
|
11079
|
+
{ type: "cancel" },
|
|
11080
|
+
cleanup.signal
|
|
11081
|
+
);
|
|
11082
|
+
if (cancelled.observation.type === "error" || cancelled.continuation !== void 0)
|
|
11083
|
+
attemptResult.errors.push(
|
|
11084
|
+
failure("cleanup", "EVAL_INTERRUPT_CLEANUP_FAILED")
|
|
11085
|
+
);
|
|
11086
|
+
cleanup.signal.throwIfAborted();
|
|
11087
|
+
} catch {
|
|
11043
11088
|
attemptResult.errors.push(
|
|
11044
11089
|
failure("cleanup", "EVAL_INTERRUPT_CLEANUP_FAILED")
|
|
11045
11090
|
);
|
|
11091
|
+
}
|
|
11092
|
+
}
|
|
11093
|
+
try {
|
|
11094
|
+
await options.teardown?.({
|
|
11095
|
+
...context,
|
|
11096
|
+
signal: cleanup.signal,
|
|
11097
|
+
result: clone(attemptResult)
|
|
11098
|
+
});
|
|
11046
11099
|
cleanup.signal.throwIfAborted();
|
|
11047
11100
|
} catch {
|
|
11048
|
-
attemptResult.errors.push(
|
|
11049
|
-
failure("cleanup", "EVAL_INTERRUPT_CLEANUP_FAILED")
|
|
11050
|
-
);
|
|
11101
|
+
attemptResult.errors.push(failure("cleanup"));
|
|
11051
11102
|
}
|
|
11103
|
+
} finally {
|
|
11104
|
+
clearTimeout(cleanupTimer);
|
|
11052
11105
|
}
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
...context,
|
|
11056
|
-
signal: cleanup.signal,
|
|
11057
|
-
result: clone(attemptResult)
|
|
11058
|
-
});
|
|
11059
|
-
cleanup.signal.throwIfAborted();
|
|
11060
|
-
} catch {
|
|
11061
|
-
attemptResult.errors.push(failure("cleanup"));
|
|
11062
|
-
}
|
|
11063
|
-
} finally {
|
|
11064
|
-
clearTimeout(cleanupTimer);
|
|
11106
|
+
if (attemptResult.errors.some((error) => error.phase === "cleanup"))
|
|
11107
|
+
attemptResult.status = "error";
|
|
11065
11108
|
}
|
|
11066
|
-
|
|
11067
|
-
attemptResult.status = "error";
|
|
11109
|
+
attemptResult.durationMs = Date.now() - started;
|
|
11068
11110
|
}
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11111
|
+
acceptingExecutions = false;
|
|
11112
|
+
await associationProgress;
|
|
11113
|
+
await emit({ type: "case-completed", result: attemptResult });
|
|
11114
|
+
return attemptResult;
|
|
11115
|
+
};
|
|
11116
|
+
return (0, import_eval_attribution2.withEvalAttribution)(
|
|
11117
|
+
{
|
|
11118
|
+
attemptId,
|
|
11119
|
+
onExecution: (execution) => {
|
|
11120
|
+
if (!acceptingExecutions || runtimeExecutions.some(
|
|
11121
|
+
(entry) => entry.runId === execution.runId && entry.sessionId === execution.sessionId
|
|
11122
|
+
))
|
|
11123
|
+
return;
|
|
11124
|
+
runtimeExecutions.push(execution);
|
|
11125
|
+
associationProgress = associationProgress.then(
|
|
11126
|
+
() => emit({
|
|
11127
|
+
type: "case-runtime-associated",
|
|
11128
|
+
caseId: item.id,
|
|
11129
|
+
repetition,
|
|
11130
|
+
attemptId,
|
|
11131
|
+
execution
|
|
11132
|
+
})
|
|
11133
|
+
);
|
|
11134
|
+
}
|
|
11135
|
+
},
|
|
11136
|
+
executeAttempt
|
|
11137
|
+
);
|
|
11073
11138
|
};
|
|
11074
|
-
const total = selected.length * repetitions;
|
|
11139
|
+
const total = args.attempt ? 1 : selected.length * repetitions;
|
|
11075
11140
|
if (args.includeActivity)
|
|
11076
11141
|
await emit({
|
|
11077
11142
|
type: "run-started",
|
|
@@ -11085,8 +11150,11 @@ function createEvals(options) {
|
|
|
11085
11150
|
Array.from({ length: Math.min(concurrency, total) }, async () => {
|
|
11086
11151
|
while (cursor < total) {
|
|
11087
11152
|
const index = cursor++;
|
|
11088
|
-
const item = selected[Math.floor(index / repetitions)];
|
|
11089
|
-
completed[index] = await attempt(
|
|
11153
|
+
const item = args.attempt ? selected.find((item2) => item2.id === args.attempt?.caseId) : selected[Math.floor(index / repetitions)];
|
|
11154
|
+
completed[index] = await attempt(
|
|
11155
|
+
item,
|
|
11156
|
+
args.attempt?.repetition ?? index % repetitions + 1
|
|
11157
|
+
);
|
|
11090
11158
|
}
|
|
11091
11159
|
})
|
|
11092
11160
|
);
|
|
@@ -11396,6 +11464,7 @@ function createEvalRouteHandler({
|
|
|
11396
11464
|
const digest = (value) => (0, import_node_crypto6.createHash)("sha256").update(value).digest();
|
|
11397
11465
|
const expected = digest(`Bearer ${serviceKey}`);
|
|
11398
11466
|
let active = 0;
|
|
11467
|
+
const evaluations = /* @__PURE__ */ new Map();
|
|
11399
11468
|
return async (request) => {
|
|
11400
11469
|
if (!(0, import_node_crypto6.timingSafeEqual)(
|
|
11401
11470
|
expected,
|
|
@@ -11452,14 +11521,32 @@ function createEvalRouteHandler({
|
|
|
11452
11521
|
{ error: "App judge changed. Refresh before running." },
|
|
11453
11522
|
{ status: 409 }
|
|
11454
11523
|
);
|
|
11455
|
-
if (
|
|
11524
|
+
if (body.attempt && (!manifest.attemptScheduling || !suite.cases.some((item) => item.id === body.attempt?.caseId) || body.caseIds && !body.caseIds.includes(body.attempt.caseId) || body.attempt.repetition > body.repetitions))
|
|
11525
|
+
return Response.json(
|
|
11526
|
+
{ error: "Unsupported or invalid scheduled attempt." },
|
|
11527
|
+
{ status: 409 }
|
|
11528
|
+
);
|
|
11529
|
+
const group = body.attempt && evaluations.get(body.attempt.evaluationId);
|
|
11530
|
+
if (group && (group.concurrency !== body.concurrency || group.active >= group.concurrency))
|
|
11531
|
+
return Response.json(
|
|
11532
|
+
{ error: "Evaluation concurrency exhausted or changed." },
|
|
11533
|
+
{ status: 429 }
|
|
11534
|
+
);
|
|
11535
|
+
if (!group && active >= maxActiveRuns)
|
|
11456
11536
|
return Response.json(
|
|
11457
11537
|
{ error: "Eval executor is busy." },
|
|
11458
11538
|
{ status: 429 }
|
|
11459
11539
|
);
|
|
11460
11540
|
if (request.signal.aborted)
|
|
11461
11541
|
return Response.json({ error: "Request cancelled." }, { status: 409 });
|
|
11462
|
-
|
|
11542
|
+
if (body.attempt) {
|
|
11543
|
+
const state = group ?? { active: 0, concurrency: body.concurrency };
|
|
11544
|
+
if (!group) {
|
|
11545
|
+
active++;
|
|
11546
|
+
evaluations.set(body.attempt.evaluationId, state);
|
|
11547
|
+
}
|
|
11548
|
+
state.active++;
|
|
11549
|
+
} else active++;
|
|
11463
11550
|
const controller = new AbortController();
|
|
11464
11551
|
const requestSignal = request.signal;
|
|
11465
11552
|
const abortRequest = () => controller.abort();
|
|
@@ -11475,6 +11562,7 @@ function createEvalRouteHandler({
|
|
|
11475
11562
|
};
|
|
11476
11563
|
void evals.run({
|
|
11477
11564
|
suiteId: body.suiteId,
|
|
11565
|
+
...body.attempt ? { attempt: body.attempt } : {},
|
|
11478
11566
|
...body.grading ? { grading: body.grading } : {},
|
|
11479
11567
|
...body.judge ? { judgeIdentity: body.judge } : {},
|
|
11480
11568
|
repetitions: body.repetitions,
|
|
@@ -11487,7 +11575,14 @@ function createEvalRouteHandler({
|
|
|
11487
11575
|
() => send({ type: "error", message: "Eval runner failed." })
|
|
11488
11576
|
).finally(() => {
|
|
11489
11577
|
requestSignal.removeEventListener("abort", abortRequest);
|
|
11490
|
-
|
|
11578
|
+
if (body.attempt) {
|
|
11579
|
+
const state = evaluations.get(body.attempt.evaluationId);
|
|
11580
|
+
state.active--;
|
|
11581
|
+
if (!state.active) {
|
|
11582
|
+
evaluations.delete(body.attempt.evaluationId);
|
|
11583
|
+
active--;
|
|
11584
|
+
}
|
|
11585
|
+
} else active--;
|
|
11491
11586
|
if (!controller.signal.aborted) output.close();
|
|
11492
11587
|
});
|
|
11493
11588
|
},
|