raindrop-ai 0.4.0 → 0.5.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/README.md +168 -0
- package/dist/{chunk-MZZJWWWB.mjs → chunk-AUNFORWV.mjs} +1213 -860
- package/dist/chunk-HFHHGRTW.mjs +1201 -0
- package/dist/index-CQj4bn2v.d.mts +60760 -0
- package/dist/index-CQj4bn2v.d.ts +60760 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +11422 -6431
- package/dist/index.mjs +3397 -176
- package/dist/portable-runtime-GBE3SHBZ.mjs +193 -0
- package/dist/tracing/index.d.mts +2 -1
- package/dist/tracing/index.d.ts +2 -1
- package/dist/tracing/index.js +1301 -982
- package/dist/tracing/index.mjs +1 -1
- package/package.json +19 -12
- package/dist/index-CDmWtd83.d.mts +0 -1525
- package/dist/index-CDmWtd83.d.ts +0 -1525
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
3
|
+
EVAL_CORRELATION_ID_ATTRIBUTE,
|
|
3
4
|
MAX_RETRY_DELAY_MS,
|
|
4
5
|
POST_SHUTDOWN_TIMEOUT_MS,
|
|
5
6
|
SHUTDOWN_DEADLINE_MS,
|
|
6
7
|
authHintForKey,
|
|
8
|
+
boundedClone,
|
|
7
9
|
capText,
|
|
10
|
+
currentEvalScope,
|
|
11
|
+
currentReplayTraceDestination,
|
|
12
|
+
evalScopeAttributes,
|
|
8
13
|
localDebuggerEnabled,
|
|
9
14
|
mirrorPartialEventToLocalDebugger,
|
|
10
15
|
normalizeFeatureFlags,
|
|
@@ -12,6 +17,7 @@ import {
|
|
|
12
17
|
package_default,
|
|
13
18
|
projectIdHeaders,
|
|
14
19
|
raceWithTimeout,
|
|
20
|
+
randomUUID,
|
|
15
21
|
rateLimitedLog,
|
|
16
22
|
redactUrlForLog,
|
|
17
23
|
resolveLocalDebuggerBaseUrl,
|
|
@@ -20,8 +26,36 @@ import {
|
|
|
20
26
|
setDefaultMaxTextFieldChars,
|
|
21
27
|
stringifyBounded,
|
|
22
28
|
tracing,
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
withEvalScope,
|
|
30
|
+
withReplayTraceDestination,
|
|
31
|
+
withRoutingContext,
|
|
32
|
+
withTask
|
|
33
|
+
} from "./chunk-AUNFORWV.mjs";
|
|
34
|
+
import {
|
|
35
|
+
BooleanThresholdSchema,
|
|
36
|
+
CapturedTraceSnapshotSchema,
|
|
37
|
+
EvalCaseTracePairSchema,
|
|
38
|
+
EvalDatasetCaseIdSchema,
|
|
39
|
+
EvalScoreSchema,
|
|
40
|
+
NumericThresholdSchema,
|
|
41
|
+
TraceSchema,
|
|
42
|
+
defineDataset,
|
|
43
|
+
defineEvalSuite,
|
|
44
|
+
defineEvaluatorProgram,
|
|
45
|
+
defineLocalEvaluator,
|
|
46
|
+
evaluatePortableEvaluator,
|
|
47
|
+
evaluatorName,
|
|
48
|
+
evaluatorOutput,
|
|
49
|
+
importPortableEvaluator,
|
|
50
|
+
isEvalDataset,
|
|
51
|
+
isEvalSuiteDefinition,
|
|
52
|
+
projectCapturedEvent,
|
|
53
|
+
projectWorkshopTrace,
|
|
54
|
+
traceOutput,
|
|
55
|
+
traceToolCalls,
|
|
56
|
+
validateEvaluatorOutput,
|
|
57
|
+
verifyPortableEvalArtifact
|
|
58
|
+
} from "./chunk-HFHHGRTW.mjs";
|
|
25
59
|
import {
|
|
26
60
|
__commonJS,
|
|
27
61
|
__toESM
|
|
@@ -717,11 +751,11 @@ var require_lodash = __commonJS({
|
|
|
717
751
|
function unicodeWords(string) {
|
|
718
752
|
return string.match(reUnicodeWord) || [];
|
|
719
753
|
}
|
|
720
|
-
var runInContext = (function runInContext2(
|
|
721
|
-
|
|
722
|
-
var Array2 =
|
|
754
|
+
var runInContext = (function runInContext2(context3) {
|
|
755
|
+
context3 = context3 == null ? root : _.defaults(root.Object(), context3, _.pick(root, contextProps));
|
|
756
|
+
var Array2 = context3.Array, Date2 = context3.Date, Error2 = context3.Error, Function2 = context3.Function, Math2 = context3.Math, Object2 = context3.Object, RegExp2 = context3.RegExp, String2 = context3.String, TypeError = context3.TypeError;
|
|
723
757
|
var arrayProto = Array2.prototype, funcProto = Function2.prototype, objectProto = Object2.prototype;
|
|
724
|
-
var coreJsData =
|
|
758
|
+
var coreJsData = context3["__core-js_shared__"];
|
|
725
759
|
var funcToString = funcProto.toString;
|
|
726
760
|
var hasOwnProperty = objectProto.hasOwnProperty;
|
|
727
761
|
var idCounter = 0;
|
|
@@ -735,7 +769,7 @@ var require_lodash = __commonJS({
|
|
|
735
769
|
var reIsNative = RegExp2(
|
|
736
770
|
"^" + funcToString.call(hasOwnProperty).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"
|
|
737
771
|
);
|
|
738
|
-
var
|
|
772
|
+
var Buffer2 = moduleExports ? context3.Buffer : undefined2, Symbol2 = context3.Symbol, Uint8Array2 = context3.Uint8Array, allocUnsafe = Buffer2 ? Buffer2.allocUnsafe : undefined2, getPrototype = overArg(Object2.getPrototypeOf, Object2), objectCreate = Object2.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol2 ? Symbol2.isConcatSpreadable : undefined2, symIterator = Symbol2 ? Symbol2.iterator : undefined2, symToStringTag = Symbol2 ? Symbol2.toStringTag : undefined2;
|
|
739
773
|
var defineProperty = (function() {
|
|
740
774
|
try {
|
|
741
775
|
var func = getNative(Object2, "defineProperty");
|
|
@@ -744,9 +778,9 @@ var require_lodash = __commonJS({
|
|
|
744
778
|
} catch (e) {
|
|
745
779
|
}
|
|
746
780
|
})();
|
|
747
|
-
var ctxClearTimeout =
|
|
748
|
-
var nativeCeil = Math2.ceil, nativeFloor = Math2.floor, nativeGetSymbols = Object2.getOwnPropertySymbols, nativeIsBuffer =
|
|
749
|
-
var DataView = getNative(
|
|
781
|
+
var ctxClearTimeout = context3.clearTimeout !== root.clearTimeout && context3.clearTimeout, ctxNow = Date2 && Date2.now !== root.Date.now && Date2.now, ctxSetTimeout = context3.setTimeout !== root.setTimeout && context3.setTimeout;
|
|
782
|
+
var nativeCeil = Math2.ceil, nativeFloor = Math2.floor, nativeGetSymbols = Object2.getOwnPropertySymbols, nativeIsBuffer = Buffer2 ? Buffer2.isBuffer : undefined2, nativeIsFinite = context3.isFinite, nativeJoin = arrayProto.join, nativeKeys = overArg(Object2.keys, Object2), nativeMax = Math2.max, nativeMin = Math2.min, nativeNow = Date2.now, nativeParseInt = context3.parseInt, nativeRandom = Math2.random, nativeReverse = arrayProto.reverse;
|
|
783
|
+
var DataView = getNative(context3, "DataView"), Map2 = getNative(context3, "Map"), Promise2 = getNative(context3, "Promise"), Set2 = getNative(context3, "Set"), WeakMap = getNative(context3, "WeakMap"), nativeCreate = getNative(Object2, "create");
|
|
750
784
|
var metaMap = WeakMap && new WeakMap();
|
|
751
785
|
var realNames = {};
|
|
752
786
|
var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map2), promiseCtorString = toSource(Promise2), setCtorString = toSource(Set2), weakMapCtorString = toSource(WeakMap);
|
|
@@ -2050,7 +2084,7 @@ var require_lodash = __commonJS({
|
|
|
2050
2084
|
}
|
|
2051
2085
|
function cloneArrayBuffer(arrayBuffer) {
|
|
2052
2086
|
var result2 = new arrayBuffer.constructor(arrayBuffer.byteLength);
|
|
2053
|
-
new
|
|
2087
|
+
new Uint8Array2(result2).set(new Uint8Array2(arrayBuffer));
|
|
2054
2088
|
return result2;
|
|
2055
2089
|
}
|
|
2056
2090
|
function cloneDataView(dataView, isDeep) {
|
|
@@ -2654,7 +2688,7 @@ var require_lodash = __commonJS({
|
|
|
2654
2688
|
object = object.buffer;
|
|
2655
2689
|
other = other.buffer;
|
|
2656
2690
|
case arrayBufferTag:
|
|
2657
|
-
if (object.byteLength != other.byteLength || !equalFunc(new
|
|
2691
|
+
if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array2(object), new Uint8Array2(other))) {
|
|
2658
2692
|
return false;
|
|
2659
2693
|
}
|
|
2660
2694
|
return true;
|
|
@@ -9984,176 +10018,3264 @@ var AiTrackUsageSchema = z.object({
|
|
|
9984
10018
|
});
|
|
9985
10019
|
var AttachmentTypeSchema = z.enum(["code", "text", "image", "iframe"]);
|
|
9986
10020
|
|
|
9987
|
-
// src/index.ts
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10021
|
+
// src/replay/index.ts
|
|
10022
|
+
import { context, trace } from "@opentelemetry/api";
|
|
10023
|
+
import { z as z3 } from "zod";
|
|
10024
|
+
|
|
10025
|
+
// src/evals/generated/dataset.ts
|
|
10026
|
+
import { z as z2 } from "zod";
|
|
10027
|
+
var EVAL_DATASET_MAX_CASES = 1e3;
|
|
10028
|
+
var EVAL_DATASET_MAX_REQUEST_BYTES = 4 * 1024 * 1024;
|
|
10029
|
+
var EVAL_DATASET_MAX_REFERENCE_BYTES = 1024 * 1024;
|
|
10030
|
+
var EvalDatasetSlugSchema = z2.string().min(1).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/);
|
|
10031
|
+
var EvalDatasetFingerprintSchema = z2.string().regex(/^[0-9a-f]{64}$/);
|
|
10032
|
+
var EvalDatasetCaseInputSchema = z2.object({
|
|
10033
|
+
id: EvalDatasetCaseIdSchema,
|
|
10034
|
+
name: z2.string().trim().min(1).max(200),
|
|
10035
|
+
input: z2.string().nullable(),
|
|
10036
|
+
properties: z2.record(z2.string(), z2.string()),
|
|
10037
|
+
referenceTrace: CapturedTraceSnapshotSchema
|
|
10038
|
+
}).strict();
|
|
10039
|
+
var EvalDatasetVersionCaseSchema = EvalDatasetCaseInputSchema.extend({
|
|
10040
|
+
referenceTraceSha256: EvalDatasetFingerprintSchema
|
|
10041
|
+
});
|
|
10042
|
+
var EvalDatasetCaseInputsSchema = z2.array(EvalDatasetCaseInputSchema).min(1).max(EVAL_DATASET_MAX_CASES).superRefine((cases, context3) => {
|
|
10043
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10044
|
+
for (const [index, entry] of cases.entries()) {
|
|
10045
|
+
if (seen.has(entry.id)) {
|
|
10046
|
+
context3.addIssue({
|
|
10047
|
+
code: z2.ZodIssueCode.custom,
|
|
10048
|
+
message: `Duplicate case id ${entry.id}`,
|
|
10049
|
+
path: [index, "id"]
|
|
10050
|
+
});
|
|
10051
|
+
}
|
|
10052
|
+
seen.add(entry.id);
|
|
10007
10053
|
}
|
|
10008
|
-
};
|
|
10009
|
-
|
|
10010
|
-
|
|
10054
|
+
});
|
|
10055
|
+
var PublishEvalDatasetVersionInputSchema = z2.object({
|
|
10056
|
+
name: z2.string().trim().min(1).max(100),
|
|
10057
|
+
requestKey: z2.string().uuid(),
|
|
10058
|
+
expectedCurrentVersionId: z2.string().uuid().nullable(),
|
|
10059
|
+
fingerprint: EvalDatasetFingerprintSchema,
|
|
10060
|
+
cases: EvalDatasetCaseInputsSchema
|
|
10061
|
+
}).strict();
|
|
10062
|
+
var EvalDatasetIdentitySchema = z2.object({
|
|
10063
|
+
id: z2.string().uuid(),
|
|
10064
|
+
slug: EvalDatasetSlugSchema,
|
|
10065
|
+
name: z2.string().min(1).max(100),
|
|
10066
|
+
currentVersionId: z2.string().uuid().nullable()
|
|
10067
|
+
});
|
|
10068
|
+
var EvalDatasetVersionSchema = z2.object({
|
|
10069
|
+
id: z2.string().uuid(),
|
|
10070
|
+
datasetId: z2.string().uuid(),
|
|
10071
|
+
parentVersionId: z2.string().uuid().nullable(),
|
|
10072
|
+
fingerprint: EvalDatasetFingerprintSchema,
|
|
10073
|
+
cases: z2.array(EvalDatasetVersionCaseSchema).min(1).max(EVAL_DATASET_MAX_CASES),
|
|
10074
|
+
createdAt: z2.string().datetime({ offset: true })
|
|
10075
|
+
});
|
|
10076
|
+
var EvalDatasetManifestSchema = z2.object({
|
|
10077
|
+
dataset: EvalDatasetIdentitySchema,
|
|
10078
|
+
version: EvalDatasetVersionSchema.omit({ cases: true }),
|
|
10079
|
+
cases: z2.array(EvalDatasetVersionCaseSchema.pick({ id: true, name: true, input: true, properties: true }).extend({
|
|
10080
|
+
referenceTraceSha256: EvalDatasetFingerprintSchema
|
|
10081
|
+
}))
|
|
10082
|
+
});
|
|
10083
|
+
function compareNanoseconds(left, right) {
|
|
10084
|
+
const leftValue = BigInt(left);
|
|
10085
|
+
const rightValue = BigInt(right);
|
|
10086
|
+
return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0;
|
|
10011
10087
|
}
|
|
10012
|
-
function
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
if (!signalKey || !value || typeof value !== "object") return void 0;
|
|
10018
|
-
const description = (_a = value.description) == null ? void 0 : _a.trim();
|
|
10019
|
-
if (!description) return void 0;
|
|
10020
|
-
const sentiment = value.sentiment;
|
|
10021
|
-
return [
|
|
10022
|
-
signalKey,
|
|
10023
|
-
{
|
|
10024
|
-
description,
|
|
10025
|
-
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
10026
|
-
}
|
|
10027
|
-
];
|
|
10028
|
-
}).filter(
|
|
10029
|
-
(entry) => entry !== void 0
|
|
10030
|
-
);
|
|
10031
|
-
if (normalizedEntries.length === 0) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
10032
|
-
return Object.fromEntries(normalizedEntries);
|
|
10088
|
+
function orderSnapshot(snapshot) {
|
|
10089
|
+
return {
|
|
10090
|
+
event: snapshot.event,
|
|
10091
|
+
spans: [...snapshot.spans].sort((left, right) => compareNanoseconds(left.start_unix_ns, right.start_unix_ns) || left.span_id.localeCompare(right.span_id))
|
|
10092
|
+
};
|
|
10033
10093
|
}
|
|
10034
|
-
function
|
|
10094
|
+
function normalizeCasesForFingerprint(cases) {
|
|
10095
|
+
return cases.map((entry) => ({
|
|
10096
|
+
...entry,
|
|
10097
|
+
referenceTrace: orderSnapshot(entry.referenceTrace)
|
|
10098
|
+
}));
|
|
10099
|
+
}
|
|
10100
|
+
function stableStringify(value) {
|
|
10101
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
10102
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
10103
|
+
return `{${Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(",")}}`;
|
|
10104
|
+
}
|
|
10105
|
+
function canonicalJson(value) {
|
|
10106
|
+
return stableStringify(JSON.parse(JSON.stringify(value)));
|
|
10107
|
+
}
|
|
10108
|
+
async function sha256(value) {
|
|
10035
10109
|
var _a;
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
return
|
|
10110
|
+
if (!((_a = globalThis.crypto) == null ? void 0 : _a.subtle)) {
|
|
10111
|
+
throw new Error("Raindrop eval dataset publication requires Web Crypto SHA-256 support");
|
|
10112
|
+
}
|
|
10113
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
10114
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
10115
|
+
}
|
|
10116
|
+
async function evalDatasetVersionFingerprint(cases) {
|
|
10117
|
+
const parsed = EvalDatasetCaseInputsSchema.parse(cases);
|
|
10118
|
+
return sha256(canonicalJson(normalizeCasesForFingerprint(parsed)));
|
|
10119
|
+
}
|
|
10045
10120
|
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10121
|
+
// src/replay/endpoint.ts
|
|
10122
|
+
function credentialEndpoint(value) {
|
|
10123
|
+
const url = new URL(value);
|
|
10124
|
+
const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
10125
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
10126
|
+
throw new Error("Raindrop endpoints require HTTPS, except on loopback hosts");
|
|
10127
|
+
}
|
|
10128
|
+
if (url.username || url.password || url.search || url.hash) {
|
|
10129
|
+
throw new Error("Raindrop endpoints cannot contain credentials, a query, or a fragment");
|
|
10130
|
+
}
|
|
10131
|
+
return url;
|
|
10132
|
+
}
|
|
10133
|
+
function replayIngestEndpoint(value, trustedUrl) {
|
|
10134
|
+
const url = credentialEndpoint(value);
|
|
10135
|
+
if (!trustedUrl && url.href.replace(/\/+$/, "") === "https://app.raindrop.ai/api/replays/ingest") {
|
|
10136
|
+
url.hostname = "backend.raindrop.ai";
|
|
10137
|
+
}
|
|
10138
|
+
const trusted = credentialEndpoint(trustedUrl != null ? trustedUrl : "https://backend.raindrop.ai/api/replays/ingest/");
|
|
10139
|
+
if (url.href.replace(/\/+$/, "") !== trusted.href.replace(/\/+$/, "")) {
|
|
10140
|
+
throw new Error("Raindrop replay ingest URL does not match the trusted replayIngestUrl");
|
|
10141
|
+
}
|
|
10142
|
+
return url.href;
|
|
10143
|
+
}
|
|
10051
10144
|
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10145
|
+
// src/replay/git.ts
|
|
10146
|
+
import { execFileSync } from "child_process";
|
|
10147
|
+
function readGitCommit() {
|
|
10148
|
+
const sha = git(["rev-parse", "HEAD"]);
|
|
10149
|
+
if (sha === null) return null;
|
|
10150
|
+
const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
10151
|
+
const status = git(["status", "--porcelain"]);
|
|
10152
|
+
if (status === null) return null;
|
|
10153
|
+
return {
|
|
10154
|
+
sha,
|
|
10155
|
+
branch: branch === null || branch === "HEAD" ? null : branch,
|
|
10156
|
+
dirty: status !== ""
|
|
10157
|
+
};
|
|
10158
|
+
}
|
|
10159
|
+
function git(args) {
|
|
10160
|
+
try {
|
|
10161
|
+
return execFileSync("git", [...args], {
|
|
10162
|
+
encoding: "utf8",
|
|
10163
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
10164
|
+
timeout: 2e3
|
|
10165
|
+
}).trim();
|
|
10166
|
+
} catch (e) {
|
|
10167
|
+
return null;
|
|
10168
|
+
}
|
|
10169
|
+
}
|
|
10057
10170
|
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10171
|
+
// src/replay/index.ts
|
|
10172
|
+
var DEFAULT_QUERY_URL = "https://query.raindrop.ai";
|
|
10173
|
+
var DEFAULT_TRACE_WAIT_MS = 3e4;
|
|
10174
|
+
var DEFAULT_EVAL_WAIT_MS = 10 * 6e4;
|
|
10175
|
+
var DEFAULT_EVAL_POLL_INTERVAL_MS = 2e3;
|
|
10176
|
+
var MAX_EVAL_STATUS_RETRIES = 2;
|
|
10177
|
+
var TRACE_POLL_INTERVAL_MS = 500;
|
|
10178
|
+
var DEFAULT_CONCURRENCY = 4;
|
|
10179
|
+
var EVAL_RETRY_BASE_DELAY_MS = 50;
|
|
10180
|
+
var MAX_REPLAY_REPORT_RETRIES = 2;
|
|
10181
|
+
var QUERY_REQUEST_TIMEOUT_MS = 3e4;
|
|
10182
|
+
var LOCAL_EVALUATOR_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
10183
|
+
function retryDelayMs(response, retry) {
|
|
10184
|
+
const retryAfter = response.headers.get("retry-after");
|
|
10185
|
+
if (retryAfter !== null) {
|
|
10186
|
+
const seconds = Number(retryAfter);
|
|
10187
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
10188
|
+
}
|
|
10189
|
+
return EVAL_RETRY_BASE_DELAY_MS * 2 ** retry;
|
|
10065
10190
|
}
|
|
10066
|
-
|
|
10191
|
+
var ReplayRowSchema = z3.object({
|
|
10192
|
+
id: z3.string(),
|
|
10193
|
+
name: z3.string(),
|
|
10194
|
+
input: z3.string().nullable(),
|
|
10195
|
+
output: z3.string().nullable(),
|
|
10196
|
+
properties: z3.record(z3.string())
|
|
10197
|
+
});
|
|
10198
|
+
var WireEvalManifestSchema = z3.object({
|
|
10199
|
+
dataset_id: z3.string().uuid(),
|
|
10200
|
+
fingerprint: z3.string().regex(/^[a-f0-9]{64}$/),
|
|
10201
|
+
rows: z3.array(ReplayRowSchema.extend({ id: z3.string().min(1) }))
|
|
10202
|
+
});
|
|
10203
|
+
var ReplayCountsSchema = z3.object({
|
|
10204
|
+
total: z3.number(),
|
|
10205
|
+
pending: z3.number(),
|
|
10206
|
+
done: z3.number(),
|
|
10207
|
+
missing: z3.number()
|
|
10208
|
+
});
|
|
10209
|
+
var ReplayEvaluatorCountsSchema = z3.object({
|
|
10210
|
+
traces: z3.number(),
|
|
10211
|
+
graded: z3.number(),
|
|
10212
|
+
errored: z3.number(),
|
|
10213
|
+
ungraded: z3.number(),
|
|
10214
|
+
pending: z3.number()
|
|
10215
|
+
});
|
|
10216
|
+
var ReplayEvaluatorStatsSchema = z3.object({
|
|
10217
|
+
traceCount: z3.number(),
|
|
10218
|
+
hydratedCount: z3.number(),
|
|
10219
|
+
judgeCalls: z3.number(),
|
|
10220
|
+
durationMs: z3.number(),
|
|
10221
|
+
skippedRows: z3.number(),
|
|
10222
|
+
skippedReason: z3.literal("no trace").nullable()
|
|
10223
|
+
});
|
|
10224
|
+
var ReplayEvaluatorSummarySchema = z3.discriminatedUnion("outputType", [
|
|
10225
|
+
z3.object({
|
|
10226
|
+
outputType: z3.literal("boolean"),
|
|
10227
|
+
counts: ReplayEvaluatorCountsSchema,
|
|
10228
|
+
stats: ReplayEvaluatorStatsSchema,
|
|
10229
|
+
passed: z3.number(),
|
|
10230
|
+
failed: z3.number(),
|
|
10231
|
+
passRate: z3.number().nullable()
|
|
10232
|
+
}),
|
|
10233
|
+
z3.object({
|
|
10234
|
+
outputType: z3.literal("score"),
|
|
10235
|
+
counts: ReplayEvaluatorCountsSchema,
|
|
10236
|
+
stats: ReplayEvaluatorStatsSchema,
|
|
10237
|
+
mean: z3.number().nullable(),
|
|
10238
|
+
median: z3.number().nullable(),
|
|
10239
|
+
p10: z3.number().nullable(),
|
|
10240
|
+
p90: z3.number().nullable(),
|
|
10241
|
+
atLeast4Share: z3.number().nullable(),
|
|
10242
|
+
histogram: z3.tuple([z3.number(), z3.number(), z3.number(), z3.number(), z3.number()])
|
|
10243
|
+
}),
|
|
10244
|
+
z3.object({
|
|
10245
|
+
outputType: z3.literal("number"),
|
|
10246
|
+
counts: ReplayEvaluatorCountsSchema,
|
|
10247
|
+
stats: ReplayEvaluatorStatsSchema,
|
|
10248
|
+
median: z3.number().nullable(),
|
|
10249
|
+
p10: z3.number().nullable(),
|
|
10250
|
+
p90: z3.number().nullable()
|
|
10251
|
+
})
|
|
10252
|
+
]);
|
|
10253
|
+
var ReplayVerdictIdentitySchema = z3.object({
|
|
10254
|
+
evaluator: z3.string(),
|
|
10255
|
+
runId: z3.string(),
|
|
10256
|
+
url: z3.string()
|
|
10257
|
+
});
|
|
10258
|
+
var ReplayVerdictGradedSchema = ReplayVerdictIdentitySchema.extend({
|
|
10259
|
+
state: z3.literal("graded"),
|
|
10260
|
+
note: z3.string().optional()
|
|
10261
|
+
});
|
|
10262
|
+
var ReplayVerdictSchema = z3.union([
|
|
10263
|
+
ReplayVerdictGradedSchema.extend({ pass: z3.boolean() }),
|
|
10264
|
+
ReplayVerdictGradedSchema.extend({ score: EvalScoreSchema }),
|
|
10265
|
+
ReplayVerdictGradedSchema.extend({ value: z3.number().finite() }),
|
|
10266
|
+
ReplayVerdictIdentitySchema.extend({
|
|
10267
|
+
state: z3.literal("errored"),
|
|
10268
|
+
message: z3.string()
|
|
10269
|
+
}),
|
|
10270
|
+
ReplayVerdictIdentitySchema.extend({
|
|
10271
|
+
state: z3.literal("ungraded"),
|
|
10272
|
+
reason: z3.enum(["not sampled", "trace unavailable", "content unreadable"])
|
|
10273
|
+
})
|
|
10274
|
+
]);
|
|
10275
|
+
var ReplayResultRowSchema = ReplayRowSchema.extend({
|
|
10276
|
+
status: z3.enum(["pending", "done", "missing"]),
|
|
10277
|
+
traceId: z3.string().nullable(),
|
|
10278
|
+
error: z3.string().nullable(),
|
|
10279
|
+
verdicts: z3.array(ReplayVerdictSchema)
|
|
10280
|
+
});
|
|
10281
|
+
var ReplayTraceEvidenceEntrySchema = z3.object({
|
|
10282
|
+
rowId: z3.string().min(1).max(512),
|
|
10283
|
+
traceId: z3.string().regex(/^[a-f0-9]{32}$/),
|
|
10284
|
+
spanIds: z3.array(z3.string().regex(/^[a-f0-9]{16}$/)).min(1).max(500)
|
|
10285
|
+
}).superRefine((entry, context3) => {
|
|
10286
|
+
if (new Set(entry.spanIds).size !== entry.spanIds.length) {
|
|
10287
|
+
context3.addIssue({
|
|
10288
|
+
code: z3.ZodIssueCode.custom,
|
|
10289
|
+
message: "Replay trace evidence span ids must be unique",
|
|
10290
|
+
path: ["spanIds"]
|
|
10291
|
+
});
|
|
10292
|
+
}
|
|
10293
|
+
});
|
|
10294
|
+
var ReplayTraceEvidenceSchema = z3.array(ReplayTraceEvidenceEntrySchema).superRefine((entries, context3) => {
|
|
10295
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10296
|
+
for (const [index, entry] of entries.entries()) {
|
|
10297
|
+
if (seen.has(entry.rowId)) {
|
|
10298
|
+
context3.addIssue({
|
|
10299
|
+
code: z3.ZodIssueCode.custom,
|
|
10300
|
+
message: "Replay trace evidence row ids must be unique",
|
|
10301
|
+
path: [index, "rowId"]
|
|
10302
|
+
});
|
|
10303
|
+
}
|
|
10304
|
+
seen.add(entry.rowId);
|
|
10305
|
+
}
|
|
10306
|
+
});
|
|
10307
|
+
var LocalBooleanVerdictSchema = z3.object({ pass: z3.boolean(), note: z3.string().trim().min(1).max(600).optional() }).strict();
|
|
10308
|
+
var LocalScoreVerdictSchema = z3.object({
|
|
10309
|
+
score: EvalScoreSchema,
|
|
10310
|
+
note: z3.string().trim().min(1).max(600).optional()
|
|
10311
|
+
}).strict();
|
|
10312
|
+
var LocalNumberVerdictSchema = z3.object({ value: z3.number().finite(), note: z3.string().trim().min(1).max(600).optional() }).strict();
|
|
10313
|
+
var LocalVerdictSchema = z3.union([
|
|
10314
|
+
LocalBooleanVerdictSchema,
|
|
10315
|
+
LocalScoreVerdictSchema,
|
|
10316
|
+
LocalNumberVerdictSchema
|
|
10317
|
+
]);
|
|
10318
|
+
async function queryApi(request) {
|
|
10319
|
+
var _a, _b;
|
|
10320
|
+
if (!((_a = request.client.connection.apiKey) == null ? void 0 : _a.trim())) {
|
|
10321
|
+
throw new Error("Raindrop evals and datasets require a Query SDK API key (apiKey)");
|
|
10322
|
+
}
|
|
10323
|
+
const headers = {
|
|
10324
|
+
Authorization: `Bearer ${request.client.connection.apiKey}`,
|
|
10325
|
+
"Content-Type": "application/json"
|
|
10326
|
+
};
|
|
10327
|
+
const projectId = request.client.connection.projectId;
|
|
10328
|
+
if (projectId !== void 0) headers["X-Raindrop-Project-Id"] = projectId;
|
|
10329
|
+
const url = `${credentialEndpoint(request.queryUrl).href.replace(/\/+$/, "")}${request.path}`;
|
|
10330
|
+
const maxRetries = (_b = request.maxRetries) != null ? _b : 0;
|
|
10331
|
+
const body = request.body === void 0 ? void 0 : JSON.stringify(request.body);
|
|
10332
|
+
for (let retry = 0; ; retry += 1) {
|
|
10333
|
+
let response;
|
|
10334
|
+
let text;
|
|
10335
|
+
const controller = new AbortController();
|
|
10336
|
+
const remainingMs = () => request.deadlineAt === void 0 ? QUERY_REQUEST_TIMEOUT_MS : Math.max(0, request.deadlineAt - Date.now());
|
|
10337
|
+
const timeout = setTimeout(
|
|
10338
|
+
() => controller.abort(),
|
|
10339
|
+
Math.max(1, Math.min(QUERY_REQUEST_TIMEOUT_MS, remainingMs()))
|
|
10340
|
+
);
|
|
10341
|
+
try {
|
|
10342
|
+
response = await runWithTracingSuppressed(
|
|
10343
|
+
() => fetch(url, {
|
|
10344
|
+
method: request.method,
|
|
10345
|
+
headers,
|
|
10346
|
+
body,
|
|
10347
|
+
signal: controller.signal,
|
|
10348
|
+
redirect: "error"
|
|
10349
|
+
})
|
|
10350
|
+
);
|
|
10351
|
+
text = await response.text();
|
|
10352
|
+
} catch (cause) {
|
|
10353
|
+
if (retry >= maxRetries || remainingMs() <= 0) throw cause;
|
|
10354
|
+
const retryMs = EVAL_RETRY_BASE_DELAY_MS * 2 ** retry;
|
|
10355
|
+
const budgetMs = remainingMs();
|
|
10356
|
+
await sleep(Math.min(retryMs, budgetMs));
|
|
10357
|
+
if (retryMs >= budgetMs || remainingMs() <= 0) throw cause;
|
|
10358
|
+
continue;
|
|
10359
|
+
} finally {
|
|
10360
|
+
clearTimeout(timeout);
|
|
10361
|
+
}
|
|
10362
|
+
if (!response.ok) {
|
|
10363
|
+
const error = new Error(
|
|
10364
|
+
`Raindrop API ${request.method} ${request.path}: ${response.status} ${text}`
|
|
10365
|
+
);
|
|
10366
|
+
if (retry >= maxRetries || !isRetryableStatus(response.status) || remainingMs() <= 0)
|
|
10367
|
+
throw error;
|
|
10368
|
+
const retryMs = retryDelayMs(response, retry);
|
|
10369
|
+
const budgetMs = remainingMs();
|
|
10370
|
+
await sleep(Math.min(retryMs, budgetMs));
|
|
10371
|
+
if (retryMs >= budgetMs || remainingMs() <= 0) throw error;
|
|
10372
|
+
continue;
|
|
10373
|
+
}
|
|
10374
|
+
let json;
|
|
10375
|
+
try {
|
|
10376
|
+
json = JSON.parse(text);
|
|
10377
|
+
} catch (e) {
|
|
10378
|
+
throw new Error(`Raindrop API ${request.method} ${request.path} returned invalid JSON`);
|
|
10379
|
+
}
|
|
10380
|
+
const parsed = request.schema.safeParse(json);
|
|
10381
|
+
if (!parsed.success) {
|
|
10382
|
+
throw new Error(
|
|
10383
|
+
`Raindrop API ${request.method} ${request.path} returned an invalid response: ${parsed.error.message}`
|
|
10384
|
+
);
|
|
10385
|
+
}
|
|
10386
|
+
return parsed.data;
|
|
10387
|
+
}
|
|
10388
|
+
}
|
|
10389
|
+
var ReplayStatusSchema = z3.enum(["queued", "running", "complete", "failed"]);
|
|
10390
|
+
var ReplayRowStatusSchema = z3.enum(["pending", "done", "missing"]);
|
|
10391
|
+
var WireReplaySchema = z3.object({
|
|
10392
|
+
id: z3.string(),
|
|
10393
|
+
status: ReplayStatusSchema,
|
|
10394
|
+
counts: ReplayCountsSchema,
|
|
10395
|
+
traces_expire_at: z3.string(),
|
|
10396
|
+
traces_expired: z3.boolean()
|
|
10397
|
+
});
|
|
10398
|
+
var WireRowSchema = z3.object({
|
|
10399
|
+
row_id: z3.string(),
|
|
10400
|
+
attempt: z3.number().int().nonnegative(),
|
|
10401
|
+
status: ReplayRowStatusSchema,
|
|
10402
|
+
trace_id: z3.string().nullable(),
|
|
10403
|
+
error: z3.string().nullable()
|
|
10404
|
+
});
|
|
10405
|
+
var WirePlanSchema = z3.object({
|
|
10406
|
+
replay: WireReplaySchema,
|
|
10407
|
+
ingest_url: z3.string().min(1).nullable(),
|
|
10408
|
+
rows: z3.array(
|
|
10409
|
+
ReplayRowSchema.omit({ id: true }).extend({
|
|
10410
|
+
row_id: z3.string(),
|
|
10411
|
+
attempt: z3.literal(0),
|
|
10412
|
+
correlation_id: z3.string()
|
|
10413
|
+
})
|
|
10414
|
+
)
|
|
10415
|
+
});
|
|
10416
|
+
var WireReplayDetailSchema = z3.object({
|
|
10417
|
+
replay: WireReplaySchema,
|
|
10418
|
+
rows: z3.array(WireRowSchema)
|
|
10419
|
+
});
|
|
10420
|
+
var WireReportSchema = z3.object({ status: z3.string() });
|
|
10421
|
+
var WireAttemptStartedSchema = z3.object({
|
|
10422
|
+
status: z3.literal("pending"),
|
|
10423
|
+
row: z3.object({
|
|
10424
|
+
row_id: z3.string().min(1),
|
|
10425
|
+
attempt: z3.number().int().positive(),
|
|
10426
|
+
correlation_id: z3.string().min(1)
|
|
10427
|
+
})
|
|
10428
|
+
});
|
|
10429
|
+
var WireLocalEvaluatorSchema = z3.object({
|
|
10430
|
+
data: z3.object({
|
|
10431
|
+
id: z3.string().uuid(),
|
|
10432
|
+
slug: z3.string(),
|
|
10433
|
+
name: z3.string(),
|
|
10434
|
+
output: z3.enum(["boolean", "score", "number"]),
|
|
10435
|
+
kind: z3.literal("local"),
|
|
10436
|
+
programVersion: z3.number().int().positive()
|
|
10437
|
+
})
|
|
10438
|
+
});
|
|
10439
|
+
var WireStartedEvalRunSchema = z3.object({
|
|
10440
|
+
data: z3.object({
|
|
10441
|
+
runId: z3.string(),
|
|
10442
|
+
status: z3.literal("running"),
|
|
10443
|
+
url: z3.string(),
|
|
10444
|
+
evalId: z3.string().uuid().optional(),
|
|
10445
|
+
programVersion: z3.number().int().positive().optional()
|
|
10446
|
+
})
|
|
10447
|
+
});
|
|
10448
|
+
var WireEvalRunSchema = z3.object({
|
|
10449
|
+
data: z3.object({
|
|
10450
|
+
id: z3.string(),
|
|
10451
|
+
evalSlug: z3.string(),
|
|
10452
|
+
status: z3.enum(["running", "completed", "failed"]),
|
|
10453
|
+
summary: ReplayEvaluatorSummarySchema.nullable(),
|
|
10454
|
+
error: z3.string().nullable(),
|
|
10455
|
+
traces: z3.array(
|
|
10456
|
+
z3.union([
|
|
10457
|
+
z3.object({
|
|
10458
|
+
state: z3.literal("graded"),
|
|
10459
|
+
traceId: z3.string(),
|
|
10460
|
+
pass: z3.boolean(),
|
|
10461
|
+
note: z3.string().optional()
|
|
10462
|
+
}),
|
|
10463
|
+
z3.object({
|
|
10464
|
+
state: z3.literal("graded"),
|
|
10465
|
+
traceId: z3.string(),
|
|
10466
|
+
score: EvalScoreSchema,
|
|
10467
|
+
note: z3.string().optional()
|
|
10468
|
+
}),
|
|
10469
|
+
z3.object({
|
|
10470
|
+
state: z3.literal("graded"),
|
|
10471
|
+
traceId: z3.string(),
|
|
10472
|
+
value: z3.number().finite(),
|
|
10473
|
+
note: z3.string().optional()
|
|
10474
|
+
}),
|
|
10475
|
+
z3.object({ state: z3.literal("errored"), traceId: z3.string(), message: z3.string() }),
|
|
10476
|
+
z3.object({
|
|
10477
|
+
state: z3.literal("ungraded"),
|
|
10478
|
+
traceId: z3.string(),
|
|
10479
|
+
reason: z3.enum(["not sampled", "trace unavailable", "content unreadable"])
|
|
10480
|
+
})
|
|
10481
|
+
])
|
|
10482
|
+
)
|
|
10483
|
+
})
|
|
10484
|
+
});
|
|
10485
|
+
var WireLocalEvalRunSchema = z3.object({
|
|
10486
|
+
data: WireEvalRunSchema.shape.data.extend({ status: z3.literal("completed"), url: z3.string() })
|
|
10487
|
+
});
|
|
10488
|
+
var WireLocalEvalRunStartedSchema = z3.object({
|
|
10489
|
+
data: WireEvalRunSchema.shape.data.omit({ traces: true }).extend({ status: z3.literal("running"), url: z3.string() })
|
|
10490
|
+
});
|
|
10491
|
+
var WireLocalEvalAttemptSchema = z3.object({
|
|
10492
|
+
data: z3.object({
|
|
10493
|
+
runId: z3.string().uuid(),
|
|
10494
|
+
requestId: z3.string().uuid(),
|
|
10495
|
+
rowId: z3.string().min(1),
|
|
10496
|
+
attempt: z3.number().int().nonnegative(),
|
|
10497
|
+
traceId: z3.string().regex(/^[a-f0-9]{32}$/),
|
|
10498
|
+
outcome: WireEvalRunSchema.shape.data.shape.traces.element
|
|
10499
|
+
})
|
|
10500
|
+
});
|
|
10501
|
+
var WireEvalCaseTracePairReadSchema = z3.discriminatedUnion("state", [
|
|
10502
|
+
EvalCaseTracePairSchema.extend({
|
|
10503
|
+
state: z3.literal("ready"),
|
|
10504
|
+
attempt: z3.number().int().nonnegative(),
|
|
10505
|
+
datasetVersion: z3.object({
|
|
10506
|
+
id: z3.string().uuid(),
|
|
10507
|
+
fingerprint: EvalDatasetFingerprintSchema
|
|
10508
|
+
})
|
|
10509
|
+
}),
|
|
10510
|
+
z3.object({
|
|
10511
|
+
state: z3.literal("pending"),
|
|
10512
|
+
caseId: z3.string().min(1),
|
|
10513
|
+
attempt: z3.number().int().nonnegative()
|
|
10514
|
+
}),
|
|
10515
|
+
z3.object({
|
|
10516
|
+
state: z3.literal("missing"),
|
|
10517
|
+
caseId: z3.string().min(1),
|
|
10518
|
+
attempt: z3.number().int().nonnegative(),
|
|
10519
|
+
reason: z3.string()
|
|
10520
|
+
}),
|
|
10521
|
+
z3.object({
|
|
10522
|
+
state: z3.literal("expired"),
|
|
10523
|
+
caseId: z3.string().min(1),
|
|
10524
|
+
attempt: z3.number().int().nonnegative(),
|
|
10525
|
+
expiredAt: z3.string().datetime({ offset: true })
|
|
10526
|
+
}),
|
|
10527
|
+
z3.object({
|
|
10528
|
+
state: z3.literal("errored"),
|
|
10529
|
+
caseId: z3.string().min(1),
|
|
10530
|
+
attempt: z3.number().int().nonnegative(),
|
|
10531
|
+
reason: z3.string()
|
|
10532
|
+
})
|
|
10533
|
+
]);
|
|
10534
|
+
function toReading(wire) {
|
|
10067
10535
|
return {
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
},
|
|
10081
|
-
required: ["category", "detail"]
|
|
10536
|
+
replayId: wire.replay.id,
|
|
10537
|
+
status: wire.replay.status,
|
|
10538
|
+
counts: wire.replay.counts,
|
|
10539
|
+
rows: wire.rows.map((row) => ({
|
|
10540
|
+
rowId: row.row_id,
|
|
10541
|
+
attempt: row.attempt,
|
|
10542
|
+
status: row.status,
|
|
10543
|
+
traceId: row.trace_id,
|
|
10544
|
+
error: row.error
|
|
10545
|
+
})),
|
|
10546
|
+
tracesExpireAt: wire.replay.traces_expire_at,
|
|
10547
|
+
tracesExpired: wire.replay.traces_expired
|
|
10082
10548
|
};
|
|
10083
10549
|
}
|
|
10084
|
-
function
|
|
10085
|
-
|
|
10086
|
-
const
|
|
10087
|
-
|
|
10550
|
+
async function createReplay(client, options) {
|
|
10551
|
+
var _a, _b, _c, _d;
|
|
10552
|
+
const queryUrl = (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL;
|
|
10553
|
+
const manifest = typeof options.dataset === "string" ? await readEvalDataset(client, options.dataset, { queryUrl }) : validateEvalDatasetManifest(options.dataset);
|
|
10554
|
+
const rowIds = selectEvalDatasetRows(manifest, options.rowIds);
|
|
10555
|
+
const commit = readGitCommit();
|
|
10556
|
+
const created = await queryApi({
|
|
10557
|
+
client,
|
|
10558
|
+
queryUrl,
|
|
10559
|
+
path: "/v1/replays",
|
|
10560
|
+
method: "POST",
|
|
10561
|
+
schema: WirePlanSchema,
|
|
10562
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES,
|
|
10563
|
+
body: {
|
|
10564
|
+
dataset_slug: manifest.dataset.slug,
|
|
10565
|
+
dataset_version_id: manifest.version.id,
|
|
10566
|
+
dataset_fingerprint: manifest.version.fingerprint,
|
|
10567
|
+
request_key: randomUUID(),
|
|
10568
|
+
commit_sha: (_b = commit == null ? void 0 : commit.sha) != null ? _b : null,
|
|
10569
|
+
commit_branch: (_c = commit == null ? void 0 : commit.branch) != null ? _c : null,
|
|
10570
|
+
commit_dirty: (_d = commit == null ? void 0 : commit.dirty) != null ? _d : null,
|
|
10571
|
+
row_ids: rowIds
|
|
10572
|
+
}
|
|
10573
|
+
});
|
|
10574
|
+
const handle = {
|
|
10575
|
+
...toHandle(created),
|
|
10576
|
+
datasetVersion: {
|
|
10577
|
+
id: manifest.version.id,
|
|
10578
|
+
fingerprint: manifest.version.fingerprint
|
|
10579
|
+
}
|
|
10580
|
+
};
|
|
10581
|
+
assertSelectedRows(handle, rowIds);
|
|
10582
|
+
return handle;
|
|
10088
10583
|
}
|
|
10089
|
-
function
|
|
10090
|
-
|
|
10091
|
-
if (
|
|
10092
|
-
|
|
10584
|
+
function selectEvalDatasetRows(manifest, requestedRowIds) {
|
|
10585
|
+
const rowIds = requestedRowIds != null ? requestedRowIds : manifest.cases.map((entry) => entry.id);
|
|
10586
|
+
if (rowIds.length === 0) throw new Error("Raindrop replay rowIds cannot be empty");
|
|
10587
|
+
if (rowIds.some((rowId) => rowId.length === 0)) {
|
|
10588
|
+
throw new Error("Raindrop replay rowIds cannot contain an empty id");
|
|
10589
|
+
}
|
|
10590
|
+
if (new Set(rowIds).size !== rowIds.length) {
|
|
10591
|
+
throw new Error("Raindrop replay rowIds must be unique");
|
|
10592
|
+
}
|
|
10593
|
+
const selected = new Set(rowIds);
|
|
10594
|
+
const ordered = manifest.cases.flatMap((entry) => selected.has(entry.id) ? [entry.id] : []);
|
|
10595
|
+
if (ordered.length !== selected.size) {
|
|
10596
|
+
const available = new Set(manifest.cases.map((entry) => entry.id));
|
|
10597
|
+
const unknown = rowIds.find((rowId) => !available.has(rowId));
|
|
10598
|
+
throw new Error(`Raindrop replay rowIds contains unknown case ${unknown != null ? unknown : "unknown"}`);
|
|
10599
|
+
}
|
|
10600
|
+
return ordered;
|
|
10093
10601
|
}
|
|
10094
|
-
|
|
10095
|
-
|
|
10602
|
+
function assertSelectedRows(handle, requestedRowIds) {
|
|
10603
|
+
const returnedRowIds = handle.rows.map((row) => row.id);
|
|
10604
|
+
const requested = new Set(requestedRowIds);
|
|
10605
|
+
const returned = new Set(returnedRowIds);
|
|
10606
|
+
const matches = returned.size === returnedRowIds.length && requested.size === returned.size && requestedRowIds.every((rowId, index) => returnedRowIds[index] === rowId);
|
|
10607
|
+
if (!matches) {
|
|
10608
|
+
throw new Error(
|
|
10609
|
+
`Raindrop replay ${handle.replayId} returned rows that do not match the manifest selection`
|
|
10610
|
+
);
|
|
10611
|
+
}
|
|
10096
10612
|
}
|
|
10097
|
-
function
|
|
10613
|
+
function toHandle(wire) {
|
|
10614
|
+
return {
|
|
10615
|
+
replayId: wire.replay.id,
|
|
10616
|
+
ingestUrl: wire.ingest_url,
|
|
10617
|
+
rows: wire.rows.map((row) => ({
|
|
10618
|
+
id: row.row_id,
|
|
10619
|
+
attempt: row.attempt,
|
|
10620
|
+
correlationId: row.correlation_id,
|
|
10621
|
+
name: row.name,
|
|
10622
|
+
input: row.input,
|
|
10623
|
+
output: row.output,
|
|
10624
|
+
properties: row.properties
|
|
10625
|
+
}))
|
|
10626
|
+
};
|
|
10627
|
+
}
|
|
10628
|
+
async function claimReplay(client, replayId, options) {
|
|
10098
10629
|
var _a;
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
return void 0;
|
|
10630
|
+
return toHandle(
|
|
10631
|
+
await queryApi({
|
|
10632
|
+
client,
|
|
10633
|
+
queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
|
|
10634
|
+
path: `/v1/replays/${encodeURIComponent(replayId)}/claim`,
|
|
10635
|
+
method: "POST",
|
|
10636
|
+
schema: WirePlanSchema
|
|
10637
|
+
})
|
|
10638
|
+
);
|
|
10109
10639
|
}
|
|
10110
|
-
function
|
|
10111
|
-
|
|
10112
|
-
|
|
10640
|
+
async function readReplay(client, replayId, options) {
|
|
10641
|
+
var _a;
|
|
10642
|
+
return toReading(
|
|
10643
|
+
await queryApi({
|
|
10644
|
+
client,
|
|
10645
|
+
queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
|
|
10646
|
+
path: `/v1/replays/${encodeURIComponent(replayId)}`,
|
|
10647
|
+
deadlineAt: options == null ? void 0 : options.deadlineAt,
|
|
10648
|
+
method: "GET",
|
|
10649
|
+
schema: WireReplayDetailSchema
|
|
10650
|
+
})
|
|
10651
|
+
);
|
|
10652
|
+
}
|
|
10653
|
+
async function readEvalManifest(client, dataset, options) {
|
|
10654
|
+
var _a;
|
|
10655
|
+
const normalized = dataset.trim();
|
|
10656
|
+
if (normalized.length === 0) throw new Error("Raindrop eval manifest dataset cannot be empty");
|
|
10657
|
+
const manifest = await queryApi({
|
|
10658
|
+
client,
|
|
10659
|
+
queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
|
|
10660
|
+
path: `/v1/replays/manifest?dataset=${encodeURIComponent(normalized)}`,
|
|
10661
|
+
method: "GET",
|
|
10662
|
+
schema: WireEvalManifestSchema
|
|
10663
|
+
});
|
|
10664
|
+
const caseIds = manifest.rows.map((row) => row.id);
|
|
10665
|
+
if (new Set(caseIds).size !== caseIds.length) {
|
|
10666
|
+
throw new Error(`Raindrop eval manifest for ${normalized} contains duplicate case ids`);
|
|
10113
10667
|
}
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10668
|
+
return {
|
|
10669
|
+
datasetId: manifest.dataset_id,
|
|
10670
|
+
fingerprint: manifest.fingerprint,
|
|
10671
|
+
cases: manifest.rows
|
|
10672
|
+
};
|
|
10118
10673
|
}
|
|
10119
|
-
async function
|
|
10674
|
+
async function readEvalDataset(client, dataset, options) {
|
|
10120
10675
|
var _a;
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
const isLast = attemptNumber === maxAttempts;
|
|
10139
|
-
if (debugLogs) {
|
|
10140
|
-
console.warn(
|
|
10141
|
-
`[raindrop] ${opName} attempt ${attemptNumber} failed: ${(_a = err == null ? void 0 : err.message) != null ? _a : err}. ${isLast ? "No more retries." : "Will retry."}`
|
|
10142
|
-
);
|
|
10143
|
-
}
|
|
10144
|
-
if (isLast) break;
|
|
10145
|
-
}
|
|
10676
|
+
const slug = EvalDatasetSlugSchema.parse(dataset.trim());
|
|
10677
|
+
const versionId = options == null ? void 0 : options.versionId;
|
|
10678
|
+
if (versionId !== void 0 && !z3.string().uuid().safeParse(versionId).success) {
|
|
10679
|
+
throw new Error("Raindrop eval dataset versionId must be a UUID");
|
|
10680
|
+
}
|
|
10681
|
+
const path = versionId ? `/v1/eval-datasets/${encodeURIComponent(slug)}/versions/${encodeURIComponent(versionId)}` : `/v1/eval-datasets/${encodeURIComponent(slug)}`;
|
|
10682
|
+
const manifest = validateEvalDatasetManifest(
|
|
10683
|
+
await queryApi({
|
|
10684
|
+
client,
|
|
10685
|
+
queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
|
|
10686
|
+
path,
|
|
10687
|
+
method: "GET",
|
|
10688
|
+
schema: EvalDatasetManifestSchema
|
|
10689
|
+
})
|
|
10690
|
+
);
|
|
10691
|
+
if (manifest.dataset.slug !== slug) {
|
|
10692
|
+
throw new Error(`Raindrop eval dataset ${slug} returned mismatched dataset identity`);
|
|
10146
10693
|
}
|
|
10147
|
-
|
|
10694
|
+
if (versionId !== void 0 && manifest.version.id !== versionId) {
|
|
10695
|
+
throw new Error(`Raindrop eval dataset ${slug} returned mismatched version identity`);
|
|
10696
|
+
}
|
|
10697
|
+
return manifest;
|
|
10148
10698
|
}
|
|
10149
|
-
function
|
|
10150
|
-
|
|
10699
|
+
function validateEvalDatasetManifest(value) {
|
|
10700
|
+
const manifest = EvalDatasetManifestSchema.parse(value);
|
|
10701
|
+
if (manifest.dataset.id !== manifest.version.datasetId) {
|
|
10702
|
+
throw new Error("Raindrop eval dataset and version identities do not match");
|
|
10703
|
+
}
|
|
10704
|
+
const caseIds = manifest.cases.map((entry) => entry.id);
|
|
10705
|
+
if (new Set(caseIds).size !== caseIds.length) {
|
|
10706
|
+
throw new Error(`Raindrop eval dataset ${manifest.dataset.slug} contains duplicate case ids`);
|
|
10707
|
+
}
|
|
10708
|
+
return manifest;
|
|
10151
10709
|
}
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10710
|
+
async function runReplay(client, handle, options) {
|
|
10711
|
+
return (await runReplayRows(client, handle, options)).reading;
|
|
10712
|
+
}
|
|
10713
|
+
async function runReplayRows(client, handle, options) {
|
|
10714
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
10715
|
+
if (!Number.isSafeInteger((_a = options.concurrency) != null ? _a : DEFAULT_CONCURRENCY) || ((_b = options.concurrency) != null ? _b : DEFAULT_CONCURRENCY) < 1) {
|
|
10716
|
+
throw new Error("Raindrop replay concurrency must be a positive safe integer");
|
|
10717
|
+
}
|
|
10718
|
+
validateWait("traceWaitMs", (_c = options.traceWaitMs) != null ? _c : DEFAULT_TRACE_WAIT_MS, true);
|
|
10719
|
+
const ingestUrl = handle.ingestUrl && replayIngestEndpoint(handle.ingestUrl, options.replayIngestUrl);
|
|
10720
|
+
if (!ingestUrl) {
|
|
10721
|
+
throw new Error(
|
|
10722
|
+
`Raindrop replay ${handle.replayId} has no ingest URL; create or claim it with this runner before running it`
|
|
10723
|
+
);
|
|
10724
|
+
}
|
|
10725
|
+
const queryUrl = (_d = options.queryUrl) != null ? _d : DEFAULT_QUERY_URL;
|
|
10726
|
+
const report = (body) => queryApi({
|
|
10727
|
+
client,
|
|
10728
|
+
queryUrl,
|
|
10729
|
+
path: `/v1/replays/${encodeURIComponent(handle.replayId)}/report`,
|
|
10730
|
+
method: "POST",
|
|
10731
|
+
schema: WireReportSchema,
|
|
10732
|
+
body,
|
|
10733
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES
|
|
10734
|
+
});
|
|
10735
|
+
await report({ type: "started" });
|
|
10736
|
+
const rows = [...handle.rows];
|
|
10737
|
+
const results = /* @__PURE__ */ new Map();
|
|
10738
|
+
const captures = /* @__PURE__ */ new Map();
|
|
10739
|
+
let next = 0;
|
|
10740
|
+
const worker = async () => {
|
|
10741
|
+
var _a2, _b2;
|
|
10742
|
+
while (next < rows.length) {
|
|
10743
|
+
const row = rows[next++];
|
|
10744
|
+
if (!row) return;
|
|
10745
|
+
const spanIds = /* @__PURE__ */ new Set();
|
|
10746
|
+
const traceIds = /* @__PURE__ */ new Set();
|
|
10747
|
+
captures.set(row.id, { spanIds, traceIds });
|
|
10748
|
+
try {
|
|
10749
|
+
const { correlationId: _attribution, attempt: _attempt, ...visible } = row;
|
|
10750
|
+
const replayDestination = {
|
|
10751
|
+
kind: "raindrop",
|
|
10752
|
+
url: ingestUrl,
|
|
10753
|
+
replayId: handle.replayId,
|
|
10754
|
+
apiKey: (_a2 = client.connection.apiKey) != null ? _a2 : "",
|
|
10755
|
+
projectId: client.connection.projectId,
|
|
10756
|
+
exportedSpanIds: spanIds,
|
|
10757
|
+
exportedTraceIds: traceIds
|
|
10758
|
+
};
|
|
10759
|
+
const result = await context.with(
|
|
10760
|
+
trace.deleteSpan(context.active()),
|
|
10761
|
+
() => withReplayTraceDestination(
|
|
10762
|
+
replayDestination,
|
|
10763
|
+
() => withEvalScope(
|
|
10764
|
+
{ correlationId: row.correlationId },
|
|
10765
|
+
() => withTask(
|
|
10766
|
+
{ name: "raindrop.replay.row", inputParameters: [visible], traceContent: true },
|
|
10767
|
+
async () => {
|
|
10768
|
+
var _a3;
|
|
10769
|
+
const parent = (_a3 = trace.getSpan(context.active())) == null ? void 0 : _a3.spanContext();
|
|
10770
|
+
if (!parent) return options.run(visible);
|
|
10771
|
+
return withReplayTraceDestination({
|
|
10772
|
+
...replayDestination,
|
|
10773
|
+
parentSpanContext: {
|
|
10774
|
+
traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
|
|
10775
|
+
spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
|
|
10776
|
+
eventId: parent.traceId
|
|
10777
|
+
}
|
|
10778
|
+
}, () => options.run(visible));
|
|
10779
|
+
}
|
|
10780
|
+
)
|
|
10781
|
+
)
|
|
10782
|
+
)
|
|
10783
|
+
);
|
|
10784
|
+
results.set(row.id, { value: result });
|
|
10785
|
+
} catch (cause) {
|
|
10786
|
+
const error = describeError(cause);
|
|
10787
|
+
await report({
|
|
10788
|
+
type: "row_missing",
|
|
10789
|
+
row_id: row.id,
|
|
10790
|
+
attempt: row.attempt,
|
|
10791
|
+
error
|
|
10792
|
+
});
|
|
10793
|
+
await client.forceFlush();
|
|
10794
|
+
try {
|
|
10795
|
+
await waitForFailedTraceAssociation({
|
|
10796
|
+
client,
|
|
10797
|
+
queryUrl,
|
|
10798
|
+
replayId: handle.replayId,
|
|
10799
|
+
rowId: row.id,
|
|
10800
|
+
attempt: row.attempt,
|
|
10801
|
+
traceIds,
|
|
10802
|
+
spanIds,
|
|
10803
|
+
waitMs: (_b2 = options.traceWaitMs) != null ? _b2 : DEFAULT_TRACE_WAIT_MS
|
|
10804
|
+
});
|
|
10805
|
+
} catch (retentionCause) {
|
|
10806
|
+
throw failedTraceRetentionError(cause, retentionCause, row.id);
|
|
10807
|
+
}
|
|
10808
|
+
captures.delete(row.id);
|
|
10809
|
+
}
|
|
10810
|
+
}
|
|
10811
|
+
};
|
|
10812
|
+
await Promise.all(
|
|
10813
|
+
Array.from(
|
|
10814
|
+
{ length: Math.min((_e = options.concurrency) != null ? _e : DEFAULT_CONCURRENCY, rows.length) },
|
|
10815
|
+
() => worker()
|
|
10816
|
+
)
|
|
10817
|
+
);
|
|
10818
|
+
await client.forceFlush();
|
|
10819
|
+
const traceEvidence = [];
|
|
10820
|
+
for (const row of rows) {
|
|
10821
|
+
const capture = captures.get(row.id);
|
|
10822
|
+
if (!capture) continue;
|
|
10823
|
+
const traceIds = [...capture.traceIds];
|
|
10824
|
+
const spanIds = [...capture.spanIds];
|
|
10825
|
+
if (traceIds.length !== 1 || spanIds.length === 0 || spanIds.length > 500) {
|
|
10826
|
+
const detail = traceIds.length !== 1 ? `expected one trace, captured ${traceIds.length}` : spanIds.length === 0 ? "captured no ended spans" : `captured ${spanIds.length} spans, exceeding the 500 span limit`;
|
|
10827
|
+
await report({ type: "row_missing", row_id: row.id, attempt: row.attempt, error: detail });
|
|
10828
|
+
continue;
|
|
10829
|
+
}
|
|
10830
|
+
const traceId = traceIds[0];
|
|
10831
|
+
if (traceId === void 0) {
|
|
10832
|
+
throw new Error(`Raindrop replay ${handle.replayId} lost trace evidence for ${row.id}`);
|
|
10833
|
+
}
|
|
10834
|
+
traceEvidence.push({ rowId: row.id, traceId, spanIds });
|
|
10835
|
+
}
|
|
10836
|
+
const settled = await waitForTraces({
|
|
10837
|
+
client,
|
|
10838
|
+
queryUrl,
|
|
10839
|
+
replayId: handle.replayId,
|
|
10840
|
+
deadlineAt: Date.now() + ((_f = options.traceWaitMs) != null ? _f : DEFAULT_TRACE_WAIT_MS)
|
|
10841
|
+
});
|
|
10842
|
+
for (const row of settled.rows) {
|
|
10843
|
+
if (row.status !== "pending") continue;
|
|
10844
|
+
await report({
|
|
10845
|
+
type: "row_missing",
|
|
10846
|
+
row_id: row.rowId,
|
|
10847
|
+
attempt: row.attempt,
|
|
10848
|
+
error: `no trace arrived for this row within ${Math.round(
|
|
10849
|
+
((_g = options.traceWaitMs) != null ? _g : DEFAULT_TRACE_WAIT_MS) / 1e3
|
|
10850
|
+
)}s`
|
|
10851
|
+
});
|
|
10852
|
+
}
|
|
10853
|
+
await report({ type: "finished" });
|
|
10854
|
+
return {
|
|
10855
|
+
reading: await readReplay(client, handle.replayId, { queryUrl }),
|
|
10856
|
+
results,
|
|
10857
|
+
traceEvidence: ReplayTraceEvidenceSchema.parse(traceEvidence)
|
|
10858
|
+
};
|
|
10859
|
+
}
|
|
10860
|
+
async function readEvalCaseTracePairs(input) {
|
|
10861
|
+
const deadlineAt = Date.now() + input.waitMs;
|
|
10862
|
+
const entries = await Promise.all(
|
|
10863
|
+
input.evidence.map(async (evidence) => {
|
|
10864
|
+
var _a, _b, _c;
|
|
10865
|
+
let reading = await readEvalCaseTracePair(
|
|
10866
|
+
{ ...input, deadlineAt },
|
|
10867
|
+
evidence,
|
|
10868
|
+
(_a = input.attempt) != null ? _a : 0
|
|
10869
|
+
);
|
|
10870
|
+
while (reading.state === "pending" && Date.now() < deadlineAt) {
|
|
10871
|
+
await sleep(Math.min(TRACE_POLL_INTERVAL_MS, Math.max(0, deadlineAt - Date.now())));
|
|
10872
|
+
if (Date.now() >= deadlineAt) break;
|
|
10873
|
+
reading = await readEvalCaseTracePair({ ...input, deadlineAt }, evidence, (_b = input.attempt) != null ? _b : 0);
|
|
10874
|
+
}
|
|
10875
|
+
if (reading.state !== "ready") {
|
|
10876
|
+
const detail = reading.state === "pending" ? `did not become complete within ${input.waitMs}ms` : reading.state === "expired" ? `expired at ${reading.expiredAt}` : reading.reason;
|
|
10877
|
+
throw new Error(
|
|
10878
|
+
`Raindrop replay ${input.replayId} trace pair for ${evidence.rowId} ${detail}`
|
|
10879
|
+
);
|
|
10880
|
+
}
|
|
10881
|
+
if (reading.caseId !== evidence.rowId) {
|
|
10882
|
+
throw new Error(
|
|
10883
|
+
`Raindrop replay ${input.replayId} returned a trace pair for the wrong case`
|
|
10884
|
+
);
|
|
10885
|
+
}
|
|
10886
|
+
if (reading.attempt !== ((_c = input.attempt) != null ? _c : 0)) {
|
|
10887
|
+
throw new Error(
|
|
10888
|
+
`Raindrop replay ${input.replayId} returned a trace pair for the wrong attempt`
|
|
10889
|
+
);
|
|
10890
|
+
}
|
|
10891
|
+
if (input.datasetVersion && (reading.datasetVersion.id !== input.datasetVersion.id || reading.datasetVersion.fingerprint !== input.datasetVersion.fingerprint)) {
|
|
10892
|
+
throw new Error(
|
|
10893
|
+
`Raindrop replay ${input.replayId} returned a trace pair from the wrong dataset version`
|
|
10894
|
+
);
|
|
10895
|
+
}
|
|
10896
|
+
return [evidence.rowId, reading];
|
|
10897
|
+
})
|
|
10898
|
+
);
|
|
10899
|
+
return new Map(entries);
|
|
10900
|
+
}
|
|
10901
|
+
async function readEvalCaseTracePair(input, evidence, attempt) {
|
|
10902
|
+
const query = new URLSearchParams({
|
|
10903
|
+
row_id: evidence.rowId,
|
|
10904
|
+
attempt: String(attempt),
|
|
10905
|
+
expected_trace_id: evidence.traceId
|
|
10906
|
+
});
|
|
10907
|
+
for (const spanId of evidence.spanIds) query.append("expected_span_id", spanId);
|
|
10908
|
+
return queryApi({
|
|
10909
|
+
client: input.client,
|
|
10910
|
+
queryUrl: input.queryUrl,
|
|
10911
|
+
path: `/v1/replays/${encodeURIComponent(input.replayId)}/trace?${query.toString()}`,
|
|
10912
|
+
method: "GET",
|
|
10913
|
+
schema: WireEvalCaseTracePairReadSchema,
|
|
10914
|
+
deadlineAt: input.deadlineAt,
|
|
10915
|
+
maxRetries: MAX_EVAL_STATUS_RETRIES
|
|
10916
|
+
});
|
|
10917
|
+
}
|
|
10918
|
+
async function createLocalEvalReplaySession(client, options) {
|
|
10919
|
+
var _a;
|
|
10920
|
+
if (options.evaluators.length === 0) {
|
|
10921
|
+
throw new Error("Raindrop local eval replay requires at least one evaluator");
|
|
10922
|
+
}
|
|
10923
|
+
const queryUrl = (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL;
|
|
10924
|
+
const handle = await createReplay(client, {
|
|
10925
|
+
dataset: options.dataset,
|
|
10926
|
+
rowIds: options.rowIds,
|
|
10927
|
+
queryUrl
|
|
10928
|
+
});
|
|
10929
|
+
const validatedHandle = {
|
|
10930
|
+
...handle,
|
|
10931
|
+
ingestUrl: replayIngestEndpoint(requireReplayIngestUrl(handle), options.replayIngestUrl)
|
|
10932
|
+
};
|
|
10933
|
+
await reportReplay(client, queryUrl, handle.replayId, { type: "started" });
|
|
10934
|
+
const parents = await Promise.all(
|
|
10935
|
+
options.evaluators.map(async (evaluator) => {
|
|
10936
|
+
const registered = await registerLocalEvaluator(client, evaluator, queryUrl);
|
|
10937
|
+
const runId = randomUUID();
|
|
10938
|
+
const started = await queryApi({
|
|
10939
|
+
client,
|
|
10940
|
+
queryUrl,
|
|
10941
|
+
path: `/v1/evals/${encodeURIComponent(evaluator.slug)}/runs/local`,
|
|
10942
|
+
method: "POST",
|
|
10943
|
+
schema: WireLocalEvalRunStartedSchema,
|
|
10944
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES,
|
|
10945
|
+
body: {
|
|
10946
|
+
type: "started",
|
|
10947
|
+
runId,
|
|
10948
|
+
expectedEvalId: registered.data.id,
|
|
10949
|
+
expectedProgramVersion: registered.data.programVersion,
|
|
10950
|
+
source: { replay: handle.replayId }
|
|
10951
|
+
}
|
|
10952
|
+
});
|
|
10953
|
+
if (started.data.id !== runId) {
|
|
10954
|
+
throw new Error(`Raindrop local evaluator ${evaluator.slug} returned the wrong run id`);
|
|
10955
|
+
}
|
|
10956
|
+
return {
|
|
10957
|
+
evaluator,
|
|
10958
|
+
runId,
|
|
10959
|
+
url: started.data.url
|
|
10960
|
+
};
|
|
10961
|
+
})
|
|
10962
|
+
);
|
|
10963
|
+
return new LocalEvalReplaySessionImpl(client, validatedHandle, parents, queryUrl);
|
|
10964
|
+
}
|
|
10965
|
+
var LocalEvalReplaySessionImpl = class {
|
|
10966
|
+
constructor(client, handle, parents, queryUrl) {
|
|
10967
|
+
this.client = client;
|
|
10968
|
+
this.handle = handle;
|
|
10969
|
+
this.parents = parents;
|
|
10970
|
+
this.queryUrl = queryUrl;
|
|
10971
|
+
this.state = "open";
|
|
10972
|
+
this.replayId = handle.replayId;
|
|
10973
|
+
this.rows = handle.rows.map(
|
|
10974
|
+
({ correlationId: _correlationId, attempt: _attempt, ...row }) => row
|
|
10975
|
+
);
|
|
10976
|
+
}
|
|
10977
|
+
async run(input) {
|
|
10978
|
+
var _a, _b, _c, _d;
|
|
10979
|
+
validateWait("traceWaitMs", (_a = input.traceWaitMs) != null ? _a : DEFAULT_TRACE_WAIT_MS, true);
|
|
10980
|
+
if (this.state !== "open") {
|
|
10981
|
+
throw new Error(`Raindrop replay ${this.replayId} is already finishing`);
|
|
10982
|
+
}
|
|
10983
|
+
if (!Number.isInteger(input.attempt) || input.attempt < 0) {
|
|
10984
|
+
throw new Error("Raindrop replay attempt must be a non-negative integer");
|
|
10985
|
+
}
|
|
10986
|
+
const planned = this.handle.rows.find((row2) => row2.id === input.rowId);
|
|
10987
|
+
if (!planned) throw new Error(`Raindrop replay ${this.replayId} has no row ${input.rowId}`);
|
|
10988
|
+
const correlationId = input.attempt === 0 ? planned.correlationId : await this.allocateRetry(input.rowId, input.attempt);
|
|
10989
|
+
const { correlationId: _correlationId, attempt: _attempt, ...row } = planned;
|
|
10990
|
+
const spanIds = /* @__PURE__ */ new Set();
|
|
10991
|
+
const traceIds = /* @__PURE__ */ new Set();
|
|
10992
|
+
let result;
|
|
10993
|
+
const startedAt = Date.now();
|
|
10994
|
+
const replayDestination = {
|
|
10995
|
+
kind: "raindrop",
|
|
10996
|
+
url: requireReplayIngestUrl(this.handle),
|
|
10997
|
+
replayId: this.replayId,
|
|
10998
|
+
apiKey: (_b = this.client.connection.apiKey) != null ? _b : "",
|
|
10999
|
+
projectId: this.client.connection.projectId,
|
|
11000
|
+
exportedSpanIds: spanIds,
|
|
11001
|
+
exportedTraceIds: traceIds
|
|
11002
|
+
};
|
|
11003
|
+
try {
|
|
11004
|
+
result = await context.with(
|
|
11005
|
+
trace.deleteSpan(context.active()),
|
|
11006
|
+
() => withReplayTraceDestination(
|
|
11007
|
+
replayDestination,
|
|
11008
|
+
() => withEvalScope(
|
|
11009
|
+
{ correlationId },
|
|
11010
|
+
() => withTask(
|
|
11011
|
+
{ name: "raindrop.replay.row", inputParameters: [row], traceContent: true },
|
|
11012
|
+
() => {
|
|
11013
|
+
var _a2;
|
|
11014
|
+
const parent = (_a2 = trace.getSpan(context.active())) == null ? void 0 : _a2.spanContext();
|
|
11015
|
+
if (!parent) return input.run(row);
|
|
11016
|
+
return withReplayTraceDestination({
|
|
11017
|
+
...replayDestination,
|
|
11018
|
+
parentSpanContext: {
|
|
11019
|
+
traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
|
|
11020
|
+
spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
|
|
11021
|
+
eventId: parent.traceId
|
|
11022
|
+
}
|
|
11023
|
+
}, () => input.run(row));
|
|
11024
|
+
}
|
|
11025
|
+
)
|
|
11026
|
+
)
|
|
11027
|
+
)
|
|
11028
|
+
);
|
|
11029
|
+
} catch (cause) {
|
|
11030
|
+
const error = describeError(cause);
|
|
11031
|
+
await reportReplay(this.client, this.queryUrl, this.replayId, {
|
|
11032
|
+
type: "row_missing",
|
|
11033
|
+
row_id: row.id,
|
|
11034
|
+
attempt: input.attempt,
|
|
11035
|
+
error
|
|
11036
|
+
});
|
|
11037
|
+
await this.client.forceFlush();
|
|
11038
|
+
let retainedTraceId;
|
|
11039
|
+
try {
|
|
11040
|
+
retainedTraceId = await waitForFailedTraceAssociation({
|
|
11041
|
+
client: this.client,
|
|
11042
|
+
queryUrl: this.queryUrl,
|
|
11043
|
+
replayId: this.replayId,
|
|
11044
|
+
rowId: row.id,
|
|
11045
|
+
attempt: input.attempt,
|
|
11046
|
+
traceIds,
|
|
11047
|
+
spanIds,
|
|
11048
|
+
waitMs: (_c = input.traceWaitMs) != null ? _c : DEFAULT_TRACE_WAIT_MS
|
|
11049
|
+
});
|
|
11050
|
+
} catch (retentionCause) {
|
|
11051
|
+
throw failedTraceRetentionError(cause, retentionCause, row.id);
|
|
11052
|
+
}
|
|
11053
|
+
return {
|
|
11054
|
+
row,
|
|
11055
|
+
attempt: input.attempt,
|
|
11056
|
+
status: "missing",
|
|
11057
|
+
traceId: retainedTraceId,
|
|
11058
|
+
error,
|
|
11059
|
+
verdicts: []
|
|
11060
|
+
};
|
|
11061
|
+
}
|
|
11062
|
+
await this.client.forceFlush();
|
|
11063
|
+
let pair;
|
|
11064
|
+
let evidence;
|
|
11065
|
+
try {
|
|
11066
|
+
evidence = capturedEvidence(row.id, traceIds, spanIds);
|
|
11067
|
+
const pairs = await readEvalCaseTracePairs({
|
|
11068
|
+
client: this.client,
|
|
11069
|
+
queryUrl: this.queryUrl,
|
|
11070
|
+
replayId: this.replayId,
|
|
11071
|
+
datasetVersion: this.handle.datasetVersion,
|
|
11072
|
+
evidence: [evidence],
|
|
11073
|
+
waitMs: (_d = input.traceWaitMs) != null ? _d : DEFAULT_TRACE_WAIT_MS,
|
|
11074
|
+
attempt: input.attempt
|
|
11075
|
+
});
|
|
11076
|
+
const captured = pairs.get(row.id);
|
|
11077
|
+
if (!captured)
|
|
11078
|
+
throw new Error(`Raindrop replay ${this.replayId} omitted trace pair ${row.id}`);
|
|
11079
|
+
pair = captured;
|
|
11080
|
+
} catch (cause) {
|
|
11081
|
+
const error = describeError(cause);
|
|
11082
|
+
await reportReplay(this.client, this.queryUrl, this.replayId, {
|
|
11083
|
+
type: "row_missing",
|
|
11084
|
+
row_id: row.id,
|
|
11085
|
+
attempt: input.attempt,
|
|
11086
|
+
error
|
|
11087
|
+
});
|
|
11088
|
+
return { row, attempt: input.attempt, status: "missing", traceId: null, error, verdicts: [] };
|
|
11089
|
+
}
|
|
11090
|
+
const verdicts = await Promise.all(
|
|
11091
|
+
this.parents.map(async (parent) => {
|
|
11092
|
+
const judgeStartedAt = Date.now();
|
|
11093
|
+
let verdict;
|
|
11094
|
+
try {
|
|
11095
|
+
verdict = parseLocalVerdict(
|
|
11096
|
+
parent.evaluator,
|
|
11097
|
+
await parent.evaluator.judge({
|
|
11098
|
+
trace: pair.candidate.trace,
|
|
11099
|
+
case: pair,
|
|
11100
|
+
row,
|
|
11101
|
+
result
|
|
11102
|
+
})
|
|
11103
|
+
);
|
|
11104
|
+
} catch (cause) {
|
|
11105
|
+
verdict = { error: localEvaluatorError(cause) };
|
|
11106
|
+
}
|
|
11107
|
+
const requestId = randomUUID();
|
|
11108
|
+
let receipt;
|
|
11109
|
+
try {
|
|
11110
|
+
receipt = await queryApi({
|
|
11111
|
+
client: this.client,
|
|
11112
|
+
queryUrl: this.queryUrl,
|
|
11113
|
+
path: `/v1/evals/${encodeURIComponent(parent.evaluator.slug)}/runs/local`,
|
|
11114
|
+
method: "POST",
|
|
11115
|
+
schema: WireLocalEvalAttemptSchema,
|
|
11116
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES,
|
|
11117
|
+
body: {
|
|
11118
|
+
type: "attempt",
|
|
11119
|
+
runId: parent.runId,
|
|
11120
|
+
requestId,
|
|
11121
|
+
source: {
|
|
11122
|
+
replay: this.replayId,
|
|
11123
|
+
rowId: row.id,
|
|
11124
|
+
attempt: input.attempt,
|
|
11125
|
+
traceEvidence: [evidence]
|
|
11126
|
+
},
|
|
11127
|
+
verdict,
|
|
11128
|
+
durationMs: Date.now() - judgeStartedAt
|
|
11129
|
+
}
|
|
11130
|
+
});
|
|
11131
|
+
} catch (cause) {
|
|
11132
|
+
throw new Error(
|
|
11133
|
+
`Raindrop local evaluator ${parent.evaluator.slug} could not record attempt at ${parent.url}: ${describeError(cause)}`
|
|
11134
|
+
);
|
|
11135
|
+
}
|
|
11136
|
+
const data = receipt.data;
|
|
11137
|
+
if (data.runId !== parent.runId || data.requestId !== requestId || data.rowId !== row.id || data.attempt !== input.attempt || data.traceId !== evidence.traceId) {
|
|
11138
|
+
throw new Error(
|
|
11139
|
+
`Raindrop local evaluator ${parent.evaluator.slug} returned a mismatched attempt receipt`
|
|
11140
|
+
);
|
|
11141
|
+
}
|
|
11142
|
+
return replayVerdict(
|
|
11143
|
+
{
|
|
11144
|
+
evaluator: parent.evaluator.slug,
|
|
11145
|
+
runId: parent.runId,
|
|
11146
|
+
status: "completed",
|
|
11147
|
+
url: parent.url,
|
|
11148
|
+
summary: null,
|
|
11149
|
+
error: null
|
|
11150
|
+
},
|
|
11151
|
+
data.outcome
|
|
11152
|
+
);
|
|
11153
|
+
})
|
|
11154
|
+
);
|
|
11155
|
+
return {
|
|
11156
|
+
row,
|
|
11157
|
+
attempt: input.attempt,
|
|
11158
|
+
status: "done",
|
|
11159
|
+
traceId: evidence.traceId,
|
|
11160
|
+
error: null,
|
|
11161
|
+
result,
|
|
11162
|
+
verdicts
|
|
11163
|
+
};
|
|
11164
|
+
}
|
|
11165
|
+
finish() {
|
|
11166
|
+
if (this.finishPromise) return this.finishPromise;
|
|
11167
|
+
this.state = "finishing";
|
|
11168
|
+
this.finishPromise = this.finishOnce();
|
|
11169
|
+
return this.finishPromise;
|
|
11170
|
+
}
|
|
11171
|
+
async finishOnce() {
|
|
11172
|
+
await reportReplay(this.client, this.queryUrl, this.replayId, { type: "finished" });
|
|
11173
|
+
const completed = await Promise.all(
|
|
11174
|
+
this.parents.map(async (parent) => {
|
|
11175
|
+
const response = await queryApi({
|
|
11176
|
+
client: this.client,
|
|
11177
|
+
queryUrl: this.queryUrl,
|
|
11178
|
+
path: `/v1/evals/${encodeURIComponent(parent.evaluator.slug)}/runs/local`,
|
|
11179
|
+
method: "POST",
|
|
11180
|
+
schema: WireLocalEvalRunSchema,
|
|
11181
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES,
|
|
11182
|
+
body: { type: "finished", runId: parent.runId, source: { replay: this.replayId } }
|
|
11183
|
+
});
|
|
11184
|
+
if (response.data.id !== parent.runId) {
|
|
11185
|
+
throw new Error(
|
|
11186
|
+
`Raindrop local evaluator ${parent.evaluator.slug} returned the wrong run id`
|
|
11187
|
+
);
|
|
11188
|
+
}
|
|
11189
|
+
return {
|
|
11190
|
+
evaluator: parent.evaluator.slug,
|
|
11191
|
+
runId: response.data.id,
|
|
11192
|
+
status: response.data.status,
|
|
11193
|
+
url: response.data.url,
|
|
11194
|
+
summary: response.data.summary,
|
|
11195
|
+
error: response.data.error
|
|
11196
|
+
};
|
|
11197
|
+
})
|
|
11198
|
+
);
|
|
11199
|
+
const reading = await readReplay(this.client, this.replayId, { queryUrl: this.queryUrl });
|
|
11200
|
+
this.state = "finished";
|
|
11201
|
+
return { reading, evaluators: completed };
|
|
11202
|
+
}
|
|
11203
|
+
async allocateRetry(rowId, attempt) {
|
|
11204
|
+
const response = await queryApi({
|
|
11205
|
+
client: this.client,
|
|
11206
|
+
queryUrl: this.queryUrl,
|
|
11207
|
+
path: `/v1/replays/${encodeURIComponent(this.replayId)}/report`,
|
|
11208
|
+
method: "POST",
|
|
11209
|
+
schema: WireAttemptStartedSchema,
|
|
11210
|
+
body: { type: "attempt_started", row_id: rowId, attempt, request_key: randomUUID() },
|
|
11211
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES
|
|
11212
|
+
});
|
|
11213
|
+
if (response.row.row_id !== rowId || response.row.attempt !== attempt) {
|
|
11214
|
+
throw new Error(`Raindrop replay ${this.replayId} returned the wrong retry attempt`);
|
|
11215
|
+
}
|
|
11216
|
+
return response.row.correlation_id;
|
|
11217
|
+
}
|
|
11218
|
+
};
|
|
11219
|
+
function requireReplayIngestUrl(handle) {
|
|
11220
|
+
if (!handle.ingestUrl) {
|
|
11221
|
+
throw new Error(`Raindrop replay ${handle.replayId} has no ingest URL`);
|
|
11222
|
+
}
|
|
11223
|
+
return handle.ingestUrl;
|
|
11224
|
+
}
|
|
11225
|
+
function capturedEvidence(rowId, traceIds, spanIds) {
|
|
11226
|
+
const traces = [...traceIds];
|
|
11227
|
+
const spans = [...spanIds];
|
|
11228
|
+
if (traces.length !== 1 || spans.length === 0 || spans.length > 500) {
|
|
11229
|
+
const detail = traces.length !== 1 ? `expected one trace, captured ${traces.length}` : spans.length === 0 ? "captured no ended spans" : `captured ${spans.length} spans, exceeding the 500 span limit`;
|
|
11230
|
+
throw new Error(`Raindrop replay row ${rowId} ${detail}`);
|
|
11231
|
+
}
|
|
11232
|
+
const traceId = traces[0];
|
|
11233
|
+
if (traceId === void 0) throw new Error(`Raindrop replay row ${rowId} lost its trace id`);
|
|
11234
|
+
return ReplayTraceEvidenceEntrySchema.parse({ rowId, traceId, spanIds: spans });
|
|
11235
|
+
}
|
|
11236
|
+
function reportReplay(client, queryUrl, replayId, body) {
|
|
11237
|
+
return queryApi({
|
|
11238
|
+
client,
|
|
11239
|
+
queryUrl,
|
|
11240
|
+
path: `/v1/replays/${encodeURIComponent(replayId)}/report`,
|
|
11241
|
+
method: "POST",
|
|
11242
|
+
schema: WireReportSchema,
|
|
11243
|
+
body,
|
|
11244
|
+
maxRetries: MAX_REPLAY_REPORT_RETRIES
|
|
11245
|
+
});
|
|
11246
|
+
}
|
|
11247
|
+
async function replay(client, options) {
|
|
11248
|
+
var _a, _b;
|
|
11249
|
+
const evaluators = normalizeEvaluators(options.evaluators);
|
|
11250
|
+
const local = evaluators.flatMap(
|
|
11251
|
+
(evaluator) => typeof evaluator === "string" ? [] : [{ evaluator, verdicts: [], durationMs: 0 }]
|
|
11252
|
+
);
|
|
11253
|
+
await Promise.all(
|
|
11254
|
+
local.map(
|
|
11255
|
+
(state) => {
|
|
11256
|
+
var _a2;
|
|
11257
|
+
return registerLocalEvaluator(client, state.evaluator, (_a2 = options.queryUrl) != null ? _a2 : DEFAULT_QUERY_URL);
|
|
11258
|
+
}
|
|
11259
|
+
)
|
|
11260
|
+
);
|
|
11261
|
+
const handle = await createReplay(client, options);
|
|
11262
|
+
const execution = await runReplayRows(client, handle, options);
|
|
11263
|
+
const { reading } = execution;
|
|
11264
|
+
const doneRowIds = new Set(
|
|
11265
|
+
reading.rows.flatMap((row) => row.status === "done" ? [row.rowId] : [])
|
|
11266
|
+
);
|
|
11267
|
+
const traceEvidence = execution.traceEvidence.filter((entry) => doneRowIds.has(entry.rowId));
|
|
11268
|
+
if (traceEvidence.length !== doneRowIds.size) {
|
|
11269
|
+
throw new Error(`Raindrop replay ${reading.replayId} completed without full trace evidence`);
|
|
11270
|
+
}
|
|
11271
|
+
if (local.length > 0) {
|
|
11272
|
+
const pairs = await readEvalCaseTracePairs({
|
|
11273
|
+
client,
|
|
11274
|
+
queryUrl: (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
|
|
11275
|
+
replayId: reading.replayId,
|
|
11276
|
+
datasetVersion: handle.datasetVersion,
|
|
11277
|
+
evidence: traceEvidence,
|
|
11278
|
+
waitMs: (_b = options.traceWaitMs) != null ? _b : DEFAULT_TRACE_WAIT_MS
|
|
11279
|
+
});
|
|
11280
|
+
const rowsById = new Map(
|
|
11281
|
+
handle.rows.map(({ correlationId: _correlationId, ...row }) => [row.id, row])
|
|
11282
|
+
);
|
|
11283
|
+
for (const [rowId, pair] of pairs) {
|
|
11284
|
+
const row = rowsById.get(rowId);
|
|
11285
|
+
const result = execution.results.get(rowId);
|
|
11286
|
+
if (!row || !result) {
|
|
11287
|
+
throw new Error(`Raindrop replay ${reading.replayId} omitted result for ${rowId}`);
|
|
11288
|
+
}
|
|
11289
|
+
await runLocalEvaluators(local, pair, row, result.value);
|
|
11290
|
+
}
|
|
11291
|
+
}
|
|
11292
|
+
const hosted = evaluators.flatMap(
|
|
11293
|
+
(evaluator) => typeof evaluator === "string" ? [evaluator] : []
|
|
11294
|
+
);
|
|
11295
|
+
const [hostedResults, localResults] = await Promise.all([
|
|
11296
|
+
hosted.length === 0 ? Promise.resolve([]) : runReplayEvaluators(client, reading.replayId, {
|
|
11297
|
+
evaluators: hosted,
|
|
11298
|
+
evaluatorPins: options.evaluatorPins,
|
|
11299
|
+
queryUrl: options.queryUrl,
|
|
11300
|
+
waitMs: options.evalWaitMs,
|
|
11301
|
+
pollIntervalMs: options.evalPollIntervalMs,
|
|
11302
|
+
traceEvidence
|
|
11303
|
+
}),
|
|
11304
|
+
Promise.all(
|
|
11305
|
+
local.map(
|
|
11306
|
+
(state) => {
|
|
11307
|
+
var _a2;
|
|
11308
|
+
return submitLocalEvaluator(
|
|
11309
|
+
client,
|
|
11310
|
+
reading.replayId,
|
|
11311
|
+
state,
|
|
11312
|
+
(_a2 = options.queryUrl) != null ? _a2 : DEFAULT_QUERY_URL,
|
|
11313
|
+
traceEvidence
|
|
11314
|
+
);
|
|
11315
|
+
}
|
|
11316
|
+
)
|
|
11317
|
+
)
|
|
11318
|
+
]);
|
|
11319
|
+
const resultsByEvaluator = new Map(
|
|
11320
|
+
[...hostedResults, ...localResults].map((result) => [result.evaluator, result])
|
|
11321
|
+
);
|
|
11322
|
+
const evaluatorResults = evaluators.map((evaluator) => {
|
|
11323
|
+
const name = typeof evaluator === "string" ? evaluator : evaluator.slug;
|
|
11324
|
+
const result = resultsByEvaluator.get(name);
|
|
11325
|
+
if (!result) throw new Error(`Raindrop replay omitted evaluator ${name}`);
|
|
11326
|
+
return result;
|
|
11327
|
+
});
|
|
11328
|
+
return {
|
|
11329
|
+
...reading,
|
|
11330
|
+
rows: replayResultRows(handle, reading, evaluatorResults),
|
|
11331
|
+
evaluators: evaluatorResults.map(({ outcomes: _outcomes, ...evaluator }) => evaluator)
|
|
11332
|
+
};
|
|
11333
|
+
}
|
|
11334
|
+
async function runLocalEvaluators(evaluators, evalCase, row, result) {
|
|
11335
|
+
await Promise.all(
|
|
11336
|
+
evaluators.map(async (state) => {
|
|
11337
|
+
const startedAt = Date.now();
|
|
11338
|
+
try {
|
|
11339
|
+
const verdict = parseLocalVerdict(
|
|
11340
|
+
state.evaluator,
|
|
11341
|
+
await state.evaluator.judge({
|
|
11342
|
+
trace: evalCase.candidate.trace,
|
|
11343
|
+
row,
|
|
11344
|
+
result,
|
|
11345
|
+
case: evalCase
|
|
11346
|
+
})
|
|
11347
|
+
);
|
|
11348
|
+
state.verdicts.push({ rowId: row.id, ...verdict });
|
|
11349
|
+
} catch (cause) {
|
|
11350
|
+
state.verdicts.push({ rowId: row.id, error: localEvaluatorError(cause) });
|
|
11351
|
+
} finally {
|
|
11352
|
+
state.durationMs += Date.now() - startedAt;
|
|
11353
|
+
}
|
|
11354
|
+
})
|
|
11355
|
+
);
|
|
11356
|
+
}
|
|
11357
|
+
async function registerLocalEvaluator(client, evaluator, queryUrl) {
|
|
11358
|
+
return queryApi({
|
|
11359
|
+
client,
|
|
11360
|
+
queryUrl,
|
|
11361
|
+
path: `/v1/evals/${encodeURIComponent(evaluator.slug)}/local`,
|
|
11362
|
+
method: "PUT",
|
|
11363
|
+
schema: WireLocalEvaluatorSchema,
|
|
11364
|
+
body: { name: evaluator.name, output: evaluator.output }
|
|
11365
|
+
});
|
|
11366
|
+
}
|
|
11367
|
+
function parseLocalVerdict(evaluator, verdict) {
|
|
11368
|
+
switch (evaluator.output) {
|
|
11369
|
+
case "boolean":
|
|
11370
|
+
return LocalBooleanVerdictSchema.parse(verdict);
|
|
11371
|
+
case "score":
|
|
11372
|
+
return LocalScoreVerdictSchema.parse(verdict);
|
|
11373
|
+
case "number":
|
|
11374
|
+
return LocalNumberVerdictSchema.parse(verdict);
|
|
11375
|
+
}
|
|
11376
|
+
}
|
|
11377
|
+
async function submitLocalEvaluator(client, replayId, state, queryUrl, traceEvidence) {
|
|
11378
|
+
const response = await queryApi({
|
|
11379
|
+
client,
|
|
11380
|
+
queryUrl,
|
|
11381
|
+
path: `/v1/evals/${encodeURIComponent(state.evaluator.slug)}/runs/local`,
|
|
11382
|
+
method: "POST",
|
|
11383
|
+
schema: WireLocalEvalRunSchema,
|
|
11384
|
+
body: {
|
|
11385
|
+
source: { replay: replayId, traceEvidence },
|
|
11386
|
+
verdicts: state.verdicts,
|
|
11387
|
+
durationMs: state.durationMs
|
|
11388
|
+
}
|
|
11389
|
+
});
|
|
11390
|
+
return {
|
|
11391
|
+
evaluator: state.evaluator.slug,
|
|
11392
|
+
runId: response.data.id,
|
|
11393
|
+
status: response.data.status,
|
|
11394
|
+
url: response.data.url,
|
|
11395
|
+
summary: response.data.summary,
|
|
11396
|
+
error: response.data.error,
|
|
11397
|
+
outcomes: response.data.traces
|
|
11398
|
+
};
|
|
11399
|
+
}
|
|
11400
|
+
async function evaluateReplay(client, replayId, options) {
|
|
11401
|
+
const results = await runReplayEvaluators(client, replayId, options);
|
|
11402
|
+
return results.map(({ outcomes: _outcomes, ...evaluator }) => evaluator);
|
|
11403
|
+
}
|
|
11404
|
+
async function runReplayEvaluators(client, replayId, options) {
|
|
11405
|
+
var _a, _b, _c;
|
|
11406
|
+
const evaluators = validateEvaluatorNames(options.evaluators);
|
|
11407
|
+
const evaluatorPins = validateEvaluatorPins(evaluators, options.evaluatorPins);
|
|
11408
|
+
const queryUrl = (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL;
|
|
11409
|
+
const waitMs = (_b = options.waitMs) != null ? _b : DEFAULT_EVAL_WAIT_MS;
|
|
11410
|
+
const pollIntervalMs = (_c = options.pollIntervalMs) != null ? _c : DEFAULT_EVAL_POLL_INTERVAL_MS;
|
|
11411
|
+
validateWait("waitMs", waitMs, true);
|
|
11412
|
+
validateWait("pollIntervalMs", pollIntervalMs, false);
|
|
11413
|
+
const starts = await Promise.allSettled(
|
|
11414
|
+
evaluators.map(async (evaluator) => {
|
|
11415
|
+
const pin = evaluatorPins.get(evaluator);
|
|
11416
|
+
const started = await queryApi({
|
|
11417
|
+
client,
|
|
11418
|
+
queryUrl,
|
|
11419
|
+
path: `/v1/evals/${encodeURIComponent(evaluator)}/runs`,
|
|
11420
|
+
method: "POST",
|
|
11421
|
+
schema: WireStartedEvalRunSchema,
|
|
11422
|
+
body: {
|
|
11423
|
+
source: {
|
|
11424
|
+
replay: replayId,
|
|
11425
|
+
...options.traceEvidence ? { traceEvidence: options.traceEvidence } : {}
|
|
11426
|
+
},
|
|
11427
|
+
...pin != null ? pin : {}
|
|
11428
|
+
}
|
|
11429
|
+
});
|
|
11430
|
+
if (pin && (started.data.evalId !== pin.expectedEvalId || started.data.programVersion !== pin.expectedProgramVersion)) {
|
|
11431
|
+
throw new Error(`Raindrop evaluator ${evaluator} did not honor its pulled identity pin`);
|
|
11432
|
+
}
|
|
11433
|
+
return { evaluator, ...started.data };
|
|
11434
|
+
})
|
|
11435
|
+
);
|
|
11436
|
+
const startedRuns = starts.flatMap(
|
|
11437
|
+
(entry) => entry.status === "fulfilled" ? [entry.value] : []
|
|
11438
|
+
);
|
|
11439
|
+
const failures = starts.flatMap((entry) => entry.status === "rejected" ? [entry.reason] : []);
|
|
11440
|
+
if (failures.length > 0) {
|
|
11441
|
+
throw Object.assign(
|
|
11442
|
+
new Error(
|
|
11443
|
+
"Raindrop could not start every evaluator; startedRuns contains the runs that did start"
|
|
11444
|
+
),
|
|
11445
|
+
{ startedRuns, causes: failures }
|
|
11446
|
+
);
|
|
11447
|
+
}
|
|
11448
|
+
const deadlineAt = Date.now() + waitMs;
|
|
11449
|
+
return Promise.all(
|
|
11450
|
+
startedRuns.map(async (started) => {
|
|
11451
|
+
let run = await readEvalRun(client, queryUrl, started.runId, deadlineAt);
|
|
11452
|
+
while (run.status === "running" && Date.now() < deadlineAt) {
|
|
11453
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, deadlineAt - Date.now())));
|
|
11454
|
+
if (Date.now() >= deadlineAt) break;
|
|
11455
|
+
run = await readEvalRun(client, queryUrl, started.runId, deadlineAt);
|
|
11456
|
+
}
|
|
11457
|
+
if (run.status === "running") {
|
|
11458
|
+
throw new Error(
|
|
11459
|
+
`Raindrop eval ${started.evaluator} (${started.runId}) did not finish within ${waitMs}ms`
|
|
11460
|
+
);
|
|
11461
|
+
}
|
|
11462
|
+
return {
|
|
11463
|
+
evaluator: started.evaluator,
|
|
11464
|
+
runId: started.runId,
|
|
11465
|
+
status: run.status,
|
|
11466
|
+
url: started.url,
|
|
11467
|
+
summary: run.summary,
|
|
11468
|
+
error: run.error,
|
|
11469
|
+
outcomes: run.traces
|
|
11470
|
+
};
|
|
11471
|
+
})
|
|
11472
|
+
);
|
|
11473
|
+
}
|
|
11474
|
+
function replayVerdict(evaluator, outcome) {
|
|
11475
|
+
const identity = {
|
|
11476
|
+
evaluator: evaluator.evaluator,
|
|
11477
|
+
runId: evaluator.runId,
|
|
11478
|
+
url: evaluator.url
|
|
11479
|
+
};
|
|
11480
|
+
if (outcome.state === "errored") {
|
|
11481
|
+
return { ...identity, state: outcome.state, message: outcome.message };
|
|
11482
|
+
}
|
|
11483
|
+
if (outcome.state === "ungraded") {
|
|
11484
|
+
return { ...identity, state: outcome.state, reason: outcome.reason };
|
|
11485
|
+
}
|
|
11486
|
+
if ("pass" in outcome) {
|
|
11487
|
+
return { ...identity, state: outcome.state, pass: outcome.pass, note: outcome.note };
|
|
11488
|
+
}
|
|
11489
|
+
if ("score" in outcome) {
|
|
11490
|
+
return { ...identity, state: outcome.state, score: outcome.score, note: outcome.note };
|
|
11491
|
+
}
|
|
11492
|
+
return { ...identity, state: outcome.state, value: outcome.value, note: outcome.note };
|
|
11493
|
+
}
|
|
11494
|
+
function replayResultRows(handle, reading, evaluators) {
|
|
11495
|
+
var _a;
|
|
11496
|
+
const resultsByRow = new Map(reading.rows.map((row) => [row.rowId, row]));
|
|
11497
|
+
const verdictsByTrace = /* @__PURE__ */ new Map();
|
|
11498
|
+
for (const evaluator of evaluators) {
|
|
11499
|
+
for (const outcome of evaluator.outcomes) {
|
|
11500
|
+
const verdicts = (_a = verdictsByTrace.get(outcome.traceId)) != null ? _a : [];
|
|
11501
|
+
verdicts.push(replayVerdict(evaluator, outcome));
|
|
11502
|
+
verdictsByTrace.set(outcome.traceId, verdicts);
|
|
11503
|
+
}
|
|
11504
|
+
}
|
|
11505
|
+
return handle.rows.map(({ correlationId: _correlationId, ...row }) => {
|
|
11506
|
+
var _a2, _b;
|
|
11507
|
+
const result = resultsByRow.get(row.id);
|
|
11508
|
+
if (!result) throw new Error(`Raindrop replay result omitted row ${row.id}`);
|
|
11509
|
+
const replayRow = ReplayResultRowSchema.parse({
|
|
11510
|
+
...row,
|
|
11511
|
+
status: result.status,
|
|
11512
|
+
traceId: result.traceId,
|
|
11513
|
+
error: result.error,
|
|
11514
|
+
verdicts: evaluators.map((evaluator) => {
|
|
11515
|
+
var _a3, _b2, _c;
|
|
11516
|
+
const matches = result.traceId === null ? [] : (_b2 = (_a3 = verdictsByTrace.get(result.traceId)) == null ? void 0 : _a3.filter((entry) => entry.evaluator === evaluator.evaluator)) != null ? _b2 : [];
|
|
11517
|
+
if (matches.length > 1)
|
|
11518
|
+
throw new Error(
|
|
11519
|
+
`Raindrop replay row ${row.id} received duplicate verdicts from ${evaluator.evaluator}`
|
|
11520
|
+
);
|
|
11521
|
+
const verdict = matches[0];
|
|
11522
|
+
const identity = {
|
|
11523
|
+
evaluator: evaluator.evaluator,
|
|
11524
|
+
runId: evaluator.runId,
|
|
11525
|
+
url: evaluator.url
|
|
11526
|
+
};
|
|
11527
|
+
if (evaluator.status === "failed")
|
|
11528
|
+
return { ...identity, state: "errored", message: (_c = evaluator.error) != null ? _c : "Evaluator failed" };
|
|
11529
|
+
if (verdict) return verdict;
|
|
11530
|
+
if (result.traceId === null)
|
|
11531
|
+
return { ...identity, state: "ungraded", reason: "trace unavailable" };
|
|
11532
|
+
throw new Error(
|
|
11533
|
+
`Raindrop replay ${reading.replayId} row ${row.id} omitted verdict from ${evaluator.evaluator}`
|
|
11534
|
+
);
|
|
11535
|
+
})
|
|
11536
|
+
});
|
|
11537
|
+
if (replayRow.status === "done") {
|
|
11538
|
+
const counts = /* @__PURE__ */ new Map();
|
|
11539
|
+
for (const verdict of replayRow.verdicts) {
|
|
11540
|
+
counts.set(verdict.evaluator, ((_a2 = counts.get(verdict.evaluator)) != null ? _a2 : 0) + 1);
|
|
11541
|
+
}
|
|
11542
|
+
for (const evaluator of evaluators) {
|
|
11543
|
+
const count = (_b = counts.get(evaluator.evaluator)) != null ? _b : 0;
|
|
11544
|
+
if (count !== 1) {
|
|
11545
|
+
const failure = evaluator.status === "failed" ? ` Evaluator failed${evaluator.error ? `: ${evaluator.error}` : "."}` : "";
|
|
11546
|
+
throw new Error(
|
|
11547
|
+
`Raindrop replay ${reading.replayId} row ${row.id} expected one verdict from ${evaluator.evaluator}, received ${count}.${failure} Run: ${evaluator.url}`
|
|
11548
|
+
);
|
|
11549
|
+
}
|
|
11550
|
+
}
|
|
11551
|
+
}
|
|
11552
|
+
return replayRow;
|
|
11553
|
+
});
|
|
11554
|
+
}
|
|
11555
|
+
async function readEvalRun(client, queryUrl, runId, deadlineAt) {
|
|
11556
|
+
const response = await queryApi({
|
|
11557
|
+
client,
|
|
11558
|
+
queryUrl,
|
|
11559
|
+
path: `/v1/eval-runs/${encodeURIComponent(runId)}?include=traces`,
|
|
11560
|
+
method: "GET",
|
|
11561
|
+
schema: WireEvalRunSchema,
|
|
11562
|
+
deadlineAt,
|
|
11563
|
+
maxRetries: MAX_EVAL_STATUS_RETRIES
|
|
11564
|
+
});
|
|
11565
|
+
return response.data;
|
|
11566
|
+
}
|
|
11567
|
+
async function waitForTraces(input) {
|
|
11568
|
+
let reading = await readReplay(input.client, input.replayId, {
|
|
11569
|
+
queryUrl: input.queryUrl,
|
|
11570
|
+
deadlineAt: input.deadlineAt
|
|
11571
|
+
});
|
|
11572
|
+
while (reading.counts.pending > 0 && Date.now() < input.deadlineAt) {
|
|
11573
|
+
await sleep(Math.min(TRACE_POLL_INTERVAL_MS, Math.max(0, input.deadlineAt - Date.now())));
|
|
11574
|
+
if (Date.now() >= input.deadlineAt) break;
|
|
11575
|
+
try {
|
|
11576
|
+
reading = await readReplay(input.client, input.replayId, {
|
|
11577
|
+
queryUrl: input.queryUrl,
|
|
11578
|
+
deadlineAt: input.deadlineAt
|
|
11579
|
+
});
|
|
11580
|
+
} catch (error) {
|
|
11581
|
+
if (Date.now() < input.deadlineAt) throw error;
|
|
11582
|
+
break;
|
|
11583
|
+
}
|
|
11584
|
+
}
|
|
11585
|
+
return reading;
|
|
11586
|
+
}
|
|
11587
|
+
async function waitForFailedTraceAssociation(input) {
|
|
11588
|
+
if (input.spanIds.size === 0 && input.traceIds.size === 0) return null;
|
|
11589
|
+
if (input.spanIds.size === 0) {
|
|
11590
|
+
throw new Error(
|
|
11591
|
+
`Raindrop replay ${input.replayId} failed row ${input.rowId} captured a trace without ended spans`
|
|
11592
|
+
);
|
|
11593
|
+
}
|
|
11594
|
+
if (input.traceIds.size !== 1) {
|
|
11595
|
+
throw new Error(
|
|
11596
|
+
`Raindrop replay ${input.replayId} failed row ${input.rowId} expected one trace, captured ${input.traceIds.size}`
|
|
11597
|
+
);
|
|
11598
|
+
}
|
|
11599
|
+
validateWait("traceWaitMs", input.waitMs, true);
|
|
11600
|
+
const expectedTraceId = [...input.traceIds][0];
|
|
11601
|
+
if (expectedTraceId === void 0) {
|
|
11602
|
+
throw new Error(
|
|
11603
|
+
`Raindrop replay ${input.replayId} lost failed trace evidence for ${input.rowId}`
|
|
11604
|
+
);
|
|
11605
|
+
}
|
|
11606
|
+
const deadlineAt = Date.now() + input.waitMs;
|
|
11607
|
+
while (true) {
|
|
11608
|
+
const reading = await readReplay(input.client, input.replayId, {
|
|
11609
|
+
queryUrl: input.queryUrl,
|
|
11610
|
+
deadlineAt
|
|
11611
|
+
});
|
|
11612
|
+
const row = reading.rows.find(
|
|
11613
|
+
(candidate) => candidate.rowId === input.rowId && candidate.attempt === input.attempt
|
|
11614
|
+
);
|
|
11615
|
+
if (!row) {
|
|
11616
|
+
throw new Error(
|
|
11617
|
+
`Raindrop replay ${input.replayId} omitted failed row ${input.rowId} attempt ${input.attempt}`
|
|
11618
|
+
);
|
|
11619
|
+
}
|
|
11620
|
+
if (row.status !== "missing") {
|
|
11621
|
+
throw new Error(
|
|
11622
|
+
`Raindrop replay ${input.replayId} changed failed row ${input.rowId} attempt ${input.attempt} to ${row.status}`
|
|
11623
|
+
);
|
|
11624
|
+
}
|
|
11625
|
+
if (row.traceId === expectedTraceId) return expectedTraceId;
|
|
11626
|
+
if (row.traceId !== null) {
|
|
11627
|
+
throw new Error(
|
|
11628
|
+
`Raindrop replay ${input.replayId} linked failed row ${input.rowId} attempt ${input.attempt} to the wrong trace`
|
|
11629
|
+
);
|
|
11630
|
+
}
|
|
11631
|
+
if (Date.now() >= deadlineAt) {
|
|
11632
|
+
throw new Error(
|
|
11633
|
+
`Raindrop replay ${input.replayId} did not retain failed trace ${expectedTraceId} for ${input.rowId} attempt ${input.attempt} within ${input.waitMs}ms`
|
|
11634
|
+
);
|
|
11635
|
+
}
|
|
11636
|
+
await sleep(Math.min(TRACE_POLL_INTERVAL_MS, Math.max(0, deadlineAt - Date.now())));
|
|
11637
|
+
}
|
|
11638
|
+
}
|
|
11639
|
+
function failedTraceRetentionError(taskCause, retentionCause, rowId) {
|
|
11640
|
+
const taskError = taskCause instanceof Error ? taskCause : new Error(describeError(taskCause));
|
|
11641
|
+
const retentionError = retentionCause instanceof Error ? retentionCause : new Error(describeError(retentionCause));
|
|
11642
|
+
return new AggregateError(
|
|
11643
|
+
[taskError, retentionError],
|
|
11644
|
+
`Raindrop replay task ${rowId} failed and its trace could not be retained`,
|
|
11645
|
+
{ cause: taskError }
|
|
11646
|
+
);
|
|
11647
|
+
}
|
|
11648
|
+
function sleep(ms) {
|
|
11649
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11650
|
+
}
|
|
11651
|
+
function isRetryableStatus(status) {
|
|
11652
|
+
return status === 408 || status === 429 || status >= 500;
|
|
11653
|
+
}
|
|
11654
|
+
function validateEvaluatorNames(evaluators) {
|
|
11655
|
+
if (evaluators.length === 0) {
|
|
11656
|
+
throw new Error("Raindrop replay requires at least one evaluator");
|
|
11657
|
+
}
|
|
11658
|
+
const normalized = evaluators.map((evaluator) => evaluator.trim());
|
|
11659
|
+
if (normalized.some((evaluator) => evaluator.length === 0)) {
|
|
11660
|
+
throw new Error("Raindrop replay evaluators cannot be empty");
|
|
11661
|
+
}
|
|
11662
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
11663
|
+
throw new Error("Raindrop replay evaluators must be unique");
|
|
11664
|
+
}
|
|
11665
|
+
return normalized;
|
|
11666
|
+
}
|
|
11667
|
+
function validateEvaluatorPins(evaluators, raw) {
|
|
11668
|
+
if (!raw) return /* @__PURE__ */ new Map();
|
|
11669
|
+
const allowed = new Set(evaluators);
|
|
11670
|
+
const pins = /* @__PURE__ */ new Map();
|
|
11671
|
+
for (const [slug, pin] of Object.entries(raw)) {
|
|
11672
|
+
if (!allowed.has(slug)) {
|
|
11673
|
+
throw new Error(`Raindrop evaluator pin names unknown evaluator ${slug}`);
|
|
11674
|
+
}
|
|
11675
|
+
if (!z3.string().uuid().safeParse(pin.expectedEvalId).success) {
|
|
11676
|
+
throw new Error(`Raindrop evaluator ${slug} pin has an invalid evaluator id`);
|
|
11677
|
+
}
|
|
11678
|
+
if (!Number.isInteger(pin.expectedProgramVersion) || pin.expectedProgramVersion <= 0) {
|
|
11679
|
+
throw new Error(`Raindrop evaluator ${slug} pin has an invalid program version`);
|
|
11680
|
+
}
|
|
11681
|
+
pins.set(slug, pin);
|
|
11682
|
+
}
|
|
11683
|
+
return pins;
|
|
11684
|
+
}
|
|
11685
|
+
function normalizeEvaluators(evaluators) {
|
|
11686
|
+
if (evaluators.length === 0) {
|
|
11687
|
+
throw new Error("Raindrop replay requires at least one evaluator");
|
|
11688
|
+
}
|
|
11689
|
+
const normalized = evaluators.map(
|
|
11690
|
+
(evaluator) => typeof evaluator === "string" ? evaluator.trim() : {
|
|
11691
|
+
...evaluator,
|
|
11692
|
+
slug: evaluator.slug.trim(),
|
|
11693
|
+
name: evaluator.name.trim()
|
|
11694
|
+
}
|
|
11695
|
+
);
|
|
11696
|
+
const names = normalized.map(
|
|
11697
|
+
(evaluator) => typeof evaluator === "string" ? evaluator : evaluator.slug
|
|
11698
|
+
);
|
|
11699
|
+
validateEvaluatorNames(names);
|
|
11700
|
+
for (const evaluator of normalized) {
|
|
11701
|
+
if (typeof evaluator === "string") continue;
|
|
11702
|
+
if (!LOCAL_EVALUATOR_SLUG_PATTERN.test(evaluator.slug) || evaluator.slug.length > 64) {
|
|
11703
|
+
throw new Error(
|
|
11704
|
+
"Raindrop local evaluator slugs must use lowercase letters, numbers, and single hyphens"
|
|
11705
|
+
);
|
|
11706
|
+
}
|
|
11707
|
+
if (evaluator.name.length === 0 || evaluator.name.length > 120) {
|
|
11708
|
+
throw new Error("Raindrop local evaluator names must be between 1 and 120 characters");
|
|
11709
|
+
}
|
|
11710
|
+
}
|
|
11711
|
+
return normalized;
|
|
11712
|
+
}
|
|
11713
|
+
function localEvaluatorError(cause) {
|
|
11714
|
+
const message = describeError(cause).trim();
|
|
11715
|
+
return (message === "" ? "Local evaluator failed" : message).slice(0, 600);
|
|
11716
|
+
}
|
|
11717
|
+
function validateWait(name, value, allowZero) {
|
|
11718
|
+
const valid = Number.isFinite(value) && (allowZero ? value >= 0 : value > 0);
|
|
11719
|
+
if (!valid) {
|
|
11720
|
+
throw new Error(`Raindrop replay ${name} must be ${allowZero ? "non-negative" : "positive"}`);
|
|
11721
|
+
}
|
|
11722
|
+
}
|
|
11723
|
+
function describeError(cause) {
|
|
11724
|
+
if (cause instanceof Error) {
|
|
11725
|
+
return cause.message.trim() === "" ? cause.name : `${cause.name}: ${cause.message}`;
|
|
11726
|
+
}
|
|
11727
|
+
return String(cause);
|
|
11728
|
+
}
|
|
11729
|
+
|
|
11730
|
+
// src/evals/dataset.ts
|
|
11731
|
+
var MAX_PUBLISH_RETRIES = 2;
|
|
11732
|
+
var RETRY_BASE_DELAY_MS = 100;
|
|
11733
|
+
var MAX_RETRY_DELAY_MS2 = 3e4;
|
|
11734
|
+
var PUBLISH_REQUEST_TIMEOUT_MS = 3e4;
|
|
11735
|
+
var EvalDatasetPublishConflictError = class extends Error {
|
|
11736
|
+
constructor(slug, message) {
|
|
11737
|
+
super(message);
|
|
11738
|
+
this.name = "EvalDatasetPublishConflictError";
|
|
11739
|
+
this.slug = slug;
|
|
11740
|
+
}
|
|
11741
|
+
};
|
|
11742
|
+
async function publishEvalDataset(client, options) {
|
|
11743
|
+
var _a, _b, _c;
|
|
11744
|
+
if (!((_a = client.connection.apiKey) == null ? void 0 : _a.trim())) {
|
|
11745
|
+
throw new Error("Raindrop eval dataset publication requires a Query SDK API key (apiKey)");
|
|
11746
|
+
}
|
|
11747
|
+
const queryUrl = credentialEndpoint((_b = options.queryUrl) != null ? _b : DEFAULT_QUERY_URL).href;
|
|
11748
|
+
const slug = EvalDatasetSlugSchema.parse(options.slug.trim());
|
|
11749
|
+
const fingerprint = await evalDatasetVersionFingerprint(options.cases);
|
|
11750
|
+
const body = PublishEvalDatasetVersionInputSchema.parse({
|
|
11751
|
+
name: options.name,
|
|
11752
|
+
requestKey: randomUUID(),
|
|
11753
|
+
expectedCurrentVersionId: (_c = options.expectedCurrentVersionId) != null ? _c : null,
|
|
11754
|
+
fingerprint,
|
|
11755
|
+
cases: options.cases
|
|
11756
|
+
});
|
|
11757
|
+
validateUploadSizes(body);
|
|
11758
|
+
const path = `/v1/eval-datasets/${encodeURIComponent(slug)}`;
|
|
11759
|
+
const response = await publishRequest({
|
|
11760
|
+
client,
|
|
11761
|
+
url: `${queryUrl.replace(/\/+$/, "")}${path}`,
|
|
11762
|
+
path,
|
|
11763
|
+
slug,
|
|
11764
|
+
body: JSON.stringify(body)
|
|
11765
|
+
});
|
|
11766
|
+
const manifest = EvalDatasetManifestSchema.parse(response);
|
|
11767
|
+
if (manifest.dataset.slug !== slug || manifest.dataset.id !== manifest.version.datasetId || manifest.version.fingerprint !== fingerprint) {
|
|
11768
|
+
throw new Error(`Raindrop eval dataset ${slug} returned mismatched publication identity`);
|
|
11769
|
+
}
|
|
11770
|
+
return manifest;
|
|
11771
|
+
}
|
|
11772
|
+
function validateUploadSizes(body) {
|
|
11773
|
+
for (const [index, entry] of body.cases.entries()) {
|
|
11774
|
+
const bytes2 = encodedBytes(entry.referenceTrace);
|
|
11775
|
+
if (bytes2 > EVAL_DATASET_MAX_REFERENCE_BYTES) {
|
|
11776
|
+
throw new Error(
|
|
11777
|
+
`Raindrop eval dataset case ${entry.id} at index ${index} exceeds ${EVAL_DATASET_MAX_REFERENCE_BYTES} reference bytes`
|
|
11778
|
+
);
|
|
11779
|
+
}
|
|
11780
|
+
}
|
|
11781
|
+
const bytes = encodedBytes(body);
|
|
11782
|
+
if (bytes > EVAL_DATASET_MAX_REQUEST_BYTES) {
|
|
11783
|
+
throw new Error(`Raindrop eval dataset upload exceeds ${EVAL_DATASET_MAX_REQUEST_BYTES} bytes`);
|
|
11784
|
+
}
|
|
11785
|
+
}
|
|
11786
|
+
function encodedBytes(value) {
|
|
11787
|
+
try {
|
|
11788
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
11789
|
+
} catch (cause) {
|
|
11790
|
+
throw new Error("Raindrop eval dataset upload must be serializable", { cause });
|
|
11791
|
+
}
|
|
11792
|
+
}
|
|
11793
|
+
async function publishRequest(input) {
|
|
11794
|
+
const headers = {
|
|
11795
|
+
Authorization: `Bearer ${input.client.connection.apiKey}`,
|
|
11796
|
+
"Content-Type": "application/json"
|
|
11797
|
+
};
|
|
11798
|
+
const projectId = input.client.connection.projectId;
|
|
11799
|
+
if (projectId !== void 0) headers["X-Raindrop-Project-Id"] = projectId;
|
|
11800
|
+
for (let retry = 0; ; retry += 1) {
|
|
11801
|
+
let response;
|
|
11802
|
+
let text;
|
|
11803
|
+
const controller = new AbortController();
|
|
11804
|
+
const timeout = setTimeout(() => controller.abort(), PUBLISH_REQUEST_TIMEOUT_MS);
|
|
11805
|
+
try {
|
|
11806
|
+
response = await runWithTracingSuppressed(
|
|
11807
|
+
() => fetch(input.url, {
|
|
11808
|
+
method: "PUT",
|
|
11809
|
+
headers,
|
|
11810
|
+
body: input.body,
|
|
11811
|
+
signal: controller.signal,
|
|
11812
|
+
redirect: "error"
|
|
11813
|
+
})
|
|
11814
|
+
);
|
|
11815
|
+
text = await response.text();
|
|
11816
|
+
} catch (cause) {
|
|
11817
|
+
if (retry >= MAX_PUBLISH_RETRIES) throw cause;
|
|
11818
|
+
await sleep2(RETRY_BASE_DELAY_MS * 2 ** retry);
|
|
11819
|
+
continue;
|
|
11820
|
+
} finally {
|
|
11821
|
+
clearTimeout(timeout);
|
|
11822
|
+
}
|
|
11823
|
+
if (response.ok) {
|
|
11824
|
+
try {
|
|
11825
|
+
return JSON.parse(text);
|
|
11826
|
+
} catch (cause) {
|
|
11827
|
+
throw new Error(`Raindrop API PUT ${input.path} returned invalid JSON`, { cause });
|
|
11828
|
+
}
|
|
11829
|
+
}
|
|
11830
|
+
if (response.status === 409) {
|
|
11831
|
+
throw new EvalDatasetPublishConflictError(
|
|
11832
|
+
input.slug,
|
|
11833
|
+
`Raindrop eval dataset ${input.slug} changed before publication: ${text}`
|
|
11834
|
+
);
|
|
11835
|
+
}
|
|
11836
|
+
if (retry < MAX_PUBLISH_RETRIES && isRetryableStatus2(response.status)) {
|
|
11837
|
+
await sleep2(retryDelayMs2(response, retry));
|
|
11838
|
+
continue;
|
|
11839
|
+
}
|
|
11840
|
+
throw new Error(`Raindrop API PUT ${input.path}: ${response.status} ${text}`);
|
|
11841
|
+
}
|
|
11842
|
+
}
|
|
11843
|
+
function retryDelayMs2(response, retry) {
|
|
11844
|
+
const retryAfter = response.headers.get("retry-after");
|
|
11845
|
+
if (retryAfter !== null) {
|
|
11846
|
+
const seconds = Number(retryAfter);
|
|
11847
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
11848
|
+
return Math.min(seconds * 1e3, MAX_RETRY_DELAY_MS2);
|
|
11849
|
+
}
|
|
11850
|
+
}
|
|
11851
|
+
return RETRY_BASE_DELAY_MS * 2 ** retry;
|
|
11852
|
+
}
|
|
11853
|
+
function isRetryableStatus2(status) {
|
|
11854
|
+
return status === 408 || status === 429 || status >= 500;
|
|
11855
|
+
}
|
|
11856
|
+
function sleep2(ms) {
|
|
11857
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11858
|
+
}
|
|
11859
|
+
|
|
11860
|
+
// src/evals/pull.ts
|
|
11861
|
+
import { z as z4 } from "zod";
|
|
11862
|
+
var SNAPSHOT_VERSION = 1;
|
|
11863
|
+
var PulledEvaluatorSchema = z4.object({
|
|
11864
|
+
slug: z4.string().min(1),
|
|
11865
|
+
name: z4.string().min(1),
|
|
11866
|
+
expectedSha256: z4.string().regex(/^[a-f0-9]{64}$/),
|
|
11867
|
+
artifact: z4.unknown()
|
|
11868
|
+
});
|
|
11869
|
+
var EvalSnapshotSchema = z4.object({
|
|
11870
|
+
schemaVersion: z4.literal(SNAPSHOT_VERSION),
|
|
11871
|
+
pulledAt: z4.string().datetime(),
|
|
11872
|
+
dataset: z4.object({
|
|
11873
|
+
reference: z4.string().min(1),
|
|
11874
|
+
datasetId: z4.string().uuid(),
|
|
11875
|
+
fingerprint: z4.string().regex(/^[a-f0-9]{64}$/),
|
|
11876
|
+
rows: z4.array(
|
|
11877
|
+
z4.object({
|
|
11878
|
+
id: z4.string(),
|
|
11879
|
+
name: z4.string(),
|
|
11880
|
+
input: z4.string().nullable(),
|
|
11881
|
+
output: z4.string().nullable(),
|
|
11882
|
+
properties: z4.record(z4.string())
|
|
11883
|
+
})
|
|
11884
|
+
)
|
|
11885
|
+
}),
|
|
11886
|
+
evaluators: z4.array(PulledEvaluatorSchema)
|
|
11887
|
+
});
|
|
11888
|
+
async function pullEval(client, options) {
|
|
11889
|
+
var _a;
|
|
11890
|
+
const datasetReference = options.dataset.trim();
|
|
11891
|
+
if (!datasetReference) throw new Error("Raindrop pullEval dataset cannot be empty");
|
|
11892
|
+
const evaluatorSlugs = options.evaluators.map((slug) => slug.trim());
|
|
11893
|
+
if (evaluatorSlugs.length === 0) throw new Error("Raindrop pullEval requires evaluators");
|
|
11894
|
+
if (evaluatorSlugs.some((slug) => !slug)) {
|
|
11895
|
+
throw new Error("Raindrop pullEval evaluator slugs cannot be empty");
|
|
11896
|
+
}
|
|
11897
|
+
if (new Set(evaluatorSlugs).size !== evaluatorSlugs.length) {
|
|
11898
|
+
throw new Error("Raindrop pullEval evaluator slugs must be unique");
|
|
11899
|
+
}
|
|
11900
|
+
if (!options.snapshotPath.trim())
|
|
11901
|
+
throw new Error("Raindrop pullEval snapshotPath cannot be empty");
|
|
11902
|
+
const queryUrl = ((_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL).replace(/\/+$/, "");
|
|
11903
|
+
const [manifest, evaluators] = await Promise.all([
|
|
11904
|
+
readEvalManifest(client, datasetReference, { queryUrl }),
|
|
11905
|
+
Promise.all(evaluatorSlugs.map((slug) => pullEvaluator(client, queryUrl, slug)))
|
|
11906
|
+
]);
|
|
11907
|
+
const snapshot = {
|
|
11908
|
+
schemaVersion: SNAPSHOT_VERSION,
|
|
11909
|
+
pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11910
|
+
dataset: {
|
|
11911
|
+
reference: datasetReference,
|
|
11912
|
+
datasetId: manifest.datasetId,
|
|
11913
|
+
fingerprint: manifest.fingerprint,
|
|
11914
|
+
rows: [...manifest.cases]
|
|
11915
|
+
},
|
|
11916
|
+
evaluators
|
|
11917
|
+
};
|
|
11918
|
+
const materialized = await materializeSnapshot(options.snapshotPath, snapshot, options.judges);
|
|
11919
|
+
await writeSnapshot(options.snapshotPath, snapshot);
|
|
11920
|
+
return materialized;
|
|
11921
|
+
}
|
|
11922
|
+
async function loadEvalSnapshot(snapshotPath, options = {}) {
|
|
11923
|
+
const fs = await import("fs/promises");
|
|
11924
|
+
const parsed = EvalSnapshotSchema.parse(JSON.parse(await fs.readFile(snapshotPath, "utf8")));
|
|
11925
|
+
return materializeSnapshot(snapshotPath, parsed, options.judges);
|
|
11926
|
+
}
|
|
11927
|
+
async function pullEvaluator(client, queryUrl, rawSlug) {
|
|
11928
|
+
var _a;
|
|
11929
|
+
if (!rawSlug) throw new Error("Raindrop pullEval evaluator slugs cannot be empty");
|
|
11930
|
+
if (!((_a = client.connection.apiKey) == null ? void 0 : _a.trim())) {
|
|
11931
|
+
throw new Error("Raindrop pullEval requires an org API key for evaluator artifacts");
|
|
11932
|
+
}
|
|
11933
|
+
const response = await runWithTracingSuppressed(
|
|
11934
|
+
() => fetch(
|
|
11935
|
+
`${credentialEndpoint(queryUrl).href.replace(/\/+$/, "")}/v1/evals/${encodeURIComponent(rawSlug)}`,
|
|
11936
|
+
{
|
|
11937
|
+
redirect: "error",
|
|
11938
|
+
signal: AbortSignal.timeout(3e4),
|
|
11939
|
+
headers: {
|
|
11940
|
+
Authorization: `Bearer ${client.connection.apiKey}`,
|
|
11941
|
+
...client.connection.projectId ? { "X-Raindrop-Project-Id": client.connection.projectId } : {}
|
|
11942
|
+
}
|
|
11943
|
+
}
|
|
11944
|
+
)
|
|
11945
|
+
);
|
|
11946
|
+
if (!response.ok) {
|
|
11947
|
+
throw new Error(
|
|
11948
|
+
`Raindrop evaluator ${rawSlug} pull failed with ${response.status}: ${await response.text()}`
|
|
11949
|
+
);
|
|
11950
|
+
}
|
|
11951
|
+
const body = z4.object({
|
|
11952
|
+
data: z4.object({
|
|
11953
|
+
slug: z4.string(),
|
|
11954
|
+
name: z4.string(),
|
|
11955
|
+
artifact: z4.unknown().nullable()
|
|
11956
|
+
})
|
|
11957
|
+
}).parse(await response.json());
|
|
11958
|
+
if (!body.data.artifact)
|
|
11959
|
+
throw new Error(`Raindrop evaluator ${rawSlug} has no portable artifact`);
|
|
11960
|
+
const artifact = await verifyPortableEvalArtifact(body.data.artifact);
|
|
11961
|
+
if (body.data.slug !== rawSlug || artifact.evaluator.slug !== rawSlug) {
|
|
11962
|
+
throw new Error(`Raindrop evaluator ${rawSlug} returned mismatched artifact identity`);
|
|
11963
|
+
}
|
|
11964
|
+
const evaluator = importPortableEvaluator(artifact, {
|
|
11965
|
+
name: body.data.name
|
|
11966
|
+
});
|
|
11967
|
+
return {
|
|
11968
|
+
slug: body.data.slug,
|
|
11969
|
+
name: body.data.name,
|
|
11970
|
+
expectedSha256: evaluator.artifact.sha256,
|
|
11971
|
+
artifact: evaluator.artifact
|
|
11972
|
+
};
|
|
11973
|
+
}
|
|
11974
|
+
async function materializeSnapshot(snapshotPath, raw, judges) {
|
|
11975
|
+
const snapshot = EvalSnapshotSchema.parse(raw);
|
|
11976
|
+
const fingerprint = await datasetFingerprint(snapshot.dataset.datasetId, snapshot.dataset.rows);
|
|
11977
|
+
if (fingerprint !== snapshot.dataset.fingerprint) {
|
|
11978
|
+
throw new Error("Raindrop eval snapshot dataset fingerprint is invalid");
|
|
11979
|
+
}
|
|
11980
|
+
const dataset = defineDataset({
|
|
11981
|
+
id: snapshot.dataset.datasetId,
|
|
11982
|
+
name: snapshot.dataset.reference,
|
|
11983
|
+
version: snapshot.dataset.fingerprint,
|
|
11984
|
+
cases: snapshot.dataset.rows,
|
|
11985
|
+
remote: {
|
|
11986
|
+
reference: snapshot.dataset.reference,
|
|
11987
|
+
datasetId: snapshot.dataset.datasetId,
|
|
11988
|
+
fingerprint: snapshot.dataset.fingerprint
|
|
11989
|
+
}
|
|
11990
|
+
});
|
|
11991
|
+
const evaluators = await Promise.all(
|
|
11992
|
+
snapshot.evaluators.map(async (entry) => {
|
|
11993
|
+
const artifact = await verifyPortableEvalArtifact(entry.artifact, {
|
|
11994
|
+
expectedSha256: entry.expectedSha256
|
|
11995
|
+
});
|
|
11996
|
+
if (artifact.evaluator.slug !== entry.slug) {
|
|
11997
|
+
throw new Error(`Raindrop eval snapshot evaluator ${entry.slug} has mismatched identity`);
|
|
11998
|
+
}
|
|
11999
|
+
return importPortableEvaluator(artifact, {
|
|
12000
|
+
name: entry.name,
|
|
12001
|
+
expectedSha256: entry.expectedSha256,
|
|
12002
|
+
judge: judges == null ? void 0 : judges[entry.slug]
|
|
12003
|
+
});
|
|
12004
|
+
})
|
|
12005
|
+
);
|
|
12006
|
+
return { dataset, evaluators, snapshotPath };
|
|
12007
|
+
}
|
|
12008
|
+
async function datasetFingerprint(datasetId, rows) {
|
|
12009
|
+
const canonicalRows = rows.map((row) => ({
|
|
12010
|
+
id: row.id,
|
|
12011
|
+
name: row.name,
|
|
12012
|
+
input: row.input,
|
|
12013
|
+
output: row.output,
|
|
12014
|
+
properties: Object.fromEntries(
|
|
12015
|
+
Object.entries(row.properties).sort(([left], [right]) => compareCodeUnits(left, right))
|
|
12016
|
+
)
|
|
12017
|
+
})).sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
12018
|
+
const value = JSON.stringify({ dataset_id: datasetId, rows: canonicalRows });
|
|
12019
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
12020
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
12021
|
+
}
|
|
12022
|
+
function compareCodeUnits(left, right) {
|
|
12023
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
12024
|
+
}
|
|
12025
|
+
async function writeSnapshot(path, value) {
|
|
12026
|
+
const [fs, nodePath] = await Promise.all([import("fs/promises"), import("path")]);
|
|
12027
|
+
const absolute = nodePath.resolve(path);
|
|
12028
|
+
await fs.mkdir(nodePath.dirname(absolute), { recursive: true });
|
|
12029
|
+
const temporary = `${absolute}.${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`;
|
|
12030
|
+
try {
|
|
12031
|
+
await fs.writeFile(temporary, `${JSON.stringify(value, null, 2)}
|
|
12032
|
+
`, { flag: "wx" });
|
|
12033
|
+
await fs.rename(temporary, absolute);
|
|
12034
|
+
} catch (cause) {
|
|
12035
|
+
await fs.rm(temporary, { force: true }).catch(() => {
|
|
12036
|
+
});
|
|
12037
|
+
throw cause;
|
|
12038
|
+
}
|
|
12039
|
+
}
|
|
12040
|
+
|
|
12041
|
+
// src/evals/run.ts
|
|
12042
|
+
import { context as context2, trace as otelTrace } from "@opentelemetry/api";
|
|
12043
|
+
|
|
12044
|
+
// src/evals/policy.ts
|
|
12045
|
+
function assessEvalVerdict(evaluator, verdict, resolvedOutput) {
|
|
12046
|
+
var _a;
|
|
12047
|
+
const name = evaluatorName(evaluator);
|
|
12048
|
+
if (verdict.evaluator !== name) {
|
|
12049
|
+
throw new Error(`Raindrop evaluator ${name} received a verdict for ${verdict.evaluator}`);
|
|
12050
|
+
}
|
|
12051
|
+
if (verdict.state === "errored") return { state: "errored", evaluator: name, verdict };
|
|
12052
|
+
if (verdict.state === "ungraded") return { state: "ungraded", evaluator: name, verdict };
|
|
12053
|
+
const output = resolvedOutput != null ? resolvedOutput : evaluatorOutput(evaluator);
|
|
12054
|
+
if (output === void 0) {
|
|
12055
|
+
throw new Error(`Raindrop hosted evaluator ${name} requires its canonical output type`);
|
|
12056
|
+
}
|
|
12057
|
+
if (output === "boolean") {
|
|
12058
|
+
if (!("pass" in verdict)) throw outputMismatch(name, output);
|
|
12059
|
+
const threshold2 = evaluator.threshold;
|
|
12060
|
+
if (threshold2 && !("equals" in threshold2)) throw outputMismatch(name, output);
|
|
12061
|
+
const expected = (_a = threshold2 == null ? void 0 : threshold2.equals) != null ? _a : true;
|
|
12062
|
+
return {
|
|
12063
|
+
state: verdict.pass === expected ? "passed" : "failed",
|
|
12064
|
+
evaluator: name,
|
|
12065
|
+
verdict
|
|
12066
|
+
};
|
|
12067
|
+
}
|
|
12068
|
+
const value = output === "score" && "score" in verdict ? verdict.score : output === "number" && "value" in verdict ? verdict.value : null;
|
|
12069
|
+
if (value === null) throw outputMismatch(name, output);
|
|
12070
|
+
const threshold = evaluator.threshold;
|
|
12071
|
+
if (!threshold) return { state: "measurement", evaluator: name, verdict };
|
|
12072
|
+
if ("equals" in threshold) throw outputMismatch(name, output);
|
|
12073
|
+
const passesLower = !("gte" in threshold) || value >= threshold.gte;
|
|
12074
|
+
const passesUpper = threshold.lte === void 0 || value <= threshold.lte;
|
|
12075
|
+
return {
|
|
12076
|
+
state: passesLower && passesUpper ? "passed" : "failed",
|
|
12077
|
+
evaluator: name,
|
|
12078
|
+
verdict
|
|
12079
|
+
};
|
|
12080
|
+
}
|
|
12081
|
+
function outputMismatch(name, output) {
|
|
12082
|
+
return new Error(`Raindrop evaluator ${name} expected a ${output} verdict`);
|
|
12083
|
+
}
|
|
12084
|
+
|
|
12085
|
+
// src/evals/workshop.ts
|
|
12086
|
+
import { z as z5 } from "zod";
|
|
12087
|
+
var WorkshopRunSchema = z5.object({
|
|
12088
|
+
id: z5.string(),
|
|
12089
|
+
event_id: z5.string().nullable(),
|
|
12090
|
+
name: z5.string().nullable(),
|
|
12091
|
+
event_name: z5.string().nullable(),
|
|
12092
|
+
user_id: z5.string().nullable(),
|
|
12093
|
+
convo_id: z5.string().nullable(),
|
|
12094
|
+
started_at: z5.number().nullable(),
|
|
12095
|
+
last_updated_at: z5.number().nullable(),
|
|
12096
|
+
metadata: z5.string().nullable(),
|
|
12097
|
+
model: z5.string().nullable(),
|
|
12098
|
+
finished: z5.number().nullable()
|
|
12099
|
+
});
|
|
12100
|
+
var WorkshopSpanSchema = z5.object({
|
|
12101
|
+
id: z5.string(),
|
|
12102
|
+
run_id: z5.string(),
|
|
12103
|
+
parent_span_id: z5.string().nullable(),
|
|
12104
|
+
name: z5.string(),
|
|
12105
|
+
span_type: z5.string().nullable(),
|
|
12106
|
+
status: z5.string().nullable(),
|
|
12107
|
+
input_payload: z5.string().nullable(),
|
|
12108
|
+
output_payload: z5.string().nullable(),
|
|
12109
|
+
start_time_ms: z5.number().nullable(),
|
|
12110
|
+
end_time_ms: z5.number().nullable(),
|
|
12111
|
+
duration_ms: z5.number().nullable(),
|
|
12112
|
+
model: z5.string().nullable(),
|
|
12113
|
+
provider: z5.string().nullable(),
|
|
12114
|
+
input_tokens: z5.number().nullable(),
|
|
12115
|
+
output_tokens: z5.number().nullable(),
|
|
12116
|
+
attributes: z5.string().nullable()
|
|
12117
|
+
});
|
|
12118
|
+
var WorkshopTraceEnvelopeSchema = z5.object({
|
|
12119
|
+
status: z5.literal("complete"),
|
|
12120
|
+
correlationId: z5.string(),
|
|
12121
|
+
traceId: z5.string(),
|
|
12122
|
+
rootSpanId: z5.string(),
|
|
12123
|
+
spanCount: z5.number().int().positive(),
|
|
12124
|
+
run: WorkshopRunSchema,
|
|
12125
|
+
spans: z5.array(WorkshopSpanSchema).min(1)
|
|
12126
|
+
});
|
|
12127
|
+
async function readWorkshopTrace(input) {
|
|
12128
|
+
if (!Number.isFinite(input.waitMs) || input.waitMs < 0)
|
|
12129
|
+
throw new Error("Workshop waitMs must be finite and non-negative");
|
|
12130
|
+
const deadlineAt = Date.now() + input.waitMs;
|
|
12131
|
+
if (input.expectedSpanIds.length === 0) {
|
|
12132
|
+
throw new Error(`Workshop trace ${input.correlationId} exported no spans`);
|
|
12133
|
+
}
|
|
12134
|
+
const url = new URL(
|
|
12135
|
+
`${credentialEndpoint(input.workshopUrl).href.replace(/\/+$/, "")}/v1/evals/traces/${encodeURIComponent(input.correlationId)}`
|
|
12136
|
+
);
|
|
12137
|
+
for (const spanId of input.expectedSpanIds) url.searchParams.append("expectedSpanId", spanId);
|
|
12138
|
+
while (true) {
|
|
12139
|
+
const response = await runWithTracingSuppressed(
|
|
12140
|
+
() => fetch(url, {
|
|
12141
|
+
redirect: "error",
|
|
12142
|
+
signal: AbortSignal.timeout(
|
|
12143
|
+
Math.min(3e4, Math.max(1, Math.ceil(deadlineAt - Date.now())))
|
|
12144
|
+
)
|
|
12145
|
+
})
|
|
12146
|
+
);
|
|
12147
|
+
if (response.ok) {
|
|
12148
|
+
const parsed = WorkshopTraceEnvelopeSchema.safeParse(await response.json());
|
|
12149
|
+
if (!parsed.success) {
|
|
12150
|
+
throw new Error(`Workshop returned an invalid completed trace: ${parsed.error.message}`);
|
|
12151
|
+
}
|
|
12152
|
+
return parsed.data;
|
|
12153
|
+
}
|
|
12154
|
+
if (response.status !== 404 && response.status !== 409) {
|
|
12155
|
+
throw new Error(
|
|
12156
|
+
`Workshop trace read failed with ${response.status}: ${await response.text()}`
|
|
12157
|
+
);
|
|
12158
|
+
}
|
|
12159
|
+
if (Date.now() >= deadlineAt) {
|
|
12160
|
+
const state = response.status === 404 ? "missing" : "incomplete";
|
|
12161
|
+
throw new Error(`Workshop trace ${input.correlationId} remained ${state}`);
|
|
12162
|
+
}
|
|
12163
|
+
await new Promise(
|
|
12164
|
+
(resolve) => setTimeout(resolve, Math.min(50, Math.max(0, deadlineAt - Date.now())))
|
|
12165
|
+
);
|
|
12166
|
+
}
|
|
12167
|
+
}
|
|
12168
|
+
async function writeWorkshopEvalRun(workshopUrl, packet) {
|
|
12169
|
+
const normalized = {
|
|
12170
|
+
...packet,
|
|
12171
|
+
cases: packet.cases.map((entry) => ({
|
|
12172
|
+
...entry,
|
|
12173
|
+
task: entry.task.state === "completed" ? { ...entry.task, output: serializableOutput(entry.task.output) } : entry.task
|
|
12174
|
+
}))
|
|
12175
|
+
};
|
|
12176
|
+
const response = await runWithTracingSuppressed(
|
|
12177
|
+
() => fetch(`${credentialEndpoint(workshopUrl).href.replace(/\/+$/, "")}/v1/evals/runs`, {
|
|
12178
|
+
method: "POST",
|
|
12179
|
+
headers: { "content-type": "application/json" },
|
|
12180
|
+
body: JSON.stringify(normalized),
|
|
12181
|
+
redirect: "error",
|
|
12182
|
+
signal: AbortSignal.timeout(3e4)
|
|
12183
|
+
})
|
|
12184
|
+
);
|
|
12185
|
+
if (!response.ok) {
|
|
12186
|
+
throw new Error(
|
|
12187
|
+
`Workshop eval result write failed with ${response.status}: ${await response.text()}`
|
|
12188
|
+
);
|
|
12189
|
+
}
|
|
12190
|
+
const body = await response.json();
|
|
12191
|
+
if (typeof body !== "object" || body === null || !("id" in body) || typeof body.id !== "string") {
|
|
12192
|
+
throw new Error("Workshop eval result write returned an invalid response");
|
|
12193
|
+
}
|
|
12194
|
+
return { id: body.id };
|
|
12195
|
+
}
|
|
12196
|
+
function serializableOutput(value) {
|
|
12197
|
+
try {
|
|
12198
|
+
const serialized = JSON.stringify(boundedClone(value, 64e3));
|
|
12199
|
+
if (serialized === void 0) return null;
|
|
12200
|
+
if (serialized.length > 64e3) return "[Output exceeds 64000 characters]";
|
|
12201
|
+
return JSON.parse(serialized);
|
|
12202
|
+
} catch (e) {
|
|
12203
|
+
return "[Output could not be serialized]";
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
|
|
12207
|
+
// src/evals/run.ts
|
|
12208
|
+
var DEFAULT_WORKSHOP_TRACE_WAIT_MS = 3e4;
|
|
12209
|
+
function createEvalSuiteRun(client, definition, options) {
|
|
12210
|
+
const validated = validateDefinition(definition);
|
|
12211
|
+
if (options.destination.kind === "raindrop") {
|
|
12212
|
+
if (!("selection" in options)) {
|
|
12213
|
+
throw new Error("Raindrop remote eval sessions require selected cases");
|
|
12214
|
+
}
|
|
12215
|
+
if (typeof validated.dataset !== "string") {
|
|
12216
|
+
throw new Error("Raindrop remote eval sessions require a dataset slug");
|
|
12217
|
+
}
|
|
12218
|
+
if (options.selection.dataset.dataset.slug !== validated.dataset) {
|
|
12219
|
+
throw new Error(
|
|
12220
|
+
`Raindrop eval ${validated.name} pinned dataset ${options.selection.dataset.dataset.slug} does not match ${validated.dataset}`
|
|
12221
|
+
);
|
|
12222
|
+
}
|
|
12223
|
+
const localEvaluators = validated.evaluators.flatMap((entry) => {
|
|
12224
|
+
const evaluator = replayEvaluator(entry);
|
|
12225
|
+
return typeof evaluator === "string" ? [] : [evaluator];
|
|
12226
|
+
});
|
|
12227
|
+
if (localEvaluators.length !== validated.evaluators.length) {
|
|
12228
|
+
throw new Error("Raindrop case-scoped eval sessions support local evaluators only");
|
|
12229
|
+
}
|
|
12230
|
+
const selection = validateRemoteSelection(validated.dataset, options.selection);
|
|
12231
|
+
if (!selection) throw new Error("Raindrop remote eval sessions require selected cases");
|
|
12232
|
+
return new RaindropCaseEvalRun(
|
|
12233
|
+
client,
|
|
12234
|
+
validated,
|
|
12235
|
+
options.destination,
|
|
12236
|
+
options.selection.dataset,
|
|
12237
|
+
selection.caseIds,
|
|
12238
|
+
localEvaluators
|
|
12239
|
+
);
|
|
12240
|
+
}
|
|
12241
|
+
if (!isEvalDataset(validated.dataset)) {
|
|
12242
|
+
throw new Error("Workshop eval execution requires a pulled or local dataset snapshot");
|
|
12243
|
+
}
|
|
12244
|
+
credentialEndpoint(options.destination.url);
|
|
12245
|
+
const hosted = validated.evaluators.find((entry) => typeof entry.evaluator === "string");
|
|
12246
|
+
if (hosted && typeof hosted.evaluator === "string") {
|
|
12247
|
+
throw new Error(`Workshop evaluator ${hosted.evaluator} must be pulled before local execution`);
|
|
12248
|
+
}
|
|
12249
|
+
if (validated.evaluators.some(
|
|
12250
|
+
(entry) => typeof entry.evaluator !== "string" && "kind" in entry.evaluator && entry.evaluator.kind === "program"
|
|
12251
|
+
)) {
|
|
12252
|
+
throw new Error(
|
|
12253
|
+
"Workshop authored evaluator programs must be pulled as portable evaluators before local execution"
|
|
12254
|
+
);
|
|
12255
|
+
}
|
|
12256
|
+
return new WorkshopEvalRun(
|
|
12257
|
+
client,
|
|
12258
|
+
{ ...validated, dataset: validated.dataset },
|
|
12259
|
+
options.destination
|
|
12260
|
+
);
|
|
12261
|
+
}
|
|
12262
|
+
async function runEvalSuite(client, definition, options) {
|
|
12263
|
+
var _a, _b, _c, _d;
|
|
12264
|
+
const validated = validateDefinition(definition);
|
|
12265
|
+
if (options === void 0) throw new Error("Raindrop runEvalSuite requires an explicit destination");
|
|
12266
|
+
if (options.destination.kind === "workshop") {
|
|
12267
|
+
const session = createEvalSuiteRun(client, validated, { destination: options.destination });
|
|
12268
|
+
await session.run({ selection: options.selection, attempt: options.attempt });
|
|
12269
|
+
return session.finish();
|
|
12270
|
+
}
|
|
12271
|
+
if (isCaseScopedLocalEval(validated)) {
|
|
12272
|
+
if (typeof validated.dataset !== "string") {
|
|
12273
|
+
throw new Error("Raindrop remote execution requires an eval dataset slug");
|
|
12274
|
+
}
|
|
12275
|
+
const dataset = (_b = (_a = options.selection) == null ? void 0 : _a.dataset) != null ? _b : await readEvalDataset(client, validated.dataset, {
|
|
12276
|
+
queryUrl: options.destination.queryUrl
|
|
12277
|
+
});
|
|
12278
|
+
const selection = validateRemoteSelection(validated.dataset, {
|
|
12279
|
+
dataset,
|
|
12280
|
+
caseIds: (_d = (_c = options.selection) == null ? void 0 : _c.caseIds) != null ? _d : dataset.cases.map((entry) => entry.id)
|
|
12281
|
+
});
|
|
12282
|
+
if (!selection) throw new Error("Raindrop remote eval requires selected cases");
|
|
12283
|
+
const session = createEvalSuiteRun(client, validated, {
|
|
12284
|
+
destination: options.destination,
|
|
12285
|
+
selection: { dataset, caseIds: selection.caseIds }
|
|
12286
|
+
});
|
|
12287
|
+
const attempts = await Promise.allSettled(
|
|
12288
|
+
selection.caseIds.map(
|
|
12289
|
+
(caseId) => session.run({ selection: { dataset, caseIds: [caseId] }, attempt: 0 })
|
|
12290
|
+
)
|
|
12291
|
+
);
|
|
12292
|
+
const failures = attempts.filter(
|
|
12293
|
+
(attempt) => attempt.status === "rejected"
|
|
12294
|
+
);
|
|
12295
|
+
let finished;
|
|
12296
|
+
try {
|
|
12297
|
+
finished = await session.finish();
|
|
12298
|
+
} catch (cause) {
|
|
12299
|
+
if (failures.length > 0) {
|
|
12300
|
+
throw new AggregateError(
|
|
12301
|
+
[...failures.map((failure) => failure.reason), cause],
|
|
12302
|
+
"Raindrop eval case execution and run finalization failed"
|
|
12303
|
+
);
|
|
12304
|
+
}
|
|
12305
|
+
throw cause;
|
|
12306
|
+
}
|
|
12307
|
+
const onlyFailure = failures.length === 1 ? failures[0] : void 0;
|
|
12308
|
+
if (onlyFailure) throw onlyFailure.reason;
|
|
12309
|
+
if (failures.length > 1) {
|
|
12310
|
+
throw new AggregateError(
|
|
12311
|
+
failures.map((failure) => failure.reason),
|
|
12312
|
+
"Multiple Raindrop eval cases failed"
|
|
12313
|
+
);
|
|
12314
|
+
}
|
|
12315
|
+
return finished;
|
|
12316
|
+
}
|
|
12317
|
+
return runRaindropEval(client, validated, options.destination, options);
|
|
12318
|
+
}
|
|
12319
|
+
function isCaseScopedLocalEval(definition) {
|
|
12320
|
+
return definition.evaluators.every(
|
|
12321
|
+
(entry) => typeof entry.evaluator !== "string" && entry.evaluator.scope === "case" && !("kind" in entry.evaluator)
|
|
12322
|
+
);
|
|
12323
|
+
}
|
|
12324
|
+
var RaindropCaseEvalRun = class {
|
|
12325
|
+
constructor(client, definition, destination, dataset, caseIds, evaluators) {
|
|
12326
|
+
this.client = client;
|
|
12327
|
+
this.definition = definition;
|
|
12328
|
+
this.destination = destination;
|
|
12329
|
+
this.dataset = dataset;
|
|
12330
|
+
this.caseIds = caseIds;
|
|
12331
|
+
this.evaluators = evaluators;
|
|
12332
|
+
this.id = randomId();
|
|
12333
|
+
this.active = /* @__PURE__ */ new Set();
|
|
12334
|
+
this.completed = [];
|
|
12335
|
+
this.attempts = /* @__PURE__ */ new Set();
|
|
12336
|
+
this.state = "open";
|
|
12337
|
+
this.runningCases = 0;
|
|
12338
|
+
this.caseWaiters = [];
|
|
12339
|
+
}
|
|
12340
|
+
async run(options = {}) {
|
|
12341
|
+
if (this.state !== "open") throw new Error(`Raindrop eval run ${this.id} is already finishing`);
|
|
12342
|
+
const operation = this.runOnce(options);
|
|
12343
|
+
this.active.add(operation);
|
|
12344
|
+
try {
|
|
12345
|
+
return await operation;
|
|
12346
|
+
} finally {
|
|
12347
|
+
this.active.delete(operation);
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
12350
|
+
finish() {
|
|
12351
|
+
if (this.finishPromise) return this.finishPromise;
|
|
12352
|
+
this.state = "finishing";
|
|
12353
|
+
this.finishPromise = this.finishOnce();
|
|
12354
|
+
return this.finishPromise;
|
|
12355
|
+
}
|
|
12356
|
+
async runOnce(options) {
|
|
12357
|
+
var _a, _b;
|
|
12358
|
+
const selected = (_a = options.selection) == null ? void 0 : _a.caseIds;
|
|
12359
|
+
if (!selected || selected.length !== 1) {
|
|
12360
|
+
throw new Error("Raindrop case-scoped eval session run requires exactly one case");
|
|
12361
|
+
}
|
|
12362
|
+
const caseId = selected[0];
|
|
12363
|
+
if (caseId === void 0 || !this.caseIds.includes(caseId)) {
|
|
12364
|
+
throw new Error(`Raindrop eval ${this.definition.name} did not select a planned case`);
|
|
12365
|
+
}
|
|
12366
|
+
const attempt = (_b = options.attempt) != null ? _b : 0;
|
|
12367
|
+
const attemptKey = `${caseId}:${attempt}`;
|
|
12368
|
+
if (this.attempts.has(attemptKey)) {
|
|
12369
|
+
throw new Error(`Raindrop eval case ${caseId} attempt ${attempt} already ran`);
|
|
12370
|
+
}
|
|
12371
|
+
this.attempts.add(attemptKey);
|
|
12372
|
+
return this.withCasePermit(async () => {
|
|
12373
|
+
const replay2 = await this.getReplay();
|
|
12374
|
+
const attemptResult = await replay2.run({
|
|
12375
|
+
rowId: caseId,
|
|
12376
|
+
attempt,
|
|
12377
|
+
run: this.definition.run,
|
|
12378
|
+
traceWaitMs: this.definition.traceWaitMs
|
|
12379
|
+
});
|
|
12380
|
+
const configured = new Map(
|
|
12381
|
+
this.definition.evaluators.map((entry) => [evaluatorName(entry), entry])
|
|
12382
|
+
);
|
|
12383
|
+
const result = {
|
|
12384
|
+
...attemptResult.row,
|
|
12385
|
+
attempt,
|
|
12386
|
+
status: attemptResult.status,
|
|
12387
|
+
traceId: attemptResult.traceId,
|
|
12388
|
+
error: attemptResult.error,
|
|
12389
|
+
verdicts: attemptResult.verdicts.map((verdict) => {
|
|
12390
|
+
const evaluator = configured.get(verdict.evaluator);
|
|
12391
|
+
if (!evaluator) {
|
|
12392
|
+
throw new Error(`Raindrop eval result included unknown evaluator ${verdict.evaluator}`);
|
|
12393
|
+
}
|
|
12394
|
+
return assessEvalVerdict(evaluator, verdict, evaluatorOutput(evaluator));
|
|
12395
|
+
})
|
|
12396
|
+
};
|
|
12397
|
+
this.completed.push(result);
|
|
12398
|
+
return {
|
|
12399
|
+
replayId: replay2.replayId,
|
|
12400
|
+
name: this.definition.name,
|
|
12401
|
+
status: "running",
|
|
12402
|
+
counts: {
|
|
12403
|
+
total: 1,
|
|
12404
|
+
pending: 0,
|
|
12405
|
+
done: result.status === "done" ? 1 : 0,
|
|
12406
|
+
missing: result.status === "missing" ? 1 : 0
|
|
12407
|
+
},
|
|
12408
|
+
tracesExpireAt: null,
|
|
12409
|
+
tracesExpired: false,
|
|
12410
|
+
evaluators: replayEvaluatorsFromVerdicts(attemptResult.verdicts),
|
|
12411
|
+
cases: [result]
|
|
12412
|
+
};
|
|
12413
|
+
});
|
|
12414
|
+
}
|
|
12415
|
+
async finishOnce() {
|
|
12416
|
+
await Promise.allSettled([...this.active]);
|
|
12417
|
+
const replay2 = await this.getReplay();
|
|
12418
|
+
const finished = await replay2.finish();
|
|
12419
|
+
const { rows: _rows, ...reading } = finished.reading;
|
|
12420
|
+
this.state = "finished";
|
|
12421
|
+
return {
|
|
12422
|
+
...reading,
|
|
12423
|
+
name: this.definition.name,
|
|
12424
|
+
evaluators: finished.evaluators,
|
|
12425
|
+
cases: this.sortedResults()
|
|
12426
|
+
};
|
|
12427
|
+
}
|
|
12428
|
+
getReplay() {
|
|
12429
|
+
if (!this.replayPromise) {
|
|
12430
|
+
this.replayPromise = createLocalEvalReplaySession(this.client, {
|
|
12431
|
+
dataset: this.dataset,
|
|
12432
|
+
rowIds: this.caseIds,
|
|
12433
|
+
evaluators: this.evaluators,
|
|
12434
|
+
queryUrl: this.destination.queryUrl,
|
|
12435
|
+
replayIngestUrl: this.destination.replayIngestUrl
|
|
12436
|
+
});
|
|
12437
|
+
}
|
|
12438
|
+
return this.replayPromise;
|
|
12439
|
+
}
|
|
12440
|
+
async withCasePermit(run) {
|
|
12441
|
+
var _a;
|
|
12442
|
+
const limit = (_a = this.definition.concurrency) != null ? _a : 4;
|
|
12443
|
+
if (this.runningCases >= limit) {
|
|
12444
|
+
await new Promise((resolve) => this.caseWaiters.push(resolve));
|
|
12445
|
+
} else {
|
|
12446
|
+
this.runningCases += 1;
|
|
12447
|
+
}
|
|
12448
|
+
try {
|
|
12449
|
+
return await run();
|
|
12450
|
+
} finally {
|
|
12451
|
+
const next = this.caseWaiters.shift();
|
|
12452
|
+
if (next) next();
|
|
12453
|
+
else this.runningCases -= 1;
|
|
12454
|
+
}
|
|
12455
|
+
}
|
|
12456
|
+
sortedResults() {
|
|
12457
|
+
const order = new Map(this.caseIds.map((caseId, index) => [caseId, index]));
|
|
12458
|
+
return [...this.completed].sort((left, right) => {
|
|
12459
|
+
var _a, _b, _c, _d;
|
|
12460
|
+
const caseOrder = ((_a = order.get(left.id)) != null ? _a : Number.MAX_SAFE_INTEGER) - ((_b = order.get(right.id)) != null ? _b : Number.MAX_SAFE_INTEGER);
|
|
12461
|
+
return caseOrder || ((_c = left.attempt) != null ? _c : 0) - ((_d = right.attempt) != null ? _d : 0);
|
|
12462
|
+
});
|
|
12463
|
+
}
|
|
12464
|
+
};
|
|
12465
|
+
function replayEvaluatorsFromVerdicts(verdicts) {
|
|
12466
|
+
const evaluators = /* @__PURE__ */ new Map();
|
|
12467
|
+
for (const verdict of verdicts) {
|
|
12468
|
+
if (evaluators.has(verdict.evaluator)) continue;
|
|
12469
|
+
evaluators.set(verdict.evaluator, {
|
|
12470
|
+
evaluator: verdict.evaluator,
|
|
12471
|
+
runId: verdict.runId,
|
|
12472
|
+
status: "completed",
|
|
12473
|
+
url: verdict.url,
|
|
12474
|
+
summary: null,
|
|
12475
|
+
error: null
|
|
12476
|
+
});
|
|
12477
|
+
}
|
|
12478
|
+
return [...evaluators.values()];
|
|
12479
|
+
}
|
|
12480
|
+
var WorkshopEvalRun = class {
|
|
12481
|
+
constructor(client, definition, destination) {
|
|
12482
|
+
this.client = client;
|
|
12483
|
+
this.definition = definition;
|
|
12484
|
+
this.destination = destination;
|
|
12485
|
+
this.id = randomId();
|
|
12486
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
12487
|
+
this.completed = [];
|
|
12488
|
+
this.active = /* @__PURE__ */ new Set();
|
|
12489
|
+
this.state = "open";
|
|
12490
|
+
this.runningCases = 0;
|
|
12491
|
+
this.caseWaiters = [];
|
|
12492
|
+
}
|
|
12493
|
+
async run(options = {}) {
|
|
12494
|
+
if (this.state !== "open") throw new Error(`Raindrop eval run ${this.id} is already finishing`);
|
|
12495
|
+
const operation = this.runOnce(options);
|
|
12496
|
+
this.active.add(operation);
|
|
12497
|
+
try {
|
|
12498
|
+
return await operation;
|
|
12499
|
+
} finally {
|
|
12500
|
+
this.active.delete(operation);
|
|
12501
|
+
}
|
|
12502
|
+
}
|
|
12503
|
+
async runOnce(options) {
|
|
12504
|
+
var _a;
|
|
12505
|
+
const cases = selectLocalCases(this.definition, options.selection);
|
|
12506
|
+
const completed = await Promise.all(
|
|
12507
|
+
cases.map((row) => this.withCasePermit(() => {
|
|
12508
|
+
var _a2;
|
|
12509
|
+
return this.executeCase(row, (_a2 = options.attempt) != null ? _a2 : 0);
|
|
12510
|
+
}))
|
|
12511
|
+
);
|
|
12512
|
+
const graded = await this.applyPortableEvaluators(completed, (_a = options.attempt) != null ? _a : 0);
|
|
12513
|
+
this.completed.push(...graded);
|
|
12514
|
+
return workshopResult(
|
|
12515
|
+
this.id,
|
|
12516
|
+
this.definition.name,
|
|
12517
|
+
graded.map((entry) => entry.result)
|
|
12518
|
+
);
|
|
12519
|
+
}
|
|
12520
|
+
finish() {
|
|
12521
|
+
if (this.finishPromise) return this.finishPromise;
|
|
12522
|
+
this.state = "finishing";
|
|
12523
|
+
this.finishPromise = this.finishOnce();
|
|
12524
|
+
return this.finishPromise;
|
|
12525
|
+
}
|
|
12526
|
+
async finishOnce() {
|
|
12527
|
+
await Promise.allSettled([...this.active]);
|
|
12528
|
+
const result = workshopResult(
|
|
12529
|
+
this.id,
|
|
12530
|
+
this.definition.name,
|
|
12531
|
+
this.completed.map((entry) => entry.result)
|
|
12532
|
+
);
|
|
12533
|
+
if (this.completed.length > 0) {
|
|
12534
|
+
const packet = {
|
|
12535
|
+
schemaVersion: 1,
|
|
12536
|
+
run: {
|
|
12537
|
+
id: this.id,
|
|
12538
|
+
name: this.definition.name,
|
|
12539
|
+
destination: "workshop",
|
|
12540
|
+
startedAt: this.startedAt.toISOString(),
|
|
12541
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12542
|
+
dataset: {
|
|
12543
|
+
id: this.definition.dataset.id,
|
|
12544
|
+
name: this.definition.dataset.name,
|
|
12545
|
+
version: this.definition.dataset.version
|
|
12546
|
+
},
|
|
12547
|
+
evaluators: this.definition.evaluators.map((entry) => {
|
|
12548
|
+
if (typeof entry.evaluator === "string") {
|
|
12549
|
+
throw new Error(
|
|
12550
|
+
`Workshop evaluator ${entry.evaluator} must be pulled before local execution`
|
|
12551
|
+
);
|
|
12552
|
+
}
|
|
12553
|
+
if (isPortableEvaluator(entry.evaluator)) {
|
|
12554
|
+
return {
|
|
12555
|
+
slug: entry.evaluator.slug,
|
|
12556
|
+
name: entry.evaluator.name,
|
|
12557
|
+
output: entry.evaluator.output,
|
|
12558
|
+
execution: "portable",
|
|
12559
|
+
version: entry.evaluator.artifact.evaluator.programVersion,
|
|
12560
|
+
artifactSha256: entry.evaluator.artifact.sha256,
|
|
12561
|
+
scope: entry.evaluator.scope,
|
|
12562
|
+
threshold: entry.threshold
|
|
12563
|
+
};
|
|
12564
|
+
}
|
|
12565
|
+
return {
|
|
12566
|
+
slug: entry.evaluator.slug,
|
|
12567
|
+
name: entry.evaluator.name,
|
|
12568
|
+
output: entry.evaluator.output,
|
|
12569
|
+
execution: "local",
|
|
12570
|
+
scope: entry.evaluator.scope,
|
|
12571
|
+
threshold: entry.threshold
|
|
12572
|
+
};
|
|
12573
|
+
})
|
|
12574
|
+
},
|
|
12575
|
+
cases: this.completed.map((entry) => entry.packet)
|
|
12576
|
+
};
|
|
12577
|
+
await writeWorkshopEvalRun(this.destination.url, packet);
|
|
12578
|
+
}
|
|
12579
|
+
this.state = "finished";
|
|
12580
|
+
return result;
|
|
12581
|
+
}
|
|
12582
|
+
async executeCase(row, attempt) {
|
|
12583
|
+
var _a, _b, _c;
|
|
12584
|
+
const correlationId = randomId();
|
|
12585
|
+
const exportedSpanIds = /* @__PURE__ */ new Set();
|
|
12586
|
+
const startedAt = Date.now();
|
|
12587
|
+
let completedAgent;
|
|
12588
|
+
let taskError;
|
|
12589
|
+
try {
|
|
12590
|
+
const destination = {
|
|
12591
|
+
kind: "workshop",
|
|
12592
|
+
url: this.destination.url,
|
|
12593
|
+
exportedSpanIds
|
|
12594
|
+
};
|
|
12595
|
+
const value = await withReplayTraceDestination(
|
|
12596
|
+
destination,
|
|
12597
|
+
() => withEvalScope(
|
|
12598
|
+
{ correlationId },
|
|
12599
|
+
() => this.client.tracer().withSpan(
|
|
12600
|
+
{
|
|
12601
|
+
name: "raindrop.eval.case",
|
|
12602
|
+
properties: {
|
|
12603
|
+
"raindrop.eval.run_id": this.id,
|
|
12604
|
+
"raindrop.eval.case_id": row.id,
|
|
12605
|
+
"raindrop.eval.attempt": String(attempt)
|
|
12606
|
+
},
|
|
12607
|
+
inputParameters: [row],
|
|
12608
|
+
traceContent: true
|
|
12609
|
+
},
|
|
12610
|
+
() => {
|
|
12611
|
+
var _a2;
|
|
12612
|
+
const parent = (_a2 = otelTrace.getSpan(context2.active())) == null ? void 0 : _a2.spanContext();
|
|
12613
|
+
if (!parent) return this.definition.run(row);
|
|
12614
|
+
return withReplayTraceDestination(
|
|
12615
|
+
{
|
|
12616
|
+
...destination,
|
|
12617
|
+
parentSpanContext: {
|
|
12618
|
+
traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
|
|
12619
|
+
spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
|
|
12620
|
+
eventId: parent.traceId
|
|
12621
|
+
}
|
|
12622
|
+
},
|
|
12623
|
+
() => this.definition.run(row)
|
|
12624
|
+
);
|
|
12625
|
+
}
|
|
12626
|
+
)
|
|
12627
|
+
)
|
|
12628
|
+
);
|
|
12629
|
+
completedAgent = { value };
|
|
12630
|
+
} catch (cause) {
|
|
12631
|
+
taskError = describeError2(cause);
|
|
12632
|
+
}
|
|
12633
|
+
const durationMs = Date.now() - startedAt;
|
|
12634
|
+
const taskCompleted = completedAgent !== void 0;
|
|
12635
|
+
let trace2;
|
|
12636
|
+
let traceId = null;
|
|
12637
|
+
let traceError;
|
|
12638
|
+
try {
|
|
12639
|
+
await this.client.forceFlush();
|
|
12640
|
+
const envelope = await readWorkshopTrace({
|
|
12641
|
+
workshopUrl: this.destination.url,
|
|
12642
|
+
correlationId,
|
|
12643
|
+
waitMs: (_a = this.definition.traceWaitMs) != null ? _a : DEFAULT_WORKSHOP_TRACE_WAIT_MS,
|
|
12644
|
+
expectedSpanIds: [...exportedSpanIds]
|
|
12645
|
+
});
|
|
12646
|
+
traceId = envelope.traceId;
|
|
12647
|
+
trace2 = canonicalWorkshopTrace(envelope);
|
|
12648
|
+
} catch (cause) {
|
|
12649
|
+
traceError = describeError2(cause);
|
|
12650
|
+
}
|
|
12651
|
+
const verdicts = [];
|
|
12652
|
+
if (completedAgent && !traceError && trace2) {
|
|
12653
|
+
const agentResult = completedAgent.value;
|
|
12654
|
+
await Promise.all(
|
|
12655
|
+
this.definition.evaluators.map(async (entry) => {
|
|
12656
|
+
if (typeof entry.evaluator === "string") return;
|
|
12657
|
+
if (isPortableEvaluator(entry.evaluator)) return;
|
|
12658
|
+
if ("kind" in entry.evaluator && entry.evaluator.kind === "program") return;
|
|
12659
|
+
try {
|
|
12660
|
+
const verdict = parseLocalVerdict2(
|
|
12661
|
+
entry.evaluator.output,
|
|
12662
|
+
await entry.evaluator.judge({ trace: trace2, row, result: agentResult })
|
|
12663
|
+
);
|
|
12664
|
+
verdicts.push(
|
|
12665
|
+
withVerdictIdentity(entry.evaluator.slug, this.id, this.destination.url, verdict)
|
|
12666
|
+
);
|
|
12667
|
+
} catch (cause) {
|
|
12668
|
+
verdicts.push({
|
|
12669
|
+
evaluator: entry.evaluator.slug,
|
|
12670
|
+
runId: this.id,
|
|
12671
|
+
url: workshopEvalUrl(this.destination.url, this.id),
|
|
12672
|
+
state: "errored",
|
|
12673
|
+
message: describeError2(cause)
|
|
12674
|
+
});
|
|
12675
|
+
}
|
|
12676
|
+
})
|
|
12677
|
+
);
|
|
12678
|
+
}
|
|
12679
|
+
if (taskCompleted && traceError) {
|
|
12680
|
+
for (const entry of this.definition.evaluators) {
|
|
12681
|
+
if (typeof entry.evaluator === "string") continue;
|
|
12682
|
+
verdicts.push({
|
|
12683
|
+
evaluator: entry.evaluator.slug,
|
|
12684
|
+
runId: this.id,
|
|
12685
|
+
url: workshopEvalUrl(this.destination.url, this.id),
|
|
12686
|
+
state: "ungraded",
|
|
12687
|
+
reason: "trace unavailable"
|
|
12688
|
+
});
|
|
12689
|
+
}
|
|
12690
|
+
}
|
|
12691
|
+
const assessments = verdicts.map((verdict) => {
|
|
12692
|
+
const evaluator = this.definition.evaluators.find(
|
|
12693
|
+
(entry) => evaluatorName(entry) === verdict.evaluator
|
|
12694
|
+
);
|
|
12695
|
+
if (!evaluator)
|
|
12696
|
+
throw new Error(`Raindrop eval result included unknown evaluator ${verdict.evaluator}`);
|
|
12697
|
+
return assessEvalVerdict(evaluator, verdict, evaluatorOutput(evaluator));
|
|
12698
|
+
});
|
|
12699
|
+
const error = (_b = taskError != null ? taskError : traceError) != null ? _b : null;
|
|
12700
|
+
const result = {
|
|
12701
|
+
...row,
|
|
12702
|
+
attempt,
|
|
12703
|
+
status: error ? "missing" : "done",
|
|
12704
|
+
traceId,
|
|
12705
|
+
error,
|
|
12706
|
+
verdicts: assessments
|
|
12707
|
+
};
|
|
12708
|
+
const packet = {
|
|
12709
|
+
caseId: row.id,
|
|
12710
|
+
caseName: row.name,
|
|
12711
|
+
input: row.input,
|
|
12712
|
+
...row.output === null ? {} : { expected: row.output },
|
|
12713
|
+
attempt,
|
|
12714
|
+
traceId,
|
|
12715
|
+
...taskCompleted && traceError ? { traceError } : {},
|
|
12716
|
+
task: taskError ? { state: "failed", error: taskError, durationMs } : { state: "completed", output: (_c = completedAgent == null ? void 0 : completedAgent.value) != null ? _c : null, durationMs },
|
|
12717
|
+
verdicts: verdicts.map(workshopVerdict),
|
|
12718
|
+
passed: error === null && assessments.every(
|
|
12719
|
+
(assessment) => assessment.state === "passed" || assessment.state === "measurement"
|
|
12720
|
+
)
|
|
12721
|
+
};
|
|
12722
|
+
return { result, packet, trace: trace2 };
|
|
12723
|
+
}
|
|
12724
|
+
async withCasePermit(run) {
|
|
12725
|
+
var _a;
|
|
12726
|
+
const limit = (_a = this.definition.concurrency) != null ? _a : 4;
|
|
12727
|
+
if (this.runningCases >= limit) {
|
|
12728
|
+
await new Promise((resolve) => this.caseWaiters.push(resolve));
|
|
12729
|
+
} else {
|
|
12730
|
+
this.runningCases += 1;
|
|
12731
|
+
}
|
|
12732
|
+
try {
|
|
12733
|
+
return await run();
|
|
12734
|
+
} finally {
|
|
12735
|
+
const next = this.caseWaiters.shift();
|
|
12736
|
+
if (next) next();
|
|
12737
|
+
else this.runningCases -= 1;
|
|
12738
|
+
}
|
|
12739
|
+
}
|
|
12740
|
+
async applyPortableEvaluators(completed, attempt) {
|
|
12741
|
+
let current = completed;
|
|
12742
|
+
for (const entry of this.definition.evaluators) {
|
|
12743
|
+
if (typeof entry.evaluator === "string" || !isPortableEvaluator(entry.evaluator)) continue;
|
|
12744
|
+
const portableEvaluator = entry.evaluator;
|
|
12745
|
+
const traces = current.flatMap(
|
|
12746
|
+
(item) => item.packet.task.state === "completed" && item.trace ? [item.trace] : []
|
|
12747
|
+
);
|
|
12748
|
+
if (traces.length === 0) continue;
|
|
12749
|
+
let outcomes;
|
|
12750
|
+
try {
|
|
12751
|
+
const portable = await evaluatePortableEvaluator(portableEvaluator, {
|
|
12752
|
+
traces,
|
|
12753
|
+
seed: `${this.id}:${attempt}:${portableEvaluator.slug}`
|
|
12754
|
+
});
|
|
12755
|
+
const programFailure = portable.failures.join("; ");
|
|
12756
|
+
outcomes = new Map(
|
|
12757
|
+
portable.outcomes.map((outcome) => [
|
|
12758
|
+
outcome.traceId,
|
|
12759
|
+
programFailure ? {
|
|
12760
|
+
state: "errored",
|
|
12761
|
+
traceId: outcome.traceId,
|
|
12762
|
+
message: programFailure
|
|
12763
|
+
} : outcome
|
|
12764
|
+
])
|
|
12765
|
+
);
|
|
12766
|
+
} catch (cause) {
|
|
12767
|
+
const message = describeError2(cause);
|
|
12768
|
+
outcomes = new Map(
|
|
12769
|
+
traces.map((trace2) => [
|
|
12770
|
+
trace2.event.id,
|
|
12771
|
+
{ state: "errored", traceId: trace2.event.id, message }
|
|
12772
|
+
])
|
|
12773
|
+
);
|
|
12774
|
+
}
|
|
12775
|
+
current = current.map((item) => {
|
|
12776
|
+
if (item.packet.task.state !== "completed" || !item.trace) return item;
|
|
12777
|
+
const outcome = outcomes.get(item.trace.event.id);
|
|
12778
|
+
if (!outcome) return item;
|
|
12779
|
+
const verdict = portableVerdict(
|
|
12780
|
+
portableEvaluator.slug,
|
|
12781
|
+
this.id,
|
|
12782
|
+
this.destination.url,
|
|
12783
|
+
outcome
|
|
12784
|
+
);
|
|
12785
|
+
const assessment = assessEvalVerdict(entry, verdict, portableEvaluator.output);
|
|
12786
|
+
const verdicts = [...item.result.verdicts, assessment];
|
|
12787
|
+
return {
|
|
12788
|
+
...item,
|
|
12789
|
+
result: { ...item.result, verdicts },
|
|
12790
|
+
packet: {
|
|
12791
|
+
...item.packet,
|
|
12792
|
+
verdicts: [...item.packet.verdicts, workshopVerdict(verdict)],
|
|
12793
|
+
passed: item.packet.passed && (assessment.state === "passed" || assessment.state === "measurement")
|
|
12794
|
+
}
|
|
12795
|
+
};
|
|
12796
|
+
});
|
|
12797
|
+
}
|
|
12798
|
+
return current;
|
|
12799
|
+
}
|
|
12800
|
+
};
|
|
12801
|
+
async function runRaindropEval(client, definition, destination, options) {
|
|
12802
|
+
var _a, _b, _c;
|
|
12803
|
+
if (typeof definition.dataset !== "string") {
|
|
12804
|
+
throw new Error(
|
|
12805
|
+
"Raindrop remote execution requires an eval dataset slug; legacy pulled snapshots do not carry an immutable dataset version id"
|
|
12806
|
+
);
|
|
12807
|
+
}
|
|
12808
|
+
const datasetReference = definition.dataset;
|
|
12809
|
+
const pinnedDataset = (_a = options.selection) == null ? void 0 : _a.dataset;
|
|
12810
|
+
if (pinnedDataset && pinnedDataset.dataset.slug !== datasetReference) {
|
|
12811
|
+
throw new Error(
|
|
12812
|
+
`Raindrop eval ${definition.name} pinned dataset ${pinnedDataset.dataset.slug} does not match ${datasetReference}`
|
|
12813
|
+
);
|
|
12814
|
+
}
|
|
12815
|
+
const snapshotSelection = options.selection;
|
|
12816
|
+
const selection = validateRemoteSelection(datasetReference, snapshotSelection);
|
|
12817
|
+
const configured = new Map(
|
|
12818
|
+
definition.evaluators.map((evaluator) => [evaluatorName(evaluator), evaluator])
|
|
12819
|
+
);
|
|
12820
|
+
const replayOptions = {
|
|
12821
|
+
dataset: pinnedDataset != null ? pinnedDataset : datasetReference,
|
|
12822
|
+
evaluators: definition.evaluators.map((entry) => replayEvaluator(entry)),
|
|
12823
|
+
evaluatorPins: hostedEvaluatorPins(definition.evaluators),
|
|
12824
|
+
run: definition.run,
|
|
12825
|
+
concurrency: definition.concurrency,
|
|
12826
|
+
traceWaitMs: definition.traceWaitMs,
|
|
12827
|
+
evalWaitMs: definition.evalWaitMs,
|
|
12828
|
+
evalPollIntervalMs: definition.evalPollIntervalMs,
|
|
12829
|
+
queryUrl: destination.queryUrl,
|
|
12830
|
+
replayIngestUrl: destination.replayIngestUrl
|
|
12831
|
+
};
|
|
12832
|
+
const result = await replay(
|
|
12833
|
+
client,
|
|
12834
|
+
selection ? {
|
|
12835
|
+
...replayOptions,
|
|
12836
|
+
rowIds: selection.caseIds
|
|
12837
|
+
} : replayOptions
|
|
12838
|
+
);
|
|
12839
|
+
const outputByEvaluator = /* @__PURE__ */ new Map();
|
|
12840
|
+
for (const evaluatorResult of result.evaluators) {
|
|
12841
|
+
const evaluator = configured.get(evaluatorResult.evaluator);
|
|
12842
|
+
if (!evaluator)
|
|
12843
|
+
throw new Error(
|
|
12844
|
+
`Raindrop eval result included unknown evaluator ${evaluatorResult.evaluator}`
|
|
12845
|
+
);
|
|
12846
|
+
if (evaluatorResult.status === "failed") {
|
|
12847
|
+
continue;
|
|
12848
|
+
}
|
|
12849
|
+
const output = (_c = (_b = evaluatorResult.summary) == null ? void 0 : _b.outputType) != null ? _c : evaluatorOutput(evaluator);
|
|
12850
|
+
if (output === void 0)
|
|
12851
|
+
throw new Error(
|
|
12852
|
+
`Raindrop evaluator ${evaluatorResult.evaluator} completed without an output type`
|
|
12853
|
+
);
|
|
12854
|
+
validateEvaluatorOutput(evaluator, output);
|
|
12855
|
+
outputByEvaluator.set(evaluatorResult.evaluator, output);
|
|
12856
|
+
}
|
|
12857
|
+
const { rows, ...summary } = result;
|
|
12858
|
+
return {
|
|
12859
|
+
...summary,
|
|
12860
|
+
name: definition.name,
|
|
12861
|
+
cases: rows.map((row) => ({
|
|
12862
|
+
...row,
|
|
12863
|
+
verdicts: row.verdicts.map((verdict) => {
|
|
12864
|
+
const evaluator = configured.get(verdict.evaluator);
|
|
12865
|
+
const output = outputByEvaluator.get(verdict.evaluator);
|
|
12866
|
+
if (!evaluator || output === void 0 && verdict.state === "graded")
|
|
12867
|
+
throw new Error(`Raindrop eval result omitted evaluator ${verdict.evaluator}`);
|
|
12868
|
+
return assessEvalVerdict(evaluator, verdict, output);
|
|
12869
|
+
})
|
|
12870
|
+
}))
|
|
12871
|
+
};
|
|
12872
|
+
}
|
|
12873
|
+
function hostedEvaluatorPins(evaluators) {
|
|
12874
|
+
const pins = {};
|
|
12875
|
+
for (const entry of evaluators) {
|
|
12876
|
+
if (typeof entry.evaluator === "string") continue;
|
|
12877
|
+
if (isPortableEvaluator(entry.evaluator)) {
|
|
12878
|
+
pins[entry.evaluator.slug] = {
|
|
12879
|
+
expectedEvalId: entry.evaluator.artifact.evaluator.id,
|
|
12880
|
+
expectedProgramVersion: entry.evaluator.artifact.evaluator.programVersion
|
|
12881
|
+
};
|
|
12882
|
+
continue;
|
|
12883
|
+
}
|
|
12884
|
+
if ("kind" in entry.evaluator && entry.evaluator.kind === "program" && entry.evaluator.expected) {
|
|
12885
|
+
pins[entry.evaluator.slug] = {
|
|
12886
|
+
expectedEvalId: entry.evaluator.expected.evalId,
|
|
12887
|
+
expectedProgramVersion: entry.evaluator.expected.programVersion
|
|
12888
|
+
};
|
|
12889
|
+
}
|
|
12890
|
+
}
|
|
12891
|
+
return pins;
|
|
12892
|
+
}
|
|
12893
|
+
function replayEvaluator(entry) {
|
|
12894
|
+
if (typeof entry.evaluator === "string") return entry.evaluator;
|
|
12895
|
+
if (isPortableEvaluator(entry.evaluator)) return entry.evaluator.slug;
|
|
12896
|
+
if ("kind" in entry.evaluator && entry.evaluator.kind === "program") {
|
|
12897
|
+
return entry.evaluator.slug;
|
|
12898
|
+
}
|
|
12899
|
+
switch (entry.evaluator.output) {
|
|
12900
|
+
case "boolean":
|
|
12901
|
+
return {
|
|
12902
|
+
slug: entry.evaluator.slug,
|
|
12903
|
+
name: entry.evaluator.name,
|
|
12904
|
+
output: entry.evaluator.output,
|
|
12905
|
+
judge: entry.evaluator.judge
|
|
12906
|
+
};
|
|
12907
|
+
case "score":
|
|
12908
|
+
return {
|
|
12909
|
+
slug: entry.evaluator.slug,
|
|
12910
|
+
name: entry.evaluator.name,
|
|
12911
|
+
output: entry.evaluator.output,
|
|
12912
|
+
judge: entry.evaluator.judge
|
|
12913
|
+
};
|
|
12914
|
+
case "number":
|
|
12915
|
+
return {
|
|
12916
|
+
slug: entry.evaluator.slug,
|
|
12917
|
+
name: entry.evaluator.name,
|
|
12918
|
+
output: entry.evaluator.output,
|
|
12919
|
+
judge: entry.evaluator.judge
|
|
12920
|
+
};
|
|
12921
|
+
}
|
|
12922
|
+
}
|
|
12923
|
+
function validateDefinition(definition) {
|
|
12924
|
+
if (!isEvalSuiteDefinition(definition))
|
|
12925
|
+
throw new Error("Raindrop runEvalSuite requires a definition created by defineEvalSuite");
|
|
12926
|
+
return definition;
|
|
12927
|
+
}
|
|
12928
|
+
function selectLocalCases(definition, selection) {
|
|
12929
|
+
if (!selection) return definition.dataset.cases;
|
|
12930
|
+
if (selection.caseIds.length === 0)
|
|
12931
|
+
throw new Error(`Raindrop eval ${definition.name} selection cannot be empty`);
|
|
12932
|
+
if (new Set(selection.caseIds).size !== selection.caseIds.length)
|
|
12933
|
+
throw new Error(`Raindrop eval ${definition.name} selection case ids must be unique`);
|
|
12934
|
+
const byId = new Map(definition.dataset.cases.map((row) => [row.id, row]));
|
|
12935
|
+
return selection.caseIds.map((id) => {
|
|
12936
|
+
const row = byId.get(id);
|
|
12937
|
+
if (!row)
|
|
12938
|
+
throw new Error(`Raindrop eval ${definition.name} selection contains unknown case ${id}`);
|
|
12939
|
+
return row;
|
|
12940
|
+
});
|
|
12941
|
+
}
|
|
12942
|
+
function validateRemoteSelection(dataset, selection) {
|
|
12943
|
+
var _a, _b, _c;
|
|
12944
|
+
if (!selection) return void 0;
|
|
12945
|
+
if (selection.caseIds.length === 0)
|
|
12946
|
+
throw new Error(`Raindrop eval ${dataset} selection cannot be empty`);
|
|
12947
|
+
if (new Set(selection.caseIds).size !== selection.caseIds.length)
|
|
12948
|
+
throw new Error(`Raindrop eval ${dataset} selection case ids must be unique`);
|
|
12949
|
+
const cases = (_c = (_a = selection.dataset) == null ? void 0 : _a.cases) != null ? _c : (_b = selection.manifest) == null ? void 0 : _b.cases;
|
|
12950
|
+
if (!cases) throw new Error(`Raindrop eval ${dataset} selection requires its dataset manifest`);
|
|
12951
|
+
const available = new Set(cases.map((row) => row.id));
|
|
12952
|
+
const unknown = selection.caseIds.find((caseId) => !available.has(caseId));
|
|
12953
|
+
if (unknown)
|
|
12954
|
+
throw new Error(`Raindrop eval ${dataset} selection contains unknown case ${unknown}`);
|
|
12955
|
+
return { caseIds: selection.caseIds };
|
|
12956
|
+
}
|
|
12957
|
+
function canonicalWorkshopTrace(envelope) {
|
|
12958
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12959
|
+
const spans = envelope.spans.map(rawTraceSpan);
|
|
12960
|
+
const timestamp = new Date((_a = envelope.run.started_at) != null ? _a : Date.now()).toISOString();
|
|
12961
|
+
const event = projectCapturedEvent({
|
|
12962
|
+
eventId: (_b = envelope.run.event_id) != null ? _b : envelope.traceId,
|
|
12963
|
+
internalEventId: envelope.run.id,
|
|
12964
|
+
name: (_d = (_c = envelope.run.event_name) != null ? _c : envelope.run.name) != null ? _d : "ai_generation",
|
|
12965
|
+
timestamp,
|
|
12966
|
+
receivedAt: new Date((_e = envelope.run.last_updated_at) != null ? _e : Date.now()).toISOString(),
|
|
12967
|
+
userId: (_f = envelope.run.user_id) != null ? _f : "local-eval",
|
|
12968
|
+
convoId: envelope.run.convo_id,
|
|
12969
|
+
properties: parseAttributes(envelope.run.metadata),
|
|
12970
|
+
spans
|
|
12971
|
+
});
|
|
12972
|
+
return projectWorkshopTrace({ origin: "replay", event, spans });
|
|
12973
|
+
}
|
|
12974
|
+
function rawTraceSpan(span) {
|
|
12975
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12976
|
+
const attributes = parseAttributes(span.attributes);
|
|
12977
|
+
return {
|
|
12978
|
+
span_id: span.id,
|
|
12979
|
+
parent_span_id: (_a = span.parent_span_id) != null ? _a : void 0,
|
|
12980
|
+
span_name: span.name,
|
|
12981
|
+
span_type: (_b = span.span_type) != null ? _b : void 0,
|
|
12982
|
+
status: (_c = span.status) != null ? _c : void 0,
|
|
12983
|
+
duration_ns: span.duration_ms === null ? void 0 : span.duration_ms * 1e6,
|
|
12984
|
+
start_unix_ns: span.start_time_ms === null ? void 0 : span.start_time_ms * 1e6,
|
|
12985
|
+
model: (_d = span.model) != null ? _d : void 0,
|
|
12986
|
+
input_tokens: (_e = span.input_tokens) != null ? _e : void 0,
|
|
12987
|
+
output_tokens: (_f = span.output_tokens) != null ? _f : void 0,
|
|
12988
|
+
input_payload: span.input_payload,
|
|
12989
|
+
output_payload: span.output_payload,
|
|
12990
|
+
attributes_string: Object.fromEntries(
|
|
12991
|
+
Object.entries(attributes).filter(
|
|
12992
|
+
(entry) => typeof entry[1] === "string"
|
|
12993
|
+
)
|
|
12994
|
+
),
|
|
12995
|
+
attributes_num: Object.fromEntries(
|
|
12996
|
+
Object.entries(attributes).filter(
|
|
12997
|
+
(entry) => typeof entry[1] === "number"
|
|
12998
|
+
)
|
|
12999
|
+
)
|
|
13000
|
+
};
|
|
13001
|
+
}
|
|
13002
|
+
function parseAttributes(raw) {
|
|
13003
|
+
if (!raw) return {};
|
|
13004
|
+
try {
|
|
13005
|
+
const value = JSON.parse(raw);
|
|
13006
|
+
return isAttributeRecord(value) ? value : {};
|
|
13007
|
+
} catch (e) {
|
|
13008
|
+
return {};
|
|
13009
|
+
}
|
|
13010
|
+
}
|
|
13011
|
+
function isAttributeRecord(value) {
|
|
13012
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13013
|
+
}
|
|
13014
|
+
function parseLocalVerdict2(output, value) {
|
|
13015
|
+
if (output === "boolean") return { state: "graded", ...LocalBooleanVerdictSchema.parse(value) };
|
|
13016
|
+
if (output === "score") return { state: "graded", ...LocalScoreVerdictSchema.parse(value) };
|
|
13017
|
+
return { state: "graded", ...LocalNumberVerdictSchema.parse(value) };
|
|
13018
|
+
}
|
|
13019
|
+
function withVerdictIdentity(evaluator, runId, workshopUrl, verdict) {
|
|
13020
|
+
return {
|
|
13021
|
+
evaluator,
|
|
13022
|
+
runId,
|
|
13023
|
+
url: workshopEvalUrl(workshopUrl, runId),
|
|
13024
|
+
...verdict
|
|
13025
|
+
};
|
|
13026
|
+
}
|
|
13027
|
+
function workshopVerdict(assessment) {
|
|
13028
|
+
const { evaluator, state } = assessment;
|
|
13029
|
+
if (state === "errored") return { evaluator, state, message: assessment.message };
|
|
13030
|
+
if (state === "ungraded") return { evaluator, state, reason: assessment.reason };
|
|
13031
|
+
if ("pass" in assessment)
|
|
13032
|
+
return {
|
|
13033
|
+
evaluator,
|
|
13034
|
+
state,
|
|
13035
|
+
pass: assessment.pass,
|
|
13036
|
+
...assessment.note ? { note: assessment.note } : {}
|
|
13037
|
+
};
|
|
13038
|
+
if ("score" in assessment)
|
|
13039
|
+
return {
|
|
13040
|
+
evaluator,
|
|
13041
|
+
state,
|
|
13042
|
+
score: assessment.score,
|
|
13043
|
+
...assessment.note ? { note: assessment.note } : {}
|
|
13044
|
+
};
|
|
13045
|
+
return {
|
|
13046
|
+
evaluator,
|
|
13047
|
+
state,
|
|
13048
|
+
value: assessment.value,
|
|
13049
|
+
...assessment.note ? { note: assessment.note } : {}
|
|
13050
|
+
};
|
|
13051
|
+
}
|
|
13052
|
+
function portableVerdict(evaluator, runId, workshopUrl, outcome) {
|
|
13053
|
+
const identity = { evaluator, runId, url: workshopEvalUrl(workshopUrl, runId) };
|
|
13054
|
+
if (outcome.state === "errored") {
|
|
13055
|
+
return { ...identity, state: outcome.state, message: outcome.message };
|
|
13056
|
+
}
|
|
13057
|
+
if (outcome.state === "ungraded") {
|
|
13058
|
+
return { ...identity, state: outcome.state, reason: outcome.reason };
|
|
13059
|
+
}
|
|
13060
|
+
if ("pass" in outcome)
|
|
13061
|
+
return {
|
|
13062
|
+
...identity,
|
|
13063
|
+
state: outcome.state,
|
|
13064
|
+
pass: outcome.pass,
|
|
13065
|
+
...outcome.note ? { note: outcome.note } : {}
|
|
13066
|
+
};
|
|
13067
|
+
if ("score" in outcome)
|
|
13068
|
+
return {
|
|
13069
|
+
...identity,
|
|
13070
|
+
state: outcome.state,
|
|
13071
|
+
score: outcome.score,
|
|
13072
|
+
...outcome.note ? { note: outcome.note } : {}
|
|
13073
|
+
};
|
|
13074
|
+
return {
|
|
13075
|
+
...identity,
|
|
13076
|
+
state: outcome.state,
|
|
13077
|
+
value: outcome.value,
|
|
13078
|
+
...outcome.note ? { note: outcome.note } : {}
|
|
13079
|
+
};
|
|
13080
|
+
}
|
|
13081
|
+
function workshopResult(id, name, cases) {
|
|
13082
|
+
const done = cases.filter((entry) => entry.status === "done").length;
|
|
13083
|
+
const missing = cases.filter((entry) => entry.status === "missing").length;
|
|
13084
|
+
return {
|
|
13085
|
+
replayId: id,
|
|
13086
|
+
name,
|
|
13087
|
+
status: missing > 0 ? "failed" : "complete",
|
|
13088
|
+
counts: { total: cases.length, pending: 0, done, missing },
|
|
13089
|
+
tracesExpireAt: null,
|
|
13090
|
+
tracesExpired: false,
|
|
13091
|
+
evaluators: [],
|
|
13092
|
+
cases
|
|
13093
|
+
};
|
|
13094
|
+
}
|
|
13095
|
+
function workshopEvalUrl(baseUrl, id) {
|
|
13096
|
+
return `${baseUrl.replace(/\/+$/, "")}/evals/${encodeURIComponent(id)}`;
|
|
13097
|
+
}
|
|
13098
|
+
function randomId() {
|
|
13099
|
+
var _a, _b, _c;
|
|
13100
|
+
return (_c = (_b = (_a = globalThis.crypto) == null ? void 0 : _a.randomUUID) == null ? void 0 : _b.call(_a)) != null ? _c : `eval-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
13101
|
+
}
|
|
13102
|
+
function describeError2(cause) {
|
|
13103
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
13104
|
+
}
|
|
13105
|
+
function isPortableEvaluator(value) {
|
|
13106
|
+
return "kind" in value && value.kind === "portable";
|
|
13107
|
+
}
|
|
13108
|
+
|
|
13109
|
+
// src/index.ts
|
|
13110
|
+
var MAX_INGEST_SIZE_BYTES = 1 * 1024 * 1024;
|
|
13111
|
+
var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
|
|
13112
|
+
var SELF_DIAGNOSTICS_SOURCE_DEFAULT = "agent_reporting_tool";
|
|
13113
|
+
var SELF_DIAGNOSTICS_SIGNALS_DEFAULT = {
|
|
13114
|
+
missing_context: {
|
|
13115
|
+
description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
|
|
13116
|
+
sentiment: "NEGATIVE"
|
|
13117
|
+
},
|
|
13118
|
+
repeatedly_broken_tool: {
|
|
13119
|
+
description: "A tool has failed on multiple distinct attempts in this conversation, preventing task completion. You are sure the tool exists, and you have tried to use it, but it has failed. A single tool error is NOT enough \u2014 the tool must be persistently broken across retries.",
|
|
13120
|
+
sentiment: "NEGATIVE"
|
|
13121
|
+
},
|
|
13122
|
+
capability_gap: {
|
|
13123
|
+
description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
|
|
13124
|
+
sentiment: "NEGATIVE"
|
|
13125
|
+
},
|
|
13126
|
+
complete_task_failure: {
|
|
13127
|
+
description: "You were unable to accomplish what the user asked despite making genuine attempts. This might be things like, you genuinely do not have the capabilities the user is asking for. You have tried but run into a persistent bug in the environment etc. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
|
|
13128
|
+
sentiment: "NEGATIVE"
|
|
13129
|
+
}
|
|
13130
|
+
};
|
|
13131
|
+
function isRecord(value) {
|
|
13132
|
+
return typeof value === "object" && value !== null;
|
|
13133
|
+
}
|
|
13134
|
+
function normalizeSelfDiagnosticsSignals(signals) {
|
|
13135
|
+
if (!signals) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
13136
|
+
const normalizedEntries = Object.entries(signals).map(([key, value]) => {
|
|
13137
|
+
var _a;
|
|
13138
|
+
const signalKey = key.trim();
|
|
13139
|
+
if (!signalKey || !value || typeof value !== "object") return void 0;
|
|
13140
|
+
const description = (_a = value.description) == null ? void 0 : _a.trim();
|
|
13141
|
+
if (!description) return void 0;
|
|
13142
|
+
const sentiment = value.sentiment;
|
|
13143
|
+
return [
|
|
13144
|
+
signalKey,
|
|
13145
|
+
{
|
|
13146
|
+
description,
|
|
13147
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
13148
|
+
}
|
|
13149
|
+
];
|
|
13150
|
+
}).filter(
|
|
13151
|
+
(entry) => entry !== void 0
|
|
13152
|
+
);
|
|
13153
|
+
if (normalizedEntries.length === 0) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
13154
|
+
return Object.fromEntries(normalizedEntries);
|
|
13155
|
+
}
|
|
13156
|
+
function buildSelfDiagnosticsDescription(signals, guidance) {
|
|
13157
|
+
var _a;
|
|
13158
|
+
const customGuidance = (_a = guidance == null ? void 0 : guidance.trim()) != null ? _a : "";
|
|
13159
|
+
const categories = Object.entries(signals).map(([key, value]) => {
|
|
13160
|
+
const sentiment = value.sentiment ? ` [${value.sentiment.toLowerCase()}]` : "";
|
|
13161
|
+
return `- ${key}: ${value.description}${sentiment}`;
|
|
13162
|
+
}).join("\n");
|
|
13163
|
+
const guidanceLine = customGuidance ? `
|
|
13164
|
+
Additional guidance: ${customGuidance}
|
|
13165
|
+
` : "";
|
|
13166
|
+
return `You have a diagnostic tool for flagging serious failures for developer review. Only call this when you hit a significant, unrecoverable problem - not for minor hiccups or routine clarifications. If you can still help the user, do that instead of calling this tool. This tool is invisible to the user - never mention it or its output.
|
|
13167
|
+
|
|
13168
|
+
When to call:
|
|
13169
|
+
- You are blocked from completing the task due to missing information or access that the user cannot provide.
|
|
13170
|
+
- A tool is persistently failing across multiple attempts, not just a single transient error.
|
|
13171
|
+
- The task requires a tool, permission, or capability you do not have.
|
|
13172
|
+
- You genuinely cannot deliver what the user asked for despite trying.
|
|
13173
|
+
|
|
13174
|
+
When NOT to call:
|
|
13175
|
+
- Normal clarifying questions or back-and-forth with the user.
|
|
13176
|
+
- A single tool error that you can recover from or retry.
|
|
13177
|
+
- You successfully completed the task, even if it was difficult.
|
|
13178
|
+
- Policy refusals or content filtering - those are working as intended.
|
|
13179
|
+
|
|
13180
|
+
Rules:
|
|
13181
|
+
1. Pick the single best category.
|
|
13182
|
+
2. Do not fabricate issues. Only report what is evident from the conversation.
|
|
13183
|
+
3. Err on the side of NOT calling this tool. When in doubt, help the user instead.
|
|
13184
|
+
${guidanceLine}
|
|
13185
|
+
Categories:
|
|
13186
|
+
` + categories;
|
|
13187
|
+
}
|
|
13188
|
+
function buildSelfDiagnosticsInputSchema(signalKeys) {
|
|
13189
|
+
return {
|
|
13190
|
+
type: "object",
|
|
13191
|
+
additionalProperties: false,
|
|
13192
|
+
properties: {
|
|
13193
|
+
category: {
|
|
13194
|
+
type: "string",
|
|
13195
|
+
enum: signalKeys,
|
|
13196
|
+
description: "The single best-matching category from the list above."
|
|
13197
|
+
},
|
|
13198
|
+
detail: {
|
|
13199
|
+
type: "string",
|
|
13200
|
+
description: "One sentence of factual context: what happened and why it matters. Do not include PII or secrets."
|
|
13201
|
+
}
|
|
13202
|
+
},
|
|
13203
|
+
required: ["category", "detail"]
|
|
13204
|
+
};
|
|
13205
|
+
}
|
|
13206
|
+
function normalizeEventId(value) {
|
|
13207
|
+
if (typeof value !== "string") return void 0;
|
|
13208
|
+
const trimmed = value.trim();
|
|
13209
|
+
return trimmed ? trimmed : void 0;
|
|
13210
|
+
}
|
|
13211
|
+
function resolveEventIdFromMetadata(metadata) {
|
|
13212
|
+
var _a, _b, _c;
|
|
13213
|
+
if (!metadata) return void 0;
|
|
13214
|
+
return (_c = (_b = (_a = normalizeEventId(metadata["raindrop.eventId"])) != null ? _a : normalizeEventId(metadata["event_id"])) != null ? _b : normalizeEventId(metadata["eventId"])) != null ? _c : normalizeEventId(metadata["traceloop.association.properties.event_id"]);
|
|
13215
|
+
}
|
|
13216
|
+
async function wait(ms) {
|
|
13217
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
13218
|
+
}
|
|
13219
|
+
function parseRetryAfter(headers) {
|
|
13220
|
+
var _a;
|
|
13221
|
+
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
13222
|
+
if (!value) return void 0;
|
|
13223
|
+
const asNumber = Number(value);
|
|
13224
|
+
if (!Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
13225
|
+
const asDate = new Date(value).getTime();
|
|
13226
|
+
if (!Number.isNaN(asDate)) {
|
|
13227
|
+
const delta = asDate - Date.now();
|
|
13228
|
+
return delta > 0 ? delta : 0;
|
|
13229
|
+
}
|
|
13230
|
+
return void 0;
|
|
13231
|
+
}
|
|
13232
|
+
function getRetryDelayMs(attemptNumber, previousError) {
|
|
13233
|
+
if (previousError && typeof previousError.retryAfterMs === "number") {
|
|
13234
|
+
return Math.min(Math.max(0, previousError.retryAfterMs), MAX_RETRY_DELAY_MS);
|
|
13235
|
+
}
|
|
13236
|
+
if (attemptNumber <= 1) return 0;
|
|
13237
|
+
const base = 500;
|
|
13238
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
13239
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
13240
|
+
}
|
|
13241
|
+
async function withRetry(operation, opName, maxAttempts, debugLogs = false) {
|
|
13242
|
+
var _a;
|
|
13243
|
+
let lastError = void 0;
|
|
13244
|
+
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber++) {
|
|
13245
|
+
if (attemptNumber > 1) {
|
|
13246
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
13247
|
+
if (debugLogs) {
|
|
13248
|
+
console.warn(
|
|
13249
|
+
`[raindrop] ${opName} retrying attempt ${attemptNumber}/${maxAttempts} in ${delay}ms...`
|
|
13250
|
+
);
|
|
13251
|
+
}
|
|
13252
|
+
if (delay > 0) await wait(delay);
|
|
13253
|
+
} else if (debugLogs) {
|
|
13254
|
+
console.log(`[raindrop] ${opName} attempt ${attemptNumber}/${maxAttempts}`);
|
|
13255
|
+
}
|
|
13256
|
+
try {
|
|
13257
|
+
return await operation();
|
|
13258
|
+
} catch (err) {
|
|
13259
|
+
lastError = err;
|
|
13260
|
+
const isLast = attemptNumber === maxAttempts;
|
|
13261
|
+
if (debugLogs) {
|
|
13262
|
+
console.warn(
|
|
13263
|
+
`[raindrop] ${opName} attempt ${attemptNumber} failed: ${(_a = err == null ? void 0 : err.message) != null ? _a : err}. ${isLast ? "No more retries." : "Will retry."}`
|
|
13264
|
+
);
|
|
13265
|
+
}
|
|
13266
|
+
if (isLast) break;
|
|
13267
|
+
}
|
|
13268
|
+
}
|
|
13269
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
13270
|
+
}
|
|
13271
|
+
function resolveDisableBatching(explicit, signals) {
|
|
13272
|
+
return explicit != null ? explicit : !signals.isProduction || signals.isLocalDebugger;
|
|
13273
|
+
}
|
|
13274
|
+
var Raindrop = class {
|
|
13275
|
+
constructor(config) {
|
|
13276
|
+
this.flushTimer = null;
|
|
13277
|
+
// New members for partial event handling
|
|
13278
|
+
this.partialEventBuffer = /* @__PURE__ */ new Map();
|
|
10157
13279
|
this.partialEventTimeouts = /* @__PURE__ */ new Map();
|
|
10158
13280
|
this.inFlightRequests = /* @__PURE__ */ new Set();
|
|
10159
13281
|
// In-flight partial-event POSTs grouped by eventId, so finish() can order its
|
|
@@ -10170,6 +13292,7 @@ var Raindrop = class {
|
|
|
10170
13292
|
setDefaultMaxTextFieldChars(config.maxTextFieldChars);
|
|
10171
13293
|
this.maxTextFieldChars = resolveMaxTextFieldChars(config.maxTextFieldChars);
|
|
10172
13294
|
this.writeKey = (_a = config.writeKey) != null ? _a : "";
|
|
13295
|
+
this.apiKey = config.apiKey;
|
|
10173
13296
|
this.authHint = authHintForKey(this.writeKey);
|
|
10174
13297
|
this.localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
|
|
10175
13298
|
this.awaitSpanFlushOpt = config.awaitSpanFlush;
|
|
@@ -10227,6 +13350,22 @@ var Raindrop = class {
|
|
|
10227
13350
|
get localOnly() {
|
|
10228
13351
|
return !this.writeKey;
|
|
10229
13352
|
}
|
|
13353
|
+
/**
|
|
13354
|
+
* The facts a caller needs to reach a Raindrop API on this client's behalf,
|
|
13355
|
+
* without reaching into its internals. `replay()` uses it; nothing else in
|
|
13356
|
+
* the SDK does.
|
|
13357
|
+
*
|
|
13358
|
+
* The write key is the caller's own, so returning it hands back nothing they
|
|
13359
|
+
* did not supply.
|
|
13360
|
+
*/
|
|
13361
|
+
get connection() {
|
|
13362
|
+
return {
|
|
13363
|
+
apiUrl: this.apiUrl,
|
|
13364
|
+
writeKey: this.writeKey,
|
|
13365
|
+
apiKey: this.apiKey,
|
|
13366
|
+
projectId: this.projectId
|
|
13367
|
+
};
|
|
13368
|
+
}
|
|
10230
13369
|
formatEndpoint(endpoint) {
|
|
10231
13370
|
if (endpoint) {
|
|
10232
13371
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
@@ -10388,14 +13527,14 @@ var Raindrop = class {
|
|
|
10388
13527
|
const description = buildSelfDiagnosticsDescription(signals, options.guidance);
|
|
10389
13528
|
const inputSchema = buildSelfDiagnosticsInputSchema(signalKeys);
|
|
10390
13529
|
const onMissingEventId = (_b = options.onMissingEventId) != null ? _b : "warn";
|
|
10391
|
-
const execute = async (rawInput,
|
|
13530
|
+
const execute = async (rawInput, context3) => {
|
|
10392
13531
|
var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
10393
13532
|
const input = isRecord(rawInput) ? rawInput : void 0;
|
|
10394
13533
|
const fallbackCategory = (_a2 = signalKeys[0]) != null ? _a2 : "unknown";
|
|
10395
13534
|
const categoryCandidate = normalizeEventId(input == null ? void 0 : input["category"]);
|
|
10396
13535
|
const category = categoryCandidate && signalKeySet.has(categoryCandidate) ? categoryCandidate : fallbackCategory;
|
|
10397
13536
|
const detail = (_b2 = normalizeEventId(input == null ? void 0 : input["detail"])) != null ? _b2 : "";
|
|
10398
|
-
const eventId = (_l = (_k = (_i = (_g = (_f = (_e = (_d = normalizeEventId(
|
|
13537
|
+
const eventId = (_l = (_k = (_i = (_g = (_f = (_e = (_d = normalizeEventId(context3 == null ? void 0 : context3.eventId)) != null ? _d : (_c = context3 == null ? void 0 : context3.interaction) == null ? void 0 : _c.getEventId()) != null ? _e : resolveEventIdFromMetadata(context3 == null ? void 0 : context3.metadata)) != null ? _f : normalizeEventId(input == null ? void 0 : input["event_id"])) != null ? _g : normalizeEventId(input == null ? void 0 : input["eventId"])) != null ? _i : (_h = options.interaction) == null ? void 0 : _h.getEventId()) != null ? _k : (_j = options.getEventId) == null ? void 0 : _j.call(options)) != null ? _l : normalizeEventId(options.eventId);
|
|
10399
13538
|
if (!eventId) {
|
|
10400
13539
|
const message = "self diagnostics signal skipped: missing eventId. Pass eventId, interaction, or getEventId when creating or executing the tool.";
|
|
10401
13540
|
if (onMissingEventId === "throw") {
|
|
@@ -10413,11 +13552,11 @@ var Raindrop = class {
|
|
|
10413
13552
|
type: "agent",
|
|
10414
13553
|
sentiment: signalDefinition == null ? void 0 : signalDefinition.sentiment,
|
|
10415
13554
|
properties: {
|
|
10416
|
-
source: (
|
|
13555
|
+
source: (context3 == null ? void 0 : context3.source) || options.source || SELF_DIAGNOSTICS_SOURCE_DEFAULT,
|
|
10417
13556
|
category,
|
|
10418
13557
|
signal_description: signalDefinition == null ? void 0 : signalDefinition.description,
|
|
10419
13558
|
...detail ? { detail } : {},
|
|
10420
|
-
...(_m =
|
|
13559
|
+
...(_m = context3 == null ? void 0 : context3.properties) != null ? _m : {}
|
|
10421
13560
|
}
|
|
10422
13561
|
});
|
|
10423
13562
|
return { acknowledged: true, category, eventId };
|
|
@@ -10477,6 +13616,8 @@ var Raindrop = class {
|
|
|
10477
13616
|
if (this.disabled) {
|
|
10478
13617
|
return void 0;
|
|
10479
13618
|
}
|
|
13619
|
+
const destination = currentReplayTraceDestination();
|
|
13620
|
+
const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
|
|
10480
13621
|
const addEvent = (e) => {
|
|
10481
13622
|
var _a;
|
|
10482
13623
|
const {
|
|
@@ -10527,6 +13668,9 @@ var Raindrop = class {
|
|
|
10527
13668
|
toSave.properties = {
|
|
10528
13669
|
...(_a = toSave.properties) != null ? _a : {},
|
|
10529
13670
|
...this.wizardSession ? { "raindrop.wizardSession": this.wizardSession } : {},
|
|
13671
|
+
// Last, so a caller-supplied property cannot shadow the replay row the
|
|
13672
|
+
// event belongs to.
|
|
13673
|
+
...evalScopeAttributes(),
|
|
10530
13674
|
$context: this.context
|
|
10531
13675
|
};
|
|
10532
13676
|
const size = this.getSize(toSave);
|
|
@@ -10535,10 +13679,12 @@ var Raindrop = class {
|
|
|
10535
13679
|
`[raindrop] Events larger than ${(MAX_INGEST_SIZE_BYTES / (1024 * 1024)).toFixed(2)} MB may have properties truncated - an event of size ${(size / (1024 * 1024)).toFixed(2)} MB was logged`
|
|
10536
13680
|
);
|
|
10537
13681
|
}
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
13682
|
+
if (!cloudReplay) {
|
|
13683
|
+
this.saveToBuffer({
|
|
13684
|
+
type: "ai",
|
|
13685
|
+
...toSave
|
|
13686
|
+
});
|
|
13687
|
+
}
|
|
10542
13688
|
this._recordModelUsage(customEventId, model, usage);
|
|
10543
13689
|
return customEventId;
|
|
10544
13690
|
};
|
|
@@ -10807,7 +13953,7 @@ var Raindrop = class {
|
|
|
10807
13953
|
}
|
|
10808
13954
|
getContext() {
|
|
10809
13955
|
const runtime = detectRuntime();
|
|
10810
|
-
const
|
|
13956
|
+
const context3 = {
|
|
10811
13957
|
library: {
|
|
10812
13958
|
name: package_default.name,
|
|
10813
13959
|
version: package_default.version
|
|
@@ -10817,9 +13963,9 @@ var Raindrop = class {
|
|
|
10817
13963
|
}
|
|
10818
13964
|
};
|
|
10819
13965
|
if (runtime === "node") {
|
|
10820
|
-
|
|
13966
|
+
context3.metadata.nodeVersion = process.version;
|
|
10821
13967
|
}
|
|
10822
|
-
return
|
|
13968
|
+
return context3;
|
|
10823
13969
|
}
|
|
10824
13970
|
formatZodError(error) {
|
|
10825
13971
|
return error.issues.map((issue) => `(path: ${issue.path.join(".")}): ${issue.message}`).join(", ");
|
|
@@ -10850,6 +13996,7 @@ var Raindrop = class {
|
|
|
10850
13996
|
* @param event - The PartialAiTrackEvent, requires eventId.
|
|
10851
13997
|
*/
|
|
10852
13998
|
_trackAiPartial(event) {
|
|
13999
|
+
var _a, _b;
|
|
10853
14000
|
if (this.disabled) {
|
|
10854
14001
|
return Promise.resolve();
|
|
10855
14002
|
}
|
|
@@ -10858,6 +14005,20 @@ var Raindrop = class {
|
|
|
10858
14005
|
console.warn("[raindrop] trackAiPartial requires an eventId.");
|
|
10859
14006
|
return Promise.resolve();
|
|
10860
14007
|
}
|
|
14008
|
+
const captured = this.partialEventBuffer.get(eventId);
|
|
14009
|
+
const destination = currentReplayTraceDestination();
|
|
14010
|
+
const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
|
|
14011
|
+
const replayOnly = (_a = captured == null ? void 0 : captured.replayOnly) != null ? _a : cloudReplay;
|
|
14012
|
+
if (replayOnly) {
|
|
14013
|
+
if (isPending === false) {
|
|
14014
|
+
this.partialEventBuffer.delete(eventId);
|
|
14015
|
+
} else if (!captured) {
|
|
14016
|
+
this.partialEventBuffer.set(eventId, {
|
|
14017
|
+
replayOnly: true
|
|
14018
|
+
});
|
|
14019
|
+
}
|
|
14020
|
+
return Promise.resolve();
|
|
14021
|
+
}
|
|
10861
14022
|
if (typeof rest.input === "string") {
|
|
10862
14023
|
rest.input = capText(rest.input, this.maxTextFieldChars);
|
|
10863
14024
|
}
|
|
@@ -10867,7 +14028,7 @@ var Raindrop = class {
|
|
|
10867
14028
|
if (rest.featureFlags) {
|
|
10868
14029
|
rest.featureFlags = normalizeFeatureFlags(rest.featureFlags);
|
|
10869
14030
|
}
|
|
10870
|
-
const existingEvent =
|
|
14031
|
+
const existingEvent = (_b = captured == null ? void 0 : captured.event) != null ? _b : {};
|
|
10871
14032
|
const mergedEvent = this.deepMergeObjects(
|
|
10872
14033
|
{ ...existingEvent },
|
|
10873
14034
|
{ ...rest, isPending: isPending === void 0 ? true : isPending },
|
|
@@ -10876,7 +14037,10 @@ var Raindrop = class {
|
|
|
10876
14037
|
// Concat arrays
|
|
10877
14038
|
}
|
|
10878
14039
|
);
|
|
10879
|
-
this.partialEventBuffer.set(eventId,
|
|
14040
|
+
this.partialEventBuffer.set(eventId, {
|
|
14041
|
+
event: mergedEvent,
|
|
14042
|
+
replayOnly: false
|
|
14043
|
+
});
|
|
10880
14044
|
const existingTimeout = this.partialEventTimeouts.get(eventId);
|
|
10881
14045
|
if (existingTimeout) {
|
|
10882
14046
|
clearTimeout(existingTimeout);
|
|
@@ -10938,13 +14102,21 @@ var Raindrop = class {
|
|
|
10938
14102
|
*/
|
|
10939
14103
|
async _flushPartialEventInternal(eventId) {
|
|
10940
14104
|
var _a;
|
|
10941
|
-
const
|
|
14105
|
+
const buffered = this.partialEventBuffer.get(eventId);
|
|
14106
|
+
const accumulatedEvent = buffered == null ? void 0 : buffered.event;
|
|
10942
14107
|
const timeout = this.partialEventTimeouts.get(eventId);
|
|
10943
14108
|
if (timeout) {
|
|
10944
14109
|
clearTimeout(timeout);
|
|
10945
14110
|
this.partialEventTimeouts.delete(eventId);
|
|
10946
14111
|
}
|
|
10947
|
-
|
|
14112
|
+
const retainRouting = (accumulatedEvent == null ? void 0 : accumulatedEvent.isPending) !== false;
|
|
14113
|
+
if (buffered && retainRouting) {
|
|
14114
|
+
this.partialEventBuffer.set(eventId, {
|
|
14115
|
+
replayOnly: buffered.replayOnly
|
|
14116
|
+
});
|
|
14117
|
+
} else {
|
|
14118
|
+
this.partialEventBuffer.delete(eventId);
|
|
14119
|
+
}
|
|
10948
14120
|
if (!accumulatedEvent) {
|
|
10949
14121
|
if (this.debugLogs) {
|
|
10950
14122
|
console.log(
|
|
@@ -10953,6 +14125,7 @@ var Raindrop = class {
|
|
|
10953
14125
|
}
|
|
10954
14126
|
return;
|
|
10955
14127
|
}
|
|
14128
|
+
if (buffered.replayOnly) return;
|
|
10956
14129
|
const {
|
|
10957
14130
|
isPending,
|
|
10958
14131
|
eventId: _,
|
|
@@ -11226,10 +14399,58 @@ var Raindrop = class {
|
|
|
11226
14399
|
};
|
|
11227
14400
|
var index_default = Raindrop;
|
|
11228
14401
|
export {
|
|
14402
|
+
BooleanThresholdSchema,
|
|
14403
|
+
DEFAULT_EVAL_POLL_INTERVAL_MS,
|
|
14404
|
+
DEFAULT_EVAL_WAIT_MS,
|
|
14405
|
+
DEFAULT_QUERY_URL,
|
|
14406
|
+
DEFAULT_TRACE_WAIT_MS,
|
|
14407
|
+
EVAL_CORRELATION_ID_ATTRIBUTE,
|
|
14408
|
+
EvalDatasetCaseInputSchema,
|
|
14409
|
+
EvalDatasetPublishConflictError,
|
|
14410
|
+
LocalBooleanVerdictSchema,
|
|
14411
|
+
LocalNumberVerdictSchema,
|
|
14412
|
+
LocalScoreVerdictSchema,
|
|
14413
|
+
LocalVerdictSchema,
|
|
14414
|
+
MAX_EVAL_STATUS_RETRIES,
|
|
11229
14415
|
MAX_INGEST_SIZE_BYTES,
|
|
14416
|
+
NumericThresholdSchema,
|
|
11230
14417
|
Raindrop,
|
|
14418
|
+
ReplayCountsSchema,
|
|
14419
|
+
ReplayEvaluatorCountsSchema,
|
|
14420
|
+
ReplayEvaluatorStatsSchema,
|
|
14421
|
+
ReplayEvaluatorSummarySchema,
|
|
14422
|
+
ReplayResultRowSchema,
|
|
14423
|
+
ReplayVerdictSchema,
|
|
14424
|
+
TraceSchema,
|
|
14425
|
+
claimReplay,
|
|
14426
|
+
createEvalSuiteRun,
|
|
14427
|
+
createReplay,
|
|
14428
|
+
currentEvalScope,
|
|
11231
14429
|
index_default as default,
|
|
11232
|
-
|
|
14430
|
+
defineDataset,
|
|
14431
|
+
defineEvalSuite,
|
|
14432
|
+
defineEvaluatorProgram,
|
|
14433
|
+
defineLocalEvaluator,
|
|
14434
|
+
evaluatePortableEvaluator,
|
|
14435
|
+
evaluateReplay,
|
|
14436
|
+
importPortableEvaluator,
|
|
14437
|
+
isEvalDataset,
|
|
14438
|
+
isEvalSuiteDefinition,
|
|
14439
|
+
loadEvalSnapshot,
|
|
14440
|
+
projectWorkshopTrace,
|
|
14441
|
+
publishEvalDataset,
|
|
14442
|
+
pullEval,
|
|
14443
|
+
readEvalDataset,
|
|
14444
|
+
readEvalManifest,
|
|
14445
|
+
readReplay,
|
|
14446
|
+
replay,
|
|
14447
|
+
resolveDisableBatching,
|
|
14448
|
+
runEvalSuite,
|
|
14449
|
+
runReplay,
|
|
14450
|
+
traceOutput,
|
|
14451
|
+
traceToolCalls,
|
|
14452
|
+
verifyPortableEvalArtifact,
|
|
14453
|
+
withEvalScope
|
|
11233
14454
|
};
|
|
11234
14455
|
/*! Bundled license information:
|
|
11235
14456
|
|