braintrust 3.27.0 → 3.29.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 +1 -1
- package/dev/dist/index.d.mts +879 -197
- package/dev/dist/index.d.ts +879 -197
- package/dev/dist/index.js +2960 -1865
- package/dev/dist/index.mjs +2217 -1122
- package/dist/apply-auto-instrumentation.js +345 -260
- package/dist/apply-auto-instrumentation.mjs +137 -52
- package/dist/auto-instrumentations/bundler/esbuild.cjs +124 -7
- package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
- package/dist/auto-instrumentations/bundler/next.cjs +124 -7
- package/dist/auto-instrumentations/bundler/next.mjs +3 -3
- package/dist/auto-instrumentations/bundler/rollup.cjs +124 -7
- package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
- package/dist/auto-instrumentations/bundler/vite.cjs +124 -7
- package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
- package/dist/auto-instrumentations/bundler/webpack-loader.cjs +124 -7
- package/dist/auto-instrumentations/bundler/webpack.cjs +124 -7
- package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
- package/dist/auto-instrumentations/{chunk-ZNHTSSGI.mjs → chunk-AOIYCVEL.mjs} +34 -3
- package/dist/auto-instrumentations/{chunk-XEYKUBLY.mjs → chunk-DKTGDNA7.mjs} +91 -5
- package/dist/auto-instrumentations/{chunk-BW33ULMW.mjs → chunk-OMCZ3MV2.mjs} +1 -1
- package/dist/auto-instrumentations/hook.mjs +1186 -161
- package/dist/auto-instrumentations/index.cjs +34 -3
- package/dist/auto-instrumentations/index.mjs +1 -1
- package/dist/browser.d.mts +1388 -75
- package/dist/browser.d.ts +1388 -75
- package/dist/browser.js +3954 -1124
- package/dist/browser.mjs +3954 -1124
- package/dist/{chunk-MF7NU6BT.js → chunk-6Z5S7VOU.js} +175 -25
- package/dist/{chunk-CZM5JIQL.mjs → chunk-7FA6VP2S.mjs} +172 -22
- package/dist/{chunk-YKD22IMR.mjs → chunk-M6XPNJC4.mjs} +2102 -1113
- package/dist/{chunk-QRHGVBKU.js → chunk-XLLGRXGR.js} +3273 -2284
- package/dist/cli.js +6353 -1483
- package/dist/edge-light.d.mts +1 -1
- package/dist/edge-light.d.ts +1 -1
- package/dist/edge-light.js +3954 -1124
- package/dist/edge-light.mjs +3954 -1124
- package/dist/index.d.mts +1972 -659
- package/dist/index.d.ts +1972 -659
- package/dist/index.js +2453 -676
- package/dist/index.mjs +1973 -196
- package/dist/instrumentation/index.d.mts +738 -15
- package/dist/instrumentation/index.d.ts +738 -15
- package/dist/instrumentation/index.js +2791 -874
- package/dist/instrumentation/index.mjs +2791 -874
- package/dist/vitest-evals-reporter.js +16 -16
- package/dist/vitest-evals-reporter.mjs +2 -2
- package/dist/workerd.d.mts +1 -1
- package/dist/workerd.d.ts +1 -1
- package/dist/workerd.js +3954 -1124
- package/dist/workerd.mjs +3954 -1124
- package/package.json +2 -3
- package/util/dist/index.d.mts +1596 -109
- package/util/dist/index.d.ts +1596 -109
package/dist/index.mjs
CHANGED
|
@@ -55,9 +55,11 @@ import {
|
|
|
55
55
|
X_CACHED_HEADER,
|
|
56
56
|
_exportsForTestingOnly,
|
|
57
57
|
_internalGetGlobalState,
|
|
58
|
+
_internalResumeSpan,
|
|
58
59
|
_internalSetInitialState,
|
|
59
60
|
_internalStartSpanWithInitialMerge,
|
|
60
61
|
addAzureBlobHeaders,
|
|
62
|
+
base64ToUint8Array,
|
|
61
63
|
braintrustAISDKTelemetry,
|
|
62
64
|
braintrustFlueInstrumentation,
|
|
63
65
|
braintrustFlueObserver,
|
|
@@ -72,7 +74,6 @@ import {
|
|
|
72
74
|
currentLogger,
|
|
73
75
|
currentSpan,
|
|
74
76
|
currentWorkflowAgentWrapperSpan,
|
|
75
|
-
debugLogger,
|
|
76
77
|
deepCopyEvent,
|
|
77
78
|
deserializePlainStringAsJSON,
|
|
78
79
|
devNullWritableStream,
|
|
@@ -105,7 +106,6 @@ import {
|
|
|
105
106
|
loginToState,
|
|
106
107
|
logs3OverflowUploadSchema,
|
|
107
108
|
makeScorerPropagatedEvent,
|
|
108
|
-
mergeDicts,
|
|
109
109
|
newId,
|
|
110
110
|
parseCachedHeader,
|
|
111
111
|
parseTemplateFormat,
|
|
@@ -127,8 +127,10 @@ import {
|
|
|
127
127
|
summarize,
|
|
128
128
|
summarizeAndFlush,
|
|
129
129
|
templateRegistry,
|
|
130
|
+
toNumericMetrics,
|
|
130
131
|
traceable,
|
|
131
132
|
traced,
|
|
133
|
+
uint8ArrayToBase64,
|
|
132
134
|
updateSpan,
|
|
133
135
|
uploadLogs3OverflowPayload,
|
|
134
136
|
utf8ByteLength,
|
|
@@ -141,7 +143,7 @@ import {
|
|
|
141
143
|
wrapMastraAgent,
|
|
142
144
|
wrapTraced,
|
|
143
145
|
zodToJsonSchema
|
|
144
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-M6XPNJC4.mjs";
|
|
145
147
|
import {
|
|
146
148
|
INSTRUMENTATION_NAMES,
|
|
147
149
|
__export,
|
|
@@ -153,6 +155,7 @@ import {
|
|
|
153
155
|
cloudflareThinkChannels,
|
|
154
156
|
cohereChannels,
|
|
155
157
|
cursorSDKChannels,
|
|
158
|
+
debugLogger,
|
|
156
159
|
genkitChannels,
|
|
157
160
|
gitHubCopilotChannels,
|
|
158
161
|
googleADKChannels,
|
|
@@ -172,8 +175,9 @@ import {
|
|
|
172
175
|
openRouterChannels,
|
|
173
176
|
piCodingAgentChannels,
|
|
174
177
|
strandsAgentSDKChannels,
|
|
178
|
+
voyageAIChannels,
|
|
175
179
|
withSpanInstrumentationName
|
|
176
|
-
} from "./chunk-
|
|
180
|
+
} from "./chunk-7FA6VP2S.mjs";
|
|
177
181
|
|
|
178
182
|
// src/exports.ts
|
|
179
183
|
var exports_exports = {};
|
|
@@ -186,6 +190,8 @@ __export(exports_exports, {
|
|
|
186
190
|
BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
|
|
187
191
|
BaseAttachment: () => BaseAttachment,
|
|
188
192
|
BaseExperiment: () => BaseExperiment,
|
|
193
|
+
BatchScorer: () => BatchScorer,
|
|
194
|
+
BatchTask: () => BatchTask,
|
|
189
195
|
BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
|
|
190
196
|
BraintrustMiddleware: () => BraintrustMiddleware,
|
|
191
197
|
BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
|
|
@@ -199,6 +205,8 @@ __export(exports_exports, {
|
|
|
199
205
|
DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
|
|
200
206
|
Dataset: () => Dataset,
|
|
201
207
|
DatasetPipeline: () => DatasetPipeline,
|
|
208
|
+
DurableEvalMemoryStore: () => DurableEvalMemoryStore,
|
|
209
|
+
DurableEvalRedisStore: () => DurableEvalRedisStore,
|
|
202
210
|
ERR_PERMALINK: () => ERR_PERMALINK,
|
|
203
211
|
Eval: () => Eval,
|
|
204
212
|
EvalResultWithSummary: () => EvalResultWithSummary,
|
|
@@ -256,6 +264,7 @@ __export(exports_exports, {
|
|
|
256
264
|
currentSpan: () => currentSpan,
|
|
257
265
|
deepCopyEvent: () => deepCopyEvent,
|
|
258
266
|
defaultErrorScoreHandler: () => defaultErrorScoreHandler,
|
|
267
|
+
defineDurableEval: () => defineDurableEval,
|
|
259
268
|
deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
|
|
260
269
|
devNullWritableStream: () => devNullWritableStream,
|
|
261
270
|
evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
|
|
@@ -351,7 +360,8 @@ __export(exports_exports, {
|
|
|
351
360
|
wrapPiCodingAgentSDK: () => wrapPiCodingAgentSDK,
|
|
352
361
|
wrapStrandsAgentSDK: () => wrapStrandsAgentSDK,
|
|
353
362
|
wrapTraced: () => wrapTraced,
|
|
354
|
-
wrapVitest: () => wrapVitest
|
|
363
|
+
wrapVitest: () => wrapVitest,
|
|
364
|
+
wrapVoyageAI: () => wrapVoyageAI
|
|
355
365
|
});
|
|
356
366
|
|
|
357
367
|
// src/sandbox.ts
|
|
@@ -895,6 +905,13 @@ function wrapAISDK(aiSDK, options = {}) {
|
|
|
895
905
|
switch (prop) {
|
|
896
906
|
case "generateText":
|
|
897
907
|
return wrapGenerateText(typedAISDK.generateText, options, typedAISDK);
|
|
908
|
+
case "generateImage":
|
|
909
|
+
case "experimental_generateImage":
|
|
910
|
+
return typeof original === "function" ? wrapGenerateImage(
|
|
911
|
+
original,
|
|
912
|
+
options,
|
|
913
|
+
typedAISDK
|
|
914
|
+
) : original;
|
|
898
915
|
case "streamText":
|
|
899
916
|
return wrapStreamText(typedAISDK.streamText, options, typedAISDK);
|
|
900
917
|
case "generateObject":
|
|
@@ -1131,6 +1148,32 @@ var wrapGenerateObject = (generateObject, options = {}, aiSDK) => {
|
|
|
1131
1148
|
options
|
|
1132
1149
|
);
|
|
1133
1150
|
};
|
|
1151
|
+
var wrapGenerateImage = (generateImage, options = {}, aiSDK) => {
|
|
1152
|
+
return makeGenerateImageWrapper(generateImage, { aiSDK }, options);
|
|
1153
|
+
};
|
|
1154
|
+
var makeGenerateImageWrapper = (generateImage, contextOptions = {}, options = {}) => {
|
|
1155
|
+
const wrapper = async function(allParams) {
|
|
1156
|
+
const { span_info, ...params } = allParams;
|
|
1157
|
+
const tracedParams = { ...params };
|
|
1158
|
+
return aiSDKChannels.generateImage.tracePromise(
|
|
1159
|
+
() => generateImage(tracedParams),
|
|
1160
|
+
createAISDKChannelContext(tracedParams, {
|
|
1161
|
+
aiSDK: contextOptions.aiSDK,
|
|
1162
|
+
denyOutputPaths: options.denyOutputPaths,
|
|
1163
|
+
self: contextOptions.self,
|
|
1164
|
+
span_info: mergeSpanInfo(span_info, {
|
|
1165
|
+
name: "generateImage",
|
|
1166
|
+
spanType: contextOptions.spanType
|
|
1167
|
+
})
|
|
1168
|
+
})
|
|
1169
|
+
);
|
|
1170
|
+
};
|
|
1171
|
+
Object.defineProperty(wrapper, "name", {
|
|
1172
|
+
value: "generateImage",
|
|
1173
|
+
writable: false
|
|
1174
|
+
});
|
|
1175
|
+
return wrapper;
|
|
1176
|
+
};
|
|
1134
1177
|
var makeEmbedWrapper = (channel, name, embed, contextOptions = {}, options = {}) => {
|
|
1135
1178
|
const wrapper = async function(allParams) {
|
|
1136
1179
|
const { span_info, ...params } = allParams;
|
|
@@ -1612,7 +1655,7 @@ function normalizeUsageMetrics(usage, provider, providerMetadata) {
|
|
|
1612
1655
|
cacheCreationTokens
|
|
1613
1656
|
);
|
|
1614
1657
|
Object.assign(metrics, cacheTokens);
|
|
1615
|
-
|
|
1658
|
+
return toNumericMetrics(finalizeAnthropicTokens(metrics));
|
|
1616
1659
|
}
|
|
1617
1660
|
}
|
|
1618
1661
|
return metrics;
|
|
@@ -1881,7 +1924,7 @@ function braintrustEveHook(options) {
|
|
|
1881
1924
|
}
|
|
1882
1925
|
};
|
|
1883
1926
|
}
|
|
1884
|
-
function
|
|
1927
|
+
function createLegacyEveInstrumentation(options) {
|
|
1885
1928
|
const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
|
|
1886
1929
|
return {
|
|
1887
1930
|
events: {
|
|
@@ -3269,8 +3312,10 @@ function capturedModelInput(modelInput) {
|
|
|
3269
3312
|
const value = [];
|
|
3270
3313
|
if (typeof instructions === "string") {
|
|
3271
3314
|
value.push({ content: instructions, role: "system" });
|
|
3272
|
-
} else if (instructions) {
|
|
3315
|
+
} else if (Array.isArray(instructions)) {
|
|
3273
3316
|
value.push(...instructions.map(capturedEveModelMessage));
|
|
3317
|
+
} else if (instructions) {
|
|
3318
|
+
value.push(capturedEveModelMessage(instructions));
|
|
3274
3319
|
}
|
|
3275
3320
|
value.push(...messages.map(capturedEveModelMessage));
|
|
3276
3321
|
try {
|
|
@@ -3514,6 +3559,464 @@ async function deterministicEveId(...parts) {
|
|
|
3514
3559
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3515
3560
|
}
|
|
3516
3561
|
|
|
3562
|
+
// src/instrumentation/plugins/eve-provider.ts
|
|
3563
|
+
var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
|
|
3564
|
+
function createEveInstrumentationProvider(options = {}) {
|
|
3565
|
+
const bridge = new EveProviderBridge(options.metadata);
|
|
3566
|
+
return {
|
|
3567
|
+
capture: "content",
|
|
3568
|
+
events: {
|
|
3569
|
+
"action.completed": (event, context) => bridge.handleActionTerminal(event, context),
|
|
3570
|
+
"action.failed": (event, context) => bridge.handleActionTerminal(event, context),
|
|
3571
|
+
"action.started": (event, context) => bridge.handleActionStarted(event, context),
|
|
3572
|
+
"model.call.completed": (event, context) => bridge.handleModelTerminal(event, context),
|
|
3573
|
+
"model.call.failed": (event, context) => bridge.handleModelTerminal(event, context),
|
|
3574
|
+
"model.call.started": (event, context) => bridge.handleModelStarted(event, context),
|
|
3575
|
+
"step.attempt.completed": (event) => bridge.handleStepAttemptTerminal(event),
|
|
3576
|
+
"step.attempt.failed": (event) => bridge.handleStepAttemptTerminal(event),
|
|
3577
|
+
"turn.cancelled": (event, context) => bridge.handleTurnTerminal(event, context),
|
|
3578
|
+
"turn.completed": (event, context) => bridge.handleTurnTerminal(event, context),
|
|
3579
|
+
"turn.failed": (event, context) => bridge.handleTurnTerminal(event, context),
|
|
3580
|
+
"turn.started": (event, context) => bridge.handleTurnStarted(event, context)
|
|
3581
|
+
},
|
|
3582
|
+
flush,
|
|
3583
|
+
setup: options.setup
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
var EveProviderBridge = class {
|
|
3587
|
+
constructor(metadata) {
|
|
3588
|
+
this.metadata = metadata;
|
|
3589
|
+
}
|
|
3590
|
+
metadata;
|
|
3591
|
+
activeActions = /* @__PURE__ */ new Map();
|
|
3592
|
+
activeModels = /* @__PURE__ */ new Map();
|
|
3593
|
+
modelsByAttempt = /* @__PURE__ */ new Map();
|
|
3594
|
+
settledOperations = new LRUCache({
|
|
3595
|
+
max: MAX_EVE_PROVIDER_CACHE_ENTRIES
|
|
3596
|
+
});
|
|
3597
|
+
turns = new LRUCache({
|
|
3598
|
+
max: MAX_EVE_PROVIDER_CACHE_ENTRIES
|
|
3599
|
+
});
|
|
3600
|
+
async handleTurnStarted(event, context) {
|
|
3601
|
+
await this.contain("turn start", async () => {
|
|
3602
|
+
if (this.settledOperations.has(event.idempotencyKey)) {
|
|
3603
|
+
context.state.set({ skip: true });
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3606
|
+
const { rowId, spanId } = await generateEveIds(
|
|
3607
|
+
"turn",
|
|
3608
|
+
event.idempotencyKey
|
|
3609
|
+
);
|
|
3610
|
+
const key = turnKey2(event.sessionId, event.turnId);
|
|
3611
|
+
const stored = storedSpan(context);
|
|
3612
|
+
if (stored && "exported" in stored && stored.rootSpanId) {
|
|
3613
|
+
this.turns.set(key, { rootSpanId: stored.rootSpanId, spanId });
|
|
3614
|
+
return;
|
|
3615
|
+
}
|
|
3616
|
+
let rootSpanId = spanId;
|
|
3617
|
+
let parentSpanId;
|
|
3618
|
+
if (event.parentLineage) {
|
|
3619
|
+
const parentTurnKey = turnKey2(
|
|
3620
|
+
event.parentLineage.sessionId,
|
|
3621
|
+
event.parentLineage.turnId
|
|
3622
|
+
);
|
|
3623
|
+
const parentTurn = this.turns.get(parentTurnKey);
|
|
3624
|
+
rootSpanId = parentTurn?.rootSpanId ?? // Eve propagates the root session through every nested subagent. Use
|
|
3625
|
+
// it instead of the immediate parent session when no ancestor ran
|
|
3626
|
+
// in-process.
|
|
3627
|
+
(await generateEveIds(
|
|
3628
|
+
"turn",
|
|
3629
|
+
turnIdempotencyKey(
|
|
3630
|
+
event.rootSessionId,
|
|
3631
|
+
event.parentLineage.turnId
|
|
3632
|
+
)
|
|
3633
|
+
)).spanId;
|
|
3634
|
+
parentSpanId = (await generateEveIds(
|
|
3635
|
+
"subagent",
|
|
3636
|
+
actionIdempotencyKey(
|
|
3637
|
+
event.parentLineage.sessionId,
|
|
3638
|
+
event.parentLineage.turnId,
|
|
3639
|
+
event.parentLineage.callId
|
|
3640
|
+
)
|
|
3641
|
+
)).spanId;
|
|
3642
|
+
}
|
|
3643
|
+
const metadata = this.spanMetadata(event.sessionId);
|
|
3644
|
+
const span = await this.startSpan(
|
|
3645
|
+
context,
|
|
3646
|
+
{
|
|
3647
|
+
event: { id: rowId, metadata },
|
|
3648
|
+
name: "eve.turn",
|
|
3649
|
+
parentSpanIds: parentSpanId ? { rootSpanId, spanId: parentSpanId } : { parentSpanIds: [], rootSpanId },
|
|
3650
|
+
spanAttributes: { type: "task" /* TASK */ },
|
|
3651
|
+
spanId
|
|
3652
|
+
},
|
|
3653
|
+
rootSpanId
|
|
3654
|
+
);
|
|
3655
|
+
span?.log({ metadata });
|
|
3656
|
+
this.turns.set(key, {
|
|
3657
|
+
rootSpanId,
|
|
3658
|
+
spanId
|
|
3659
|
+
});
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
async handleTurnTerminal(event, context) {
|
|
3663
|
+
await this.contain("turn terminal", async () => {
|
|
3664
|
+
if (this.settledOperations.has(event.idempotencyKey)) {
|
|
3665
|
+
context.state.set(void 0);
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
const error = event.type === "turn.failed" ? event.error : void 0;
|
|
3669
|
+
this.drainActionsForTurn(event.sessionId, event.turnId, error);
|
|
3670
|
+
const stored = storedSpan(context);
|
|
3671
|
+
if (stored && "exported" in stored) {
|
|
3672
|
+
updateSpan({
|
|
3673
|
+
exported: stored.exported,
|
|
3674
|
+
...error !== void 0 ? { error } : {},
|
|
3675
|
+
metrics: { end: Date.now() / 1e3 }
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
this.settledOperations.set(event.idempotencyKey, true);
|
|
3679
|
+
context.state.set(void 0);
|
|
3680
|
+
this.turns.delete(turnKey2(event.sessionId, event.turnId));
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3683
|
+
async handleModelStarted(event, context) {
|
|
3684
|
+
await this.contain("model start", async () => {
|
|
3685
|
+
if (this.settledOperations.has(event.idempotencyKey)) {
|
|
3686
|
+
context.state.set({ skip: true });
|
|
3687
|
+
return;
|
|
3688
|
+
}
|
|
3689
|
+
const parent = await this.parentForScope(event.scope);
|
|
3690
|
+
const { rowId, spanId } = await generateEveIds(
|
|
3691
|
+
"step",
|
|
3692
|
+
event.idempotencyKey
|
|
3693
|
+
);
|
|
3694
|
+
const input = event.input ? capturedModelInput(event.input) : void 0;
|
|
3695
|
+
const metadata = {
|
|
3696
|
+
...this.spanMetadata(event.scope.sessionId),
|
|
3697
|
+
model: event.model.modelId,
|
|
3698
|
+
provider: event.model.provider
|
|
3699
|
+
};
|
|
3700
|
+
const span = await this.startSpan(context, {
|
|
3701
|
+
event: {
|
|
3702
|
+
id: rowId,
|
|
3703
|
+
...input !== void 0 ? { input } : {},
|
|
3704
|
+
metadata
|
|
3705
|
+
},
|
|
3706
|
+
name: "eve.step",
|
|
3707
|
+
parentSpanIds: parent,
|
|
3708
|
+
spanAttributes: { type: "llm" /* LLM */ },
|
|
3709
|
+
spanId
|
|
3710
|
+
});
|
|
3711
|
+
if (!span) return;
|
|
3712
|
+
span.log({ ...input !== void 0 ? { input } : {}, metadata });
|
|
3713
|
+
this.activeModels.set(event.idempotencyKey, {
|
|
3714
|
+
span,
|
|
3715
|
+
turnKey: turnKey2(event.scope.sessionId, event.scope.turnId)
|
|
3716
|
+
});
|
|
3717
|
+
const keys = this.modelsByAttempt.get(event.scope.attemptId) ?? /* @__PURE__ */ new Set();
|
|
3718
|
+
keys.add(event.idempotencyKey);
|
|
3719
|
+
this.modelsByAttempt.set(event.scope.attemptId, keys);
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
async handleModelTerminal(event, context) {
|
|
3723
|
+
await this.contain("model terminal", async () => {
|
|
3724
|
+
if (this.settledOperations.has(event.idempotencyKey)) {
|
|
3725
|
+
context.state.set(void 0);
|
|
3726
|
+
return;
|
|
3727
|
+
}
|
|
3728
|
+
const active = this.activeModels.get(event.idempotencyKey);
|
|
3729
|
+
if (active) {
|
|
3730
|
+
const span = active.span;
|
|
3731
|
+
if (event.type === "model.call.failed") {
|
|
3732
|
+
if (event.error !== void 0) span.log({ error: event.error });
|
|
3733
|
+
} else {
|
|
3734
|
+
span.log({
|
|
3735
|
+
metrics: usageMetrics(event.usage),
|
|
3736
|
+
output: modelOutput(event)
|
|
3737
|
+
});
|
|
3738
|
+
}
|
|
3739
|
+
span.end();
|
|
3740
|
+
} else {
|
|
3741
|
+
const stored = storedSpan(context);
|
|
3742
|
+
if (stored && "exported" in stored) {
|
|
3743
|
+
updateSpan({
|
|
3744
|
+
exported: stored.exported,
|
|
3745
|
+
...event.type === "model.call.failed" ? event.error !== void 0 ? { error: event.error } : {} : { output: modelOutput(event) },
|
|
3746
|
+
metrics: {
|
|
3747
|
+
...event.type === "model.call.completed" ? usageMetrics(event.usage) : {},
|
|
3748
|
+
end: Date.now() / 1e3
|
|
3749
|
+
}
|
|
3750
|
+
});
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
this.settledOperations.set(event.idempotencyKey, true);
|
|
3754
|
+
this.forgetModel(event.scope.attemptId, event.idempotencyKey);
|
|
3755
|
+
context.state.set(void 0);
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3758
|
+
async handleActionStarted(event, context) {
|
|
3759
|
+
await this.contain("action start", async () => {
|
|
3760
|
+
if (event.kind === "load-skill" || this.settledOperations.has(event.idempotencyKey)) {
|
|
3761
|
+
context.state.set({ skip: true });
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
const parent = await this.parentForScope(event.scope);
|
|
3765
|
+
const { rowId, spanId } = await generateEveIds(
|
|
3766
|
+
event.kind === "subagent-call" ? "subagent" : "tool",
|
|
3767
|
+
event.idempotencyKey
|
|
3768
|
+
);
|
|
3769
|
+
const metadata = {
|
|
3770
|
+
...this.spanMetadata(event.scope.sessionId),
|
|
3771
|
+
"eve.action_kind": event.kind
|
|
3772
|
+
};
|
|
3773
|
+
const span = await this.startSpan(context, {
|
|
3774
|
+
event: {
|
|
3775
|
+
id: rowId,
|
|
3776
|
+
...event.input !== void 0 ? { input: event.input } : {},
|
|
3777
|
+
metadata
|
|
3778
|
+
},
|
|
3779
|
+
name: event.name,
|
|
3780
|
+
parentSpanIds: parent,
|
|
3781
|
+
spanAttributes: { type: "tool" /* TOOL */ },
|
|
3782
|
+
spanId
|
|
3783
|
+
});
|
|
3784
|
+
if (!span) return;
|
|
3785
|
+
span.log({
|
|
3786
|
+
...event.input !== void 0 ? { input: event.input } : {},
|
|
3787
|
+
metadata
|
|
3788
|
+
});
|
|
3789
|
+
this.activeActions.set(event.idempotencyKey, {
|
|
3790
|
+
span,
|
|
3791
|
+
turnKey: turnKey2(event.scope.sessionId, event.scope.turnId)
|
|
3792
|
+
});
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
async handleActionTerminal(event, context) {
|
|
3796
|
+
await this.contain("action terminal", async () => {
|
|
3797
|
+
if (this.settledOperations.has(event.idempotencyKey)) {
|
|
3798
|
+
context.state.set(void 0);
|
|
3799
|
+
return;
|
|
3800
|
+
}
|
|
3801
|
+
const stored = storedSpan(context);
|
|
3802
|
+
if (stored?.skip) {
|
|
3803
|
+
context.state.set(void 0);
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
const active = this.activeActions.get(event.idempotencyKey);
|
|
3807
|
+
const end = finiteTimestamp(event.acceptedAtMs) ?? Date.now();
|
|
3808
|
+
if (active) {
|
|
3809
|
+
const span = active.span;
|
|
3810
|
+
if (event.type === "action.failed") {
|
|
3811
|
+
span.log({
|
|
3812
|
+
error: event.error ?? new Error(event.errorCode ?? `Eve action ${event.outcome}`)
|
|
3813
|
+
});
|
|
3814
|
+
} else if (event.output.type === "error") {
|
|
3815
|
+
span.log({
|
|
3816
|
+
error: event.output.error ?? new Error("Eve action returned an error")
|
|
3817
|
+
});
|
|
3818
|
+
} else {
|
|
3819
|
+
span.log({ output: event.output.output });
|
|
3820
|
+
}
|
|
3821
|
+
span.end({ endTime: end / 1e3 });
|
|
3822
|
+
} else {
|
|
3823
|
+
const stored2 = storedSpan(context);
|
|
3824
|
+
if (stored2 && "exported" in stored2) {
|
|
3825
|
+
updateSpan({
|
|
3826
|
+
exported: stored2.exported,
|
|
3827
|
+
...event.type === "action.failed" ? {
|
|
3828
|
+
error: event.error ?? new Error(event.errorCode ?? `Eve action ${event.outcome}`)
|
|
3829
|
+
} : event.output.type === "error" ? {
|
|
3830
|
+
error: event.output.error ?? new Error("Eve action returned an error")
|
|
3831
|
+
} : { output: event.output.output },
|
|
3832
|
+
metrics: { end: end / 1e3 }
|
|
3833
|
+
});
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
this.settledOperations.set(event.idempotencyKey, true);
|
|
3837
|
+
this.activeActions.delete(event.idempotencyKey);
|
|
3838
|
+
context.state.set(void 0);
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3841
|
+
handleStepAttemptTerminal(event) {
|
|
3842
|
+
void this.contain("step attempt terminal", () => {
|
|
3843
|
+
const keys = this.modelsByAttempt.get(event.scope.attemptId);
|
|
3844
|
+
if (!keys) return;
|
|
3845
|
+
for (const key of keys) {
|
|
3846
|
+
const active = this.activeModels.get(key);
|
|
3847
|
+
if (!active) continue;
|
|
3848
|
+
if (event.type === "step.attempt.failed" && event.error !== void 0) {
|
|
3849
|
+
active.span.log({ error: event.error });
|
|
3850
|
+
}
|
|
3851
|
+
active.span.end();
|
|
3852
|
+
this.activeModels.delete(key);
|
|
3853
|
+
}
|
|
3854
|
+
this.modelsByAttempt.delete(event.scope.attemptId);
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
async parentForScope(scope) {
|
|
3858
|
+
const key = turnKey2(scope.sessionId, scope.turnId);
|
|
3859
|
+
const known = this.turns.get(key);
|
|
3860
|
+
if (known) {
|
|
3861
|
+
return { rootSpanId: known.rootSpanId, spanId: known.spanId };
|
|
3862
|
+
}
|
|
3863
|
+
const [{ spanId }, { spanId: rootSpanId }] = await Promise.all([
|
|
3864
|
+
generateEveIds("turn", turnIdempotencyKey(scope.sessionId, scope.turnId)),
|
|
3865
|
+
generateEveIds(
|
|
3866
|
+
"turn",
|
|
3867
|
+
turnIdempotencyKey(
|
|
3868
|
+
scope.rootSessionId ?? scope.sessionId,
|
|
3869
|
+
scope.turnId
|
|
3870
|
+
)
|
|
3871
|
+
)
|
|
3872
|
+
]);
|
|
3873
|
+
return { rootSpanId, spanId };
|
|
3874
|
+
}
|
|
3875
|
+
async startSpan(context, args, rootSpanId) {
|
|
3876
|
+
const span = withCurrent(
|
|
3877
|
+
NOOP_SPAN,
|
|
3878
|
+
() => _internalStartSpanWithInitialMerge(
|
|
3879
|
+
withSpanInstrumentationName(args ?? {}, INSTRUMENTATION_NAMES.EVE)
|
|
3880
|
+
)
|
|
3881
|
+
);
|
|
3882
|
+
try {
|
|
3883
|
+
context.state.set({
|
|
3884
|
+
exported: await span.export(),
|
|
3885
|
+
...rootSpanId ? { rootSpanId } : {}
|
|
3886
|
+
});
|
|
3887
|
+
} catch (error) {
|
|
3888
|
+
debugLogger.warn("Error exporting Eve provider span:", error);
|
|
3889
|
+
}
|
|
3890
|
+
return span;
|
|
3891
|
+
}
|
|
3892
|
+
drainActionsForTurn(sessionId, turnId, error) {
|
|
3893
|
+
const key = turnKey2(sessionId, turnId);
|
|
3894
|
+
for (const [idempotencyKey, active] of this.activeActions) {
|
|
3895
|
+
if (active.turnKey !== key) continue;
|
|
3896
|
+
if (error !== void 0) active.span.log({ error });
|
|
3897
|
+
active.span.end();
|
|
3898
|
+
this.activeActions.delete(idempotencyKey);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
forgetModel(attemptId, idempotencyKey) {
|
|
3902
|
+
this.activeModels.delete(idempotencyKey);
|
|
3903
|
+
const keys = this.modelsByAttempt.get(attemptId);
|
|
3904
|
+
keys?.delete(idempotencyKey);
|
|
3905
|
+
if (keys?.size === 0) this.modelsByAttempt.delete(attemptId);
|
|
3906
|
+
}
|
|
3907
|
+
spanMetadata(sessionId) {
|
|
3908
|
+
return {
|
|
3909
|
+
...this.metadata ?? {},
|
|
3910
|
+
"eve.session_id": sessionId
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
async contain(operation, fn) {
|
|
3914
|
+
try {
|
|
3915
|
+
await fn();
|
|
3916
|
+
} catch (error) {
|
|
3917
|
+
debugLogger.warn(`Error in Eve provider ${operation}:`, error);
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
};
|
|
3921
|
+
function storedSpan(context) {
|
|
3922
|
+
const value = context.state.get();
|
|
3923
|
+
if (!isObject(value)) return void 0;
|
|
3924
|
+
if (value["skip"] === true) return { skip: true };
|
|
3925
|
+
return typeof value["exported"] === "string" ? {
|
|
3926
|
+
exported: value["exported"],
|
|
3927
|
+
...typeof value["rootSpanId"] === "string" && value["rootSpanId"].length > 0 ? { rootSpanId: value["rootSpanId"] } : {}
|
|
3928
|
+
} : void 0;
|
|
3929
|
+
}
|
|
3930
|
+
function modelOutput(event) {
|
|
3931
|
+
const content = event.content ?? [];
|
|
3932
|
+
let text = "";
|
|
3933
|
+
const reasoning = [];
|
|
3934
|
+
const toolCalls2 = [];
|
|
3935
|
+
for (const part of content) {
|
|
3936
|
+
if (part.type === "text") {
|
|
3937
|
+
text += part.text;
|
|
3938
|
+
} else if (part.type === "reasoning" && part.text.trim().length > 0) {
|
|
3939
|
+
reasoning.push({ content: part.text });
|
|
3940
|
+
} else if (part.type === "tool-call") {
|
|
3941
|
+
toolCalls2.push({
|
|
3942
|
+
function: {
|
|
3943
|
+
arguments: safeJsonStringify(part.input),
|
|
3944
|
+
name: part.toolName
|
|
3945
|
+
},
|
|
3946
|
+
id: part.callId,
|
|
3947
|
+
type: "function"
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
return [
|
|
3952
|
+
{
|
|
3953
|
+
finish_reason: normalizeFinishReason2(event.finishReason),
|
|
3954
|
+
index: 0,
|
|
3955
|
+
message: {
|
|
3956
|
+
content: text || null,
|
|
3957
|
+
...reasoning.length > 0 ? { reasoning } : {},
|
|
3958
|
+
role: "assistant",
|
|
3959
|
+
...toolCalls2.length > 0 ? { tool_calls: toolCalls2 } : {}
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
];
|
|
3963
|
+
}
|
|
3964
|
+
function usageMetrics(usage) {
|
|
3965
|
+
const promptTokens = nonNegativeNumber(usage.inputTokens);
|
|
3966
|
+
const completionTokens = nonNegativeNumber(usage.outputTokens);
|
|
3967
|
+
const cachedTokens = nonNegativeNumber(
|
|
3968
|
+
usage.inputTokenDetails?.cacheReadTokens
|
|
3969
|
+
);
|
|
3970
|
+
const cacheCreationTokens = nonNegativeNumber(
|
|
3971
|
+
usage.inputTokenDetails?.cacheWriteTokens
|
|
3972
|
+
);
|
|
3973
|
+
return {
|
|
3974
|
+
...promptTokens !== void 0 ? { prompt_tokens: promptTokens } : {},
|
|
3975
|
+
...completionTokens !== void 0 ? { completion_tokens: completionTokens } : {},
|
|
3976
|
+
...promptTokens !== void 0 && completionTokens !== void 0 ? { tokens: promptTokens + completionTokens } : {},
|
|
3977
|
+
...cachedTokens !== void 0 ? { prompt_cached_tokens: cachedTokens } : {},
|
|
3978
|
+
...cacheCreationTokens !== void 0 ? { prompt_cache_creation_tokens: cacheCreationTokens } : {}
|
|
3979
|
+
};
|
|
3980
|
+
}
|
|
3981
|
+
function nonNegativeNumber(value) {
|
|
3982
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
3983
|
+
}
|
|
3984
|
+
function finiteTimestamp(value) {
|
|
3985
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3986
|
+
}
|
|
3987
|
+
function normalizeFinishReason2(value) {
|
|
3988
|
+
if (value === "content-filter") return "content_filter";
|
|
3989
|
+
if (value === "tool-calls") return "tool_calls";
|
|
3990
|
+
return value;
|
|
3991
|
+
}
|
|
3992
|
+
function safeJsonStringify(value) {
|
|
3993
|
+
try {
|
|
3994
|
+
return JSON.stringify(value) ?? "null";
|
|
3995
|
+
} catch {
|
|
3996
|
+
return "null";
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
function turnKey2(sessionId, turnId) {
|
|
4000
|
+
return `${sessionId}:${turnId}`;
|
|
4001
|
+
}
|
|
4002
|
+
function turnIdempotencyKey(sessionId, turnId) {
|
|
4003
|
+
return `turn:${sessionId}:${turnId}`;
|
|
4004
|
+
}
|
|
4005
|
+
function actionIdempotencyKey(sessionId, turnId, callId) {
|
|
4006
|
+
return `action:${sessionId}:${turnId}:${callId}`;
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
// src/instrumentation/plugins/eve-instrumentation.ts
|
|
4010
|
+
var EVE_INSTRUMENTATION_PROVIDER = /* @__PURE__ */ Symbol.for("eve.instrumentation.provider");
|
|
4011
|
+
function braintrustEveInstrumentation(options) {
|
|
4012
|
+
const definition = "defineState" in options ? createLegacyEveInstrumentation(options) : createEveInstrumentationProvider(options);
|
|
4013
|
+
const declaration = {
|
|
4014
|
+
...definition,
|
|
4015
|
+
[EVE_INSTRUMENTATION_PROVIDER]: true
|
|
4016
|
+
};
|
|
4017
|
+
return declaration;
|
|
4018
|
+
}
|
|
4019
|
+
|
|
3517
4020
|
// src/typed-instrumentation-helpers.ts
|
|
3518
4021
|
var TypedApplyProxy = Proxy;
|
|
3519
4022
|
|
|
@@ -3695,11 +4198,11 @@ function wrapClaudeAgentQuery(queryFn, defaultThis) {
|
|
|
3695
4198
|
}
|
|
3696
4199
|
};
|
|
3697
4200
|
const invocationTarget = thisArg === proxy || thisArg === void 0 ? defaultThis ?? thisArg : thisArg;
|
|
3698
|
-
return claudeAgentSDKChannels.query.
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
{
|
|
4201
|
+
return claudeAgentSDKChannels.query.invoke(
|
|
4202
|
+
target,
|
|
4203
|
+
invocationTarget,
|
|
4204
|
+
[wrappedParams],
|
|
4205
|
+
{}
|
|
3703
4206
|
);
|
|
3704
4207
|
}
|
|
3705
4208
|
});
|
|
@@ -4314,13 +4817,11 @@ function wrapAgentInstance(agent) {
|
|
|
4314
4817
|
if (prop === "stream" && typeof value === "function") {
|
|
4315
4818
|
return function(args, options) {
|
|
4316
4819
|
const callArgs = [args, options];
|
|
4317
|
-
return strandsAgentSDKChannels.agentStream.
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
self: proxy
|
|
4323
|
-
}
|
|
4820
|
+
return strandsAgentSDKChannels.agentStream.invoke(
|
|
4821
|
+
value,
|
|
4822
|
+
target,
|
|
4823
|
+
callArgs,
|
|
4824
|
+
{ agent: proxy }
|
|
4324
4825
|
);
|
|
4325
4826
|
};
|
|
4326
4827
|
}
|
|
@@ -4351,12 +4852,12 @@ function wrapMultiAgentInstance(orchestrator, kind) {
|
|
|
4351
4852
|
return function(input, options) {
|
|
4352
4853
|
const callArgs = [input, options];
|
|
4353
4854
|
const channel = kind === "graph" ? strandsAgentSDKChannels.graphStream : strandsAgentSDKChannels.swarmStream;
|
|
4354
|
-
return channel.
|
|
4355
|
-
|
|
4855
|
+
return channel.invoke(
|
|
4856
|
+
value,
|
|
4857
|
+
target,
|
|
4858
|
+
callArgs,
|
|
4356
4859
|
{
|
|
4357
|
-
|
|
4358
|
-
orchestrator: proxy,
|
|
4359
|
-
self: proxy
|
|
4860
|
+
orchestrator: proxy
|
|
4360
4861
|
}
|
|
4361
4862
|
);
|
|
4362
4863
|
};
|
|
@@ -5725,6 +6226,84 @@ function wrapRerank3(rerank) {
|
|
|
5725
6226
|
});
|
|
5726
6227
|
}
|
|
5727
6228
|
|
|
6229
|
+
// src/wrappers/voyageai.ts
|
|
6230
|
+
function wrapVoyageAI(client) {
|
|
6231
|
+
if (!isSupportedVoyageAIClient(client)) {
|
|
6232
|
+
console.warn("Unsupported Voyage AI library. Not wrapping.");
|
|
6233
|
+
return client;
|
|
6234
|
+
}
|
|
6235
|
+
return voyageAIProxy(client);
|
|
6236
|
+
}
|
|
6237
|
+
var voyageAIProxyCache = /* @__PURE__ */ new WeakMap();
|
|
6238
|
+
function isObject2(value) {
|
|
6239
|
+
return typeof value === "object" && value !== null;
|
|
6240
|
+
}
|
|
6241
|
+
function hasFunction5(value, name) {
|
|
6242
|
+
return typeof Reflect.get(value, name) === "function";
|
|
6243
|
+
}
|
|
6244
|
+
function isSupportedVoyageAIClient(value) {
|
|
6245
|
+
if (!isObject2(value)) {
|
|
6246
|
+
return false;
|
|
6247
|
+
}
|
|
6248
|
+
return hasFunction5(value, "embed") || hasFunction5(value, "multimodalEmbed") || hasFunction5(value, "rerank") || hasFunction5(value, "contextualizedEmbed");
|
|
6249
|
+
}
|
|
6250
|
+
function voyageAIProxy(client) {
|
|
6251
|
+
const cached = voyageAIProxyCache.get(client);
|
|
6252
|
+
if (cached) {
|
|
6253
|
+
return cached;
|
|
6254
|
+
}
|
|
6255
|
+
const proxy = new Proxy(client, {
|
|
6256
|
+
get(target, prop, receiver) {
|
|
6257
|
+
switch (prop) {
|
|
6258
|
+
case "embed":
|
|
6259
|
+
if (typeof target.embed !== "function") {
|
|
6260
|
+
return target.embed;
|
|
6261
|
+
}
|
|
6262
|
+
return (request, options) => voyageAIChannels.embed.invoke(
|
|
6263
|
+
target.embed,
|
|
6264
|
+
target,
|
|
6265
|
+
[request, options],
|
|
6266
|
+
{}
|
|
6267
|
+
);
|
|
6268
|
+
case "multimodalEmbed":
|
|
6269
|
+
if (typeof target.multimodalEmbed !== "function") {
|
|
6270
|
+
return target.multimodalEmbed;
|
|
6271
|
+
}
|
|
6272
|
+
return (request, options) => voyageAIChannels.multimodalEmbed.invoke(
|
|
6273
|
+
target.multimodalEmbed,
|
|
6274
|
+
target,
|
|
6275
|
+
[request, options],
|
|
6276
|
+
{}
|
|
6277
|
+
);
|
|
6278
|
+
case "rerank":
|
|
6279
|
+
if (typeof target.rerank !== "function") {
|
|
6280
|
+
return target.rerank;
|
|
6281
|
+
}
|
|
6282
|
+
return (request, options) => voyageAIChannels.rerank.invoke(
|
|
6283
|
+
target.rerank,
|
|
6284
|
+
target,
|
|
6285
|
+
[request, options],
|
|
6286
|
+
{}
|
|
6287
|
+
);
|
|
6288
|
+
case "contextualizedEmbed":
|
|
6289
|
+
if (typeof target.contextualizedEmbed !== "function") {
|
|
6290
|
+
return target.contextualizedEmbed;
|
|
6291
|
+
}
|
|
6292
|
+
return (request, options) => voyageAIChannels.contextualizedEmbed.invoke(
|
|
6293
|
+
target.contextualizedEmbed,
|
|
6294
|
+
target,
|
|
6295
|
+
[request, options],
|
|
6296
|
+
{}
|
|
6297
|
+
);
|
|
6298
|
+
default:
|
|
6299
|
+
return Reflect.get(target, prop, receiver);
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
});
|
|
6303
|
+
voyageAIProxyCache.set(client, proxy);
|
|
6304
|
+
return proxy;
|
|
6305
|
+
}
|
|
6306
|
+
|
|
5728
6307
|
// src/wrappers/groq.ts
|
|
5729
6308
|
function wrapGroq(groq) {
|
|
5730
6309
|
if (isSupportedGroqClient(groq)) {
|
|
@@ -5736,14 +6315,14 @@ function wrapGroq(groq) {
|
|
|
5736
6315
|
function isRecord4(value) {
|
|
5737
6316
|
return typeof value === "object" && value !== null;
|
|
5738
6317
|
}
|
|
5739
|
-
function
|
|
6318
|
+
function hasFunction6(value, methodName) {
|
|
5740
6319
|
return isRecord4(value) && methodName in value && typeof value[methodName] === "function";
|
|
5741
6320
|
}
|
|
5742
6321
|
function hasChat2(value) {
|
|
5743
|
-
return isRecord4(value) && isRecord4(value.completions) &&
|
|
6322
|
+
return isRecord4(value) && isRecord4(value.completions) && hasFunction6(value.completions, "create");
|
|
5744
6323
|
}
|
|
5745
6324
|
function hasEmbeddings2(value) {
|
|
5746
|
-
return
|
|
6325
|
+
return hasFunction6(value, "create");
|
|
5747
6326
|
}
|
|
5748
6327
|
function isSupportedGroqClient(value) {
|
|
5749
6328
|
return isRecord4(value) && (value.chat !== void 0 && hasChat2(value.chat) || value.embeddings !== void 0 && hasEmbeddings2(value.embeddings));
|
|
@@ -6230,9 +6809,6 @@ var VitestContextManager = class {
|
|
|
6230
6809
|
getCurrentContext() {
|
|
6231
6810
|
return this.contextStorage.getStore();
|
|
6232
6811
|
}
|
|
6233
|
-
setContext(context) {
|
|
6234
|
-
this.contextStorage.enterWith(context);
|
|
6235
|
-
}
|
|
6236
6812
|
runInContext(context, callback) {
|
|
6237
6813
|
return this.contextStorage.run(context, callback);
|
|
6238
6814
|
}
|
|
@@ -6285,7 +6861,7 @@ async function flushExperimentWithSync(context, config) {
|
|
|
6285
6861
|
// src/wrappers/shared/scorers.ts
|
|
6286
6862
|
async function runScorers(args) {
|
|
6287
6863
|
const { scorers, output, expected, input, metadata, span } = args;
|
|
6288
|
-
const
|
|
6864
|
+
const scorerArgs2 = {
|
|
6289
6865
|
output,
|
|
6290
6866
|
expected,
|
|
6291
6867
|
input,
|
|
@@ -6294,7 +6870,7 @@ async function runScorers(args) {
|
|
|
6294
6870
|
await Promise.all(
|
|
6295
6871
|
scorers.map(async (scorer) => {
|
|
6296
6872
|
try {
|
|
6297
|
-
const result = await scorer(
|
|
6873
|
+
const result = await scorer(scorerArgs2);
|
|
6298
6874
|
const scores = normalizeScores(result);
|
|
6299
6875
|
if (scores.length > 0) {
|
|
6300
6876
|
const accScores = {};
|
|
@@ -6577,8 +7153,7 @@ function wrapDescribe(originalDescribe, config, afterAll) {
|
|
|
6577
7153
|
if (config.onProgress) {
|
|
6578
7154
|
config.onProgress({ type: "suite_start", suiteName });
|
|
6579
7155
|
}
|
|
6580
|
-
contextManager.
|
|
6581
|
-
factory();
|
|
7156
|
+
contextManager.runInContext(lazyContext, factory);
|
|
6582
7157
|
if (afterAll) {
|
|
6583
7158
|
afterAll(async () => {
|
|
6584
7159
|
await flushExperimentWithSync(context, config);
|
|
@@ -8653,6 +9228,26 @@ async function getExperimentParametersRef(parameters) {
|
|
|
8653
9228
|
version: resolvedParameters.version
|
|
8654
9229
|
};
|
|
8655
9230
|
}
|
|
9231
|
+
async function _internalInitEvaluatorExperiment(projectName, evaluator, data, options = {}) {
|
|
9232
|
+
if (options.disabled) return null;
|
|
9233
|
+
const { baseExperiment } = callEvaluatorData(data);
|
|
9234
|
+
const parameters = await getExperimentParametersRef(evaluator.parameters);
|
|
9235
|
+
return initExperiment2(evaluator.state, {
|
|
9236
|
+
...evaluator.projectId ? { projectId: evaluator.projectId } : { project: projectName },
|
|
9237
|
+
experiment: options.experimentName ?? evaluator.experimentName,
|
|
9238
|
+
description: evaluator.description,
|
|
9239
|
+
metadata: evaluator.metadata,
|
|
9240
|
+
tags: evaluator.tags,
|
|
9241
|
+
isPublic: evaluator.isPublic,
|
|
9242
|
+
update: options.update ?? evaluator.update,
|
|
9243
|
+
baseExperiment: evaluator.baseExperimentName ?? baseExperiment,
|
|
9244
|
+
baseExperimentId: evaluator.baseExperimentId,
|
|
9245
|
+
gitMetadataSettings: evaluator.gitMetadataSettings,
|
|
9246
|
+
repoInfo: evaluator.repoInfo,
|
|
9247
|
+
dataset: Dataset.isDataset(data) ? data : void 0,
|
|
9248
|
+
parameters
|
|
9249
|
+
});
|
|
9250
|
+
}
|
|
8656
9251
|
function callEvaluatorData(data) {
|
|
8657
9252
|
const dataResult = typeof data === "function" ? data() : data;
|
|
8658
9253
|
let baseExperiment = void 0;
|
|
@@ -8670,6 +9265,48 @@ function isAsyncIterable2(value) {
|
|
|
8670
9265
|
function isIterable(value) {
|
|
8671
9266
|
return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
|
|
8672
9267
|
}
|
|
9268
|
+
async function _internalResolveEvaluatorData(evaluator, experiment) {
|
|
9269
|
+
if (typeof evaluator.data === "string") {
|
|
9270
|
+
throw new Error("Unimplemented: string data paths");
|
|
9271
|
+
}
|
|
9272
|
+
let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
|
|
9273
|
+
if ("_type" in dataResult) {
|
|
9274
|
+
if (dataResult._type !== "BaseExperiment") {
|
|
9275
|
+
throw new Error("Invalid _type");
|
|
9276
|
+
}
|
|
9277
|
+
if (!experiment) {
|
|
9278
|
+
throw new Error(
|
|
9279
|
+
"Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
|
|
9280
|
+
);
|
|
9281
|
+
}
|
|
9282
|
+
let name = dataResult.name;
|
|
9283
|
+
if (isEmpty(name)) {
|
|
9284
|
+
const baseExperiment = await experiment.fetchBaseExperiment();
|
|
9285
|
+
if (!baseExperiment) {
|
|
9286
|
+
throw new Error("BaseExperiment() failed to fetch base experiment");
|
|
9287
|
+
}
|
|
9288
|
+
name = baseExperiment.name;
|
|
9289
|
+
}
|
|
9290
|
+
dataResult = initExperiment2(evaluator.state, {
|
|
9291
|
+
...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
|
|
9292
|
+
experiment: name,
|
|
9293
|
+
open: true
|
|
9294
|
+
}).asDataset();
|
|
9295
|
+
}
|
|
9296
|
+
const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
|
|
9297
|
+
if (isAsyncIterable2(resolvedDataResult)) {
|
|
9298
|
+
return resolvedDataResult;
|
|
9299
|
+
}
|
|
9300
|
+
if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
|
|
9301
|
+
const iterable = resolvedDataResult;
|
|
9302
|
+
return (async function* () {
|
|
9303
|
+
for (const datum of iterable) yield datum;
|
|
9304
|
+
})();
|
|
9305
|
+
}
|
|
9306
|
+
throw new Error(
|
|
9307
|
+
"Evaluator data must be an array, iterable, or async iterable"
|
|
9308
|
+
);
|
|
9309
|
+
}
|
|
8673
9310
|
globalThis._evals = {
|
|
8674
9311
|
functions: [],
|
|
8675
9312
|
prompts: [],
|
|
@@ -8716,25 +9353,13 @@ async function Eval(name, evaluator, reporterOrOpts) {
|
|
|
8716
9353
|
}
|
|
8717
9354
|
const resolvedReporter = options.reporter || defaultReporter;
|
|
8718
9355
|
try {
|
|
8719
|
-
const { data
|
|
8720
|
-
|
|
9356
|
+
const { data } = callEvaluatorData(evaluator.data);
|
|
9357
|
+
const experiment = await _internalInitEvaluatorExperiment(
|
|
9358
|
+
name,
|
|
9359
|
+
evaluator,
|
|
9360
|
+
data,
|
|
9361
|
+
{ disabled: Boolean(options.parent || options.noSendLogs) }
|
|
8721
9362
|
);
|
|
8722
|
-
const parameters = await getExperimentParametersRef(evaluator.parameters);
|
|
8723
|
-
const experiment = options.parent || options.noSendLogs ? null : initExperiment2(evaluator.state, {
|
|
8724
|
-
...evaluator.projectId ? { projectId: evaluator.projectId } : { project: name },
|
|
8725
|
-
experiment: evaluator.experimentName,
|
|
8726
|
-
description: evaluator.description,
|
|
8727
|
-
metadata: evaluator.metadata,
|
|
8728
|
-
tags: evaluator.tags,
|
|
8729
|
-
isPublic: evaluator.isPublic,
|
|
8730
|
-
update: evaluator.update,
|
|
8731
|
-
baseExperiment: evaluator.baseExperimentName ?? defaultBaseExperiment,
|
|
8732
|
-
baseExperimentId: evaluator.baseExperimentId,
|
|
8733
|
-
gitMetadataSettings: evaluator.gitMetadataSettings,
|
|
8734
|
-
repoInfo: evaluator.repoInfo,
|
|
8735
|
-
dataset: Dataset.isDataset(data) ? data : void 0,
|
|
8736
|
-
parameters
|
|
8737
|
-
});
|
|
8738
9363
|
if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
|
|
8739
9364
|
await experiment._waitForId();
|
|
8740
9365
|
}
|
|
@@ -8832,30 +9457,85 @@ function scorerName(scorer, scorer_idx) {
|
|
|
8832
9457
|
function classifierName(classifier, classifier_idx) {
|
|
8833
9458
|
return classifier.name || `classifier_${classifier_idx}`;
|
|
8834
9459
|
}
|
|
9460
|
+
async function _internalRunEvaluatorTask(task, datum, trialIndex, parameters, span, reportProgress = () => void 0) {
|
|
9461
|
+
const metadata = {
|
|
9462
|
+
..."metadata" in datum ? datum.metadata : {}
|
|
9463
|
+
};
|
|
9464
|
+
const hooks = {
|
|
9465
|
+
meta(value) {
|
|
9466
|
+
Object.assign(metadata, value);
|
|
9467
|
+
},
|
|
9468
|
+
metadata,
|
|
9469
|
+
expected: "expected" in datum ? datum.expected : void 0,
|
|
9470
|
+
span,
|
|
9471
|
+
parameters,
|
|
9472
|
+
reportProgress,
|
|
9473
|
+
trialIndex,
|
|
9474
|
+
tags: [...datum.tags ?? []]
|
|
9475
|
+
};
|
|
9476
|
+
const output = await task(datum.input, hooks);
|
|
9477
|
+
span.log({ output });
|
|
9478
|
+
return {
|
|
9479
|
+
output,
|
|
9480
|
+
metadata: hooks.metadata,
|
|
9481
|
+
tags: hooks.tags ?? []
|
|
9482
|
+
};
|
|
9483
|
+
}
|
|
8835
9484
|
function buildSpanMetadata(results) {
|
|
8836
|
-
return results.length === 1 ? results[0].metadata :
|
|
8837
|
-
(
|
|
8838
|
-
{}
|
|
9485
|
+
return results.length === 1 ? results[0].metadata : Object.fromEntries(
|
|
9486
|
+
results.map((result) => [result.name, result.metadata])
|
|
8839
9487
|
);
|
|
8840
9488
|
}
|
|
8841
9489
|
function buildSpanScores(results) {
|
|
8842
|
-
const scoresRecord =
|
|
8843
|
-
(
|
|
8844
|
-
{}
|
|
9490
|
+
const scoresRecord = Object.fromEntries(
|
|
9491
|
+
results.map((result) => [result.name, result.score])
|
|
8845
9492
|
);
|
|
8846
9493
|
return { resultMetadata: buildSpanMetadata(results), scoresRecord };
|
|
8847
9494
|
}
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
9495
|
+
function _internalPrepareEvaluatorScore(scoreValue, name) {
|
|
9496
|
+
if (scoreValue === null) return { results: null };
|
|
9497
|
+
if (Array.isArray(scoreValue)) {
|
|
9498
|
+
for (const score of scoreValue) {
|
|
9499
|
+
if (!(typeof score === "object" && !isEmpty(score))) {
|
|
9500
|
+
throw new Error(
|
|
9501
|
+
`When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
|
|
9502
|
+
);
|
|
9503
|
+
}
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9506
|
+
let results;
|
|
9507
|
+
if (Array.isArray(scoreValue)) {
|
|
9508
|
+
results = scoreValue;
|
|
9509
|
+
} else if (typeof scoreValue === "object" && !isEmpty(scoreValue)) {
|
|
9510
|
+
results = [scoreValue];
|
|
9511
|
+
} else {
|
|
9512
|
+
results = [{ name, score: scoreValue }];
|
|
9513
|
+
}
|
|
9514
|
+
const { resultMetadata, scoresRecord } = buildSpanScores(results);
|
|
9515
|
+
const fields = (score) => {
|
|
9516
|
+
const { metadata: _metadata, name: _name, ...rest } = score;
|
|
9517
|
+
return rest;
|
|
9518
|
+
};
|
|
9519
|
+
return {
|
|
9520
|
+
results,
|
|
9521
|
+
output: results.length === 1 ? fields(results[0]) : Object.fromEntries(
|
|
9522
|
+
results.map((score) => [score.name ?? name, fields(score)])
|
|
9523
|
+
),
|
|
9524
|
+
metadata: resultMetadata,
|
|
9525
|
+
scores: scoresRecord
|
|
9526
|
+
};
|
|
9527
|
+
}
|
|
9528
|
+
async function runInScorerSpan(rootSpan, spanName, spanType, propagatedEvent, eventInput, fn) {
|
|
9529
|
+
try {
|
|
9530
|
+
const value = await rootSpan.traced(fn, {
|
|
9531
|
+
name: spanName,
|
|
9532
|
+
spanAttributes: { type: spanType, purpose: "scorer" },
|
|
9533
|
+
propagatedEvent,
|
|
9534
|
+
event: { input: eventInput }
|
|
9535
|
+
});
|
|
9536
|
+
return { kind: "score", value };
|
|
9537
|
+
} catch (e) {
|
|
9538
|
+
return { kind: "error", value: e };
|
|
8859
9539
|
}
|
|
8860
9540
|
}
|
|
8861
9541
|
function collectScoringResults(runResults, names, onResult) {
|
|
@@ -8889,6 +9569,27 @@ function toClassificationItem(c) {
|
|
|
8889
9569
|
...c.metadata !== void 0 ? { metadata: c.metadata } : {}
|
|
8890
9570
|
};
|
|
8891
9571
|
}
|
|
9572
|
+
function _internalPrepareEvaluatorClassification(value, name) {
|
|
9573
|
+
if (value === null) return { results: null };
|
|
9574
|
+
const results = (Array.isArray(value) ? value : [value]).map(
|
|
9575
|
+
(result) => validateClassificationResult(result, name)
|
|
9576
|
+
);
|
|
9577
|
+
const classifications = /* @__PURE__ */ Object.create(null);
|
|
9578
|
+
for (const result of results) {
|
|
9579
|
+
(classifications[result.name] ??= []).push(toClassificationItem(result));
|
|
9580
|
+
}
|
|
9581
|
+
return {
|
|
9582
|
+
results,
|
|
9583
|
+
output: results.length === 1 ? toClassificationItem(results[0]) : Object.fromEntries(
|
|
9584
|
+
results.map((result) => [
|
|
9585
|
+
result.name,
|
|
9586
|
+
toClassificationItem(result)
|
|
9587
|
+
])
|
|
9588
|
+
),
|
|
9589
|
+
metadata: buildSpanMetadata(results),
|
|
9590
|
+
classifications
|
|
9591
|
+
};
|
|
9592
|
+
}
|
|
8892
9593
|
function logScoringFailures(kind, failures, metadata, rootSpan, state) {
|
|
8893
9594
|
if (!failures.length) return [];
|
|
8894
9595
|
const errorMap = Object.fromEntries(
|
|
@@ -8936,54 +9637,14 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
8936
9637
|
(evaluator.state ?? _internalGetGlobalState())?.spanCache?.start();
|
|
8937
9638
|
}
|
|
8938
9639
|
try {
|
|
8939
|
-
if (typeof evaluator.data === "string") {
|
|
8940
|
-
throw new Error("Unimplemented: string data paths");
|
|
8941
|
-
}
|
|
8942
|
-
let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
|
|
8943
9640
|
parameters = await validateParameters(
|
|
8944
9641
|
parameters ?? {},
|
|
8945
9642
|
evaluator.parameters
|
|
8946
9643
|
);
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
if (!experiment) {
|
|
8952
|
-
throw new Error(
|
|
8953
|
-
"Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
|
|
8954
|
-
);
|
|
8955
|
-
}
|
|
8956
|
-
let name = dataResult.name;
|
|
8957
|
-
if (isEmpty(name)) {
|
|
8958
|
-
const baseExperiment = await experiment.fetchBaseExperiment();
|
|
8959
|
-
if (!baseExperiment) {
|
|
8960
|
-
throw new Error("BaseExperiment() failed to fetch base experiment");
|
|
8961
|
-
}
|
|
8962
|
-
name = baseExperiment.name;
|
|
8963
|
-
}
|
|
8964
|
-
dataResult = initExperiment2(evaluator.state, {
|
|
8965
|
-
...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
|
|
8966
|
-
experiment: name,
|
|
8967
|
-
open: true
|
|
8968
|
-
}).asDataset();
|
|
8969
|
-
}
|
|
8970
|
-
const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
|
|
8971
|
-
const dataIterable = (() => {
|
|
8972
|
-
if (isAsyncIterable2(resolvedDataResult)) {
|
|
8973
|
-
return resolvedDataResult;
|
|
8974
|
-
}
|
|
8975
|
-
if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
|
|
8976
|
-
const iterable = resolvedDataResult;
|
|
8977
|
-
return (async function* () {
|
|
8978
|
-
for (const datum of iterable) {
|
|
8979
|
-
yield datum;
|
|
8980
|
-
}
|
|
8981
|
-
})();
|
|
8982
|
-
}
|
|
8983
|
-
throw new Error(
|
|
8984
|
-
"Evaluator data must be an array, iterable, or async iterable"
|
|
8985
|
-
);
|
|
8986
|
-
})();
|
|
9644
|
+
const dataIterable = await _internalResolveEvaluatorData(
|
|
9645
|
+
evaluator,
|
|
9646
|
+
experiment
|
|
9647
|
+
);
|
|
8987
9648
|
progressReporter.start(evaluator.evalName, 0);
|
|
8988
9649
|
const experimentIdPromise = experiment ? (async () => {
|
|
8989
9650
|
try {
|
|
@@ -9052,57 +9713,45 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
9052
9713
|
ensureSpansFlushed,
|
|
9053
9714
|
state
|
|
9054
9715
|
}) : void 0;
|
|
9055
|
-
let metadata = {
|
|
9056
|
-
..."metadata" in datum ? datum.metadata : {}
|
|
9057
|
-
};
|
|
9716
|
+
let metadata = {};
|
|
9058
9717
|
const expected = "expected" in datum ? datum.expected : void 0;
|
|
9059
9718
|
let output = void 0;
|
|
9060
9719
|
let error = void 0;
|
|
9061
|
-
let tags = [
|
|
9062
|
-
const scores =
|
|
9063
|
-
const classifications =
|
|
9720
|
+
let tags = [];
|
|
9721
|
+
const scores = /* @__PURE__ */ Object.create(null);
|
|
9722
|
+
const classifications = /* @__PURE__ */ Object.create(null);
|
|
9064
9723
|
const scorerNames = (evaluator.scores ?? []).map(scorerName);
|
|
9065
9724
|
const classifierNames = (evaluator.classifiers ?? []).map(
|
|
9066
9725
|
classifierName
|
|
9067
9726
|
);
|
|
9068
9727
|
let unhandledScores = scorerNames;
|
|
9069
9728
|
try {
|
|
9070
|
-
const
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
object_type: "task"
|
|
9086
|
-
});
|
|
9087
|
-
},
|
|
9088
|
-
trialIndex,
|
|
9089
|
-
tags
|
|
9090
|
-
};
|
|
9091
|
-
const outputResult = evaluator.task(datum.input, hooksForTask);
|
|
9092
|
-
if (outputResult instanceof Promise) {
|
|
9093
|
-
output = await outputResult;
|
|
9094
|
-
} else {
|
|
9095
|
-
output = outputResult;
|
|
9729
|
+
const taskResult = await rootSpan.traced(
|
|
9730
|
+
(span) => _internalRunEvaluatorTask(
|
|
9731
|
+
evaluator.task,
|
|
9732
|
+
datum,
|
|
9733
|
+
trialIndex,
|
|
9734
|
+
parameters ?? {},
|
|
9735
|
+
span,
|
|
9736
|
+
(event) => {
|
|
9737
|
+
stream?.({
|
|
9738
|
+
...event,
|
|
9739
|
+
id: rootSpan.id,
|
|
9740
|
+
origin: baseEvent.event?.origin,
|
|
9741
|
+
name: evaluator.evalName,
|
|
9742
|
+
object_type: "task"
|
|
9743
|
+
});
|
|
9096
9744
|
}
|
|
9097
|
-
|
|
9098
|
-
span.log({ output });
|
|
9099
|
-
},
|
|
9745
|
+
),
|
|
9100
9746
|
{
|
|
9101
9747
|
name: "task",
|
|
9102
9748
|
spanAttributes: { type: "task" /* TASK */ },
|
|
9103
9749
|
event: { input: datum.input }
|
|
9104
9750
|
}
|
|
9105
9751
|
);
|
|
9752
|
+
output = taskResult.output;
|
|
9753
|
+
metadata = taskResult.metadata;
|
|
9754
|
+
tags = taskResult.tags;
|
|
9106
9755
|
if (tags.length) {
|
|
9107
9756
|
rootSpan.log({ output, metadata, expected, tags });
|
|
9108
9757
|
} else {
|
|
@@ -9112,20 +9761,18 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
9112
9761
|
await rootSpan.flush();
|
|
9113
9762
|
}
|
|
9114
9763
|
const scoringArgs = {
|
|
9764
|
+
id: datum.id,
|
|
9115
9765
|
input: datum.input,
|
|
9116
9766
|
expected: "expected" in datum ? datum.expected : void 0,
|
|
9117
9767
|
metadata,
|
|
9118
9768
|
output,
|
|
9769
|
+
tags,
|
|
9119
9770
|
trace
|
|
9120
9771
|
};
|
|
9121
9772
|
const { trace: _trace, ...scoringArgsForLogging } = scoringArgs;
|
|
9122
9773
|
const propagatedEvent = makeScorerPropagatedEvent(
|
|
9123
9774
|
await rootSpan.export()
|
|
9124
9775
|
);
|
|
9125
|
-
const getOtherFields = (s) => {
|
|
9126
|
-
const { metadata: _metadata, name: _name, ...rest } = s;
|
|
9127
|
-
return rest;
|
|
9128
|
-
};
|
|
9129
9776
|
const [scoreResults, classificationResults] = await Promise.all([
|
|
9130
9777
|
Promise.all(
|
|
9131
9778
|
(evaluator.scores ?? []).map(
|
|
@@ -9139,35 +9786,17 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
9139
9786
|
const scoreValue = await Promise.resolve(
|
|
9140
9787
|
score(scoringArgs)
|
|
9141
9788
|
);
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
if (!(typeof s === "object" && !isEmpty(s))) {
|
|
9146
|
-
throw new Error(
|
|
9147
|
-
`When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(s)}`
|
|
9148
|
-
);
|
|
9149
|
-
}
|
|
9150
|
-
}
|
|
9151
|
-
}
|
|
9152
|
-
const results = Array.isArray(scoreValue) ? scoreValue : typeof scoreValue === "object" && !isEmpty(scoreValue) ? [scoreValue] : [
|
|
9153
|
-
{
|
|
9154
|
-
name: scorerNames[score_idx],
|
|
9155
|
-
score: scoreValue
|
|
9156
|
-
}
|
|
9157
|
-
];
|
|
9158
|
-
const { resultMetadata, scoresRecord } = buildSpanScores(results);
|
|
9159
|
-
const resultOutput = results.length === 1 ? getOtherFields(results[0]) : results.reduce(
|
|
9160
|
-
(prev, s) => mergeDicts(prev, {
|
|
9161
|
-
[s.name]: getOtherFields(s)
|
|
9162
|
-
}),
|
|
9163
|
-
{}
|
|
9789
|
+
const prepared = _internalPrepareEvaluatorScore(
|
|
9790
|
+
scoreValue,
|
|
9791
|
+
scorerNames[score_idx]
|
|
9164
9792
|
);
|
|
9793
|
+
if (prepared.results === null) return null;
|
|
9165
9794
|
span.log({
|
|
9166
|
-
output:
|
|
9167
|
-
metadata:
|
|
9168
|
-
scores:
|
|
9795
|
+
output: prepared.output,
|
|
9796
|
+
metadata: prepared.metadata,
|
|
9797
|
+
scores: prepared.scores
|
|
9169
9798
|
});
|
|
9170
|
-
return results;
|
|
9799
|
+
return prepared.results;
|
|
9171
9800
|
}
|
|
9172
9801
|
)
|
|
9173
9802
|
)
|
|
@@ -9184,24 +9813,16 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
9184
9813
|
const classifierValue = await Promise.resolve(
|
|
9185
9814
|
classifier(scoringArgs)
|
|
9186
9815
|
);
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
result,
|
|
9191
|
-
classifierNames[idx]
|
|
9192
|
-
)
|
|
9193
|
-
);
|
|
9194
|
-
const resultOutput = rawResults.length === 1 ? toClassificationItem(rawResults[0]) : rawResults.reduce(
|
|
9195
|
-
(prev, r) => mergeDicts(prev, {
|
|
9196
|
-
[r.name]: toClassificationItem(r)
|
|
9197
|
-
}),
|
|
9198
|
-
{}
|
|
9816
|
+
const prepared = _internalPrepareEvaluatorClassification(
|
|
9817
|
+
classifierValue,
|
|
9818
|
+
classifierNames[idx]
|
|
9199
9819
|
);
|
|
9820
|
+
if (prepared.results === null) return null;
|
|
9200
9821
|
span.log({
|
|
9201
|
-
output:
|
|
9202
|
-
metadata:
|
|
9822
|
+
output: prepared.output,
|
|
9823
|
+
metadata: prepared.metadata
|
|
9203
9824
|
});
|
|
9204
|
-
return
|
|
9825
|
+
return prepared.results;
|
|
9205
9826
|
}
|
|
9206
9827
|
)
|
|
9207
9828
|
)
|
|
@@ -9430,7 +10051,7 @@ function accumulateScores(accumulator, scores) {
|
|
|
9430
10051
|
}
|
|
9431
10052
|
}
|
|
9432
10053
|
function ensureScoreAccumulator(results) {
|
|
9433
|
-
const accumulator =
|
|
10054
|
+
const accumulator = /* @__PURE__ */ Object.create(null);
|
|
9434
10055
|
for (const result of results) {
|
|
9435
10056
|
accumulateScores(accumulator, result.scores);
|
|
9436
10057
|
}
|
|
@@ -9573,6 +10194,1156 @@ var defaultReporter = {
|
|
|
9573
10194
|
}
|
|
9574
10195
|
};
|
|
9575
10196
|
|
|
10197
|
+
// src/durable-eval.ts
|
|
10198
|
+
var encoder = new TextEncoder();
|
|
10199
|
+
var decoder = new TextDecoder();
|
|
10200
|
+
var BATCH_TASK_KIND = "braintrust.durable.batch-task";
|
|
10201
|
+
var BATCH_SCORER_KIND = "braintrust.durable.batch-scorer";
|
|
10202
|
+
var DEFAULT_BATCH_SIZE = 1e3;
|
|
10203
|
+
var DurableEvalMemoryStore = class {
|
|
10204
|
+
values = /* @__PURE__ */ new Map();
|
|
10205
|
+
async read(key) {
|
|
10206
|
+
return this.values.get(key)?.slice();
|
|
10207
|
+
}
|
|
10208
|
+
async write(key, value) {
|
|
10209
|
+
this.values.set(key, value.slice());
|
|
10210
|
+
}
|
|
10211
|
+
async getOrSet(key, value) {
|
|
10212
|
+
const existing = this.values.get(key);
|
|
10213
|
+
if (existing) return { value: existing.slice(), created: false };
|
|
10214
|
+
this.values.set(key, value.slice());
|
|
10215
|
+
return { value: value.slice(), created: true };
|
|
10216
|
+
}
|
|
10217
|
+
};
|
|
10218
|
+
var DurableEvalRedisStore = class {
|
|
10219
|
+
client;
|
|
10220
|
+
keyPrefix;
|
|
10221
|
+
ttlMs;
|
|
10222
|
+
constructor(options) {
|
|
10223
|
+
this.client = options.client;
|
|
10224
|
+
this.keyPrefix = options.keyPrefix ?? "braintrust-eval:";
|
|
10225
|
+
this.ttlMs = options.ttlMs ?? 1e3 * 60 * 60 * 24 * 7;
|
|
10226
|
+
if (!Number.isInteger(this.ttlMs) || this.ttlMs < 1) {
|
|
10227
|
+
throw new Error("DurableEvalRedisStore ttlMs must be a positive integer");
|
|
10228
|
+
}
|
|
10229
|
+
}
|
|
10230
|
+
async read(key) {
|
|
10231
|
+
const value = await this.client.get(`${this.keyPrefix}${key}`);
|
|
10232
|
+
if (value == null) return void 0;
|
|
10233
|
+
if (typeof value !== "string") {
|
|
10234
|
+
throw new Error("DurableEvalRedisStore expected GET to return a string");
|
|
10235
|
+
}
|
|
10236
|
+
return base64ToUint8Array(value);
|
|
10237
|
+
}
|
|
10238
|
+
async write(key, value) {
|
|
10239
|
+
const client = this.client;
|
|
10240
|
+
const set = client.set;
|
|
10241
|
+
const redisKey = `${this.keyPrefix}${key}`;
|
|
10242
|
+
const encoded = uint8ArrayToBase64(value);
|
|
10243
|
+
if (typeof client.defineCommand === "function") {
|
|
10244
|
+
await set.call(client, redisKey, encoded, "PX", this.ttlMs);
|
|
10245
|
+
} else if (typeof client.sendCommand === "function") {
|
|
10246
|
+
await set.call(client, redisKey, encoded, { PX: this.ttlMs });
|
|
10247
|
+
} else if (typeof client.createScript === "function") {
|
|
10248
|
+
await set.call(client, redisKey, encoded, { px: this.ttlMs });
|
|
10249
|
+
} else {
|
|
10250
|
+
throw new Error(
|
|
10251
|
+
"DurableEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
|
|
10252
|
+
);
|
|
10253
|
+
}
|
|
10254
|
+
}
|
|
10255
|
+
async getOrSet(key, value) {
|
|
10256
|
+
const redisKey = `${this.keyPrefix}${key}`;
|
|
10257
|
+
const encoded = uint8ArrayToBase64(value);
|
|
10258
|
+
const client = this.client;
|
|
10259
|
+
const set = client.set;
|
|
10260
|
+
let setOptions;
|
|
10261
|
+
if (typeof client.defineCommand === "function") {
|
|
10262
|
+
setOptions = ["PX", this.ttlMs, "NX", "GET"];
|
|
10263
|
+
} else if (typeof client.sendCommand === "function") {
|
|
10264
|
+
setOptions = [{ PX: this.ttlMs, NX: true, GET: true }];
|
|
10265
|
+
} else if (typeof client.createScript === "function") {
|
|
10266
|
+
setOptions = [{ px: this.ttlMs, nx: true, get: true }];
|
|
10267
|
+
} else {
|
|
10268
|
+
throw new Error(
|
|
10269
|
+
"DurableEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
|
|
10270
|
+
);
|
|
10271
|
+
}
|
|
10272
|
+
const existing = await set.call(client, redisKey, encoded, ...setOptions);
|
|
10273
|
+
if (existing === null) return { value: value.slice(), created: true };
|
|
10274
|
+
if (typeof existing !== "string") {
|
|
10275
|
+
throw new Error(
|
|
10276
|
+
"DurableEvalRedisStore expected atomic SET to return a string or null"
|
|
10277
|
+
);
|
|
10278
|
+
}
|
|
10279
|
+
return { value: base64ToUint8Array(existing), created: false };
|
|
10280
|
+
}
|
|
10281
|
+
};
|
|
10282
|
+
var BatchTask = class {
|
|
10283
|
+
constructor(processor) {
|
|
10284
|
+
this.processor = processor;
|
|
10285
|
+
}
|
|
10286
|
+
processor;
|
|
10287
|
+
kind = BATCH_TASK_KIND;
|
|
10288
|
+
};
|
|
10289
|
+
var BatchScorer = class {
|
|
10290
|
+
constructor(processor) {
|
|
10291
|
+
this.processor = processor;
|
|
10292
|
+
this.name = processor.name;
|
|
10293
|
+
}
|
|
10294
|
+
processor;
|
|
10295
|
+
kind = BATCH_SCORER_KIND;
|
|
10296
|
+
name;
|
|
10297
|
+
};
|
|
10298
|
+
function defineDurableEval(projectName, evaluator) {
|
|
10299
|
+
const definition = {
|
|
10300
|
+
projectName,
|
|
10301
|
+
evalName: evaluator.experimentName ?? projectName,
|
|
10302
|
+
evaluator
|
|
10303
|
+
};
|
|
10304
|
+
return {
|
|
10305
|
+
start: (options = {}) => startDurableEval(definition, options),
|
|
10306
|
+
status: (options) => getDurableEvalStatus(definition, options),
|
|
10307
|
+
poll: (options) => pollDurableEval(definition, options),
|
|
10308
|
+
processBatchResult: (result) => processDurableBatchResult(definition, result)
|
|
10309
|
+
};
|
|
10310
|
+
}
|
|
10311
|
+
async function startDurableEval(definition, options) {
|
|
10312
|
+
const store = definition.evaluator.store;
|
|
10313
|
+
const runId = newId();
|
|
10314
|
+
const key = runKey(definition.projectName, definition.evalName, runId);
|
|
10315
|
+
const { data } = callEvaluatorData(definition.evaluator.data);
|
|
10316
|
+
const parameters = await validateParameters(
|
|
10317
|
+
options.parameters ?? {},
|
|
10318
|
+
definition.evaluator.parameters
|
|
10319
|
+
);
|
|
10320
|
+
const experimentName = definition.evaluator.experimentName ?? `${definition.evalName}-${runId}`;
|
|
10321
|
+
const experiment = await _internalInitEvaluatorExperiment(
|
|
10322
|
+
definition.projectName,
|
|
10323
|
+
{ ...definition.evaluator, data },
|
|
10324
|
+
data,
|
|
10325
|
+
{
|
|
10326
|
+
disabled: options.noSendLogs ?? false,
|
|
10327
|
+
experimentName,
|
|
10328
|
+
update: true
|
|
10329
|
+
}
|
|
10330
|
+
);
|
|
10331
|
+
if (!isBatchTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isBatchScorer)) {
|
|
10332
|
+
const result = await runEvaluator(
|
|
10333
|
+
experiment,
|
|
10334
|
+
{
|
|
10335
|
+
...definition.evaluator,
|
|
10336
|
+
projectName: definition.projectName,
|
|
10337
|
+
evalName: definition.evalName,
|
|
10338
|
+
data
|
|
10339
|
+
},
|
|
10340
|
+
{
|
|
10341
|
+
start: () => void 0,
|
|
10342
|
+
stop: () => void 0,
|
|
10343
|
+
increment: () => void 0
|
|
10344
|
+
},
|
|
10345
|
+
[],
|
|
10346
|
+
void 0,
|
|
10347
|
+
parameters,
|
|
10348
|
+
true,
|
|
10349
|
+
true
|
|
10350
|
+
);
|
|
10351
|
+
const state2 = {
|
|
10352
|
+
runId,
|
|
10353
|
+
experimentName,
|
|
10354
|
+
noSendLogs: options.noSendLogs ?? false,
|
|
10355
|
+
parameters: assertJsonValue(parameters, "eval parameters"),
|
|
10356
|
+
status: "completed",
|
|
10357
|
+
summary: result.summary,
|
|
10358
|
+
cases: [],
|
|
10359
|
+
batches: []
|
|
10360
|
+
};
|
|
10361
|
+
await experiment?.flush();
|
|
10362
|
+
await writeRunRecord(store, key, state2);
|
|
10363
|
+
return currentStatus(definition, state2);
|
|
10364
|
+
}
|
|
10365
|
+
const state = {
|
|
10366
|
+
runId,
|
|
10367
|
+
experimentName,
|
|
10368
|
+
noSendLogs: options.noSendLogs ?? false,
|
|
10369
|
+
parameters: assertJsonValue(parameters, "eval parameters"),
|
|
10370
|
+
status: "running",
|
|
10371
|
+
cases: await materializeCases(definition, data, experiment),
|
|
10372
|
+
batches: []
|
|
10373
|
+
};
|
|
10374
|
+
await writeCaseBaseRecords(store, key, state.cases);
|
|
10375
|
+
await writeRunRecord(store, key, state);
|
|
10376
|
+
return advanceDurableEval(definition, state, store, key, experiment);
|
|
10377
|
+
}
|
|
10378
|
+
async function getDurableEvalStatus(definition, options) {
|
|
10379
|
+
const store = definition.evaluator.store;
|
|
10380
|
+
const state = await readRunState(
|
|
10381
|
+
definition,
|
|
10382
|
+
store,
|
|
10383
|
+
runKey(definition.projectName, definition.evalName, options.runId)
|
|
10384
|
+
);
|
|
10385
|
+
if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
|
|
10386
|
+
return currentStatus(definition, state);
|
|
10387
|
+
}
|
|
10388
|
+
async function processDurableBatchResult(definition, result) {
|
|
10389
|
+
if (!result.batchId && !result.externalId) {
|
|
10390
|
+
throw new Error("Batch results require batchId or externalId");
|
|
10391
|
+
}
|
|
10392
|
+
const store = definition.evaluator.store;
|
|
10393
|
+
const key = runKey(definition.projectName, definition.evalName, result.runId);
|
|
10394
|
+
const state = await readRunState(definition, store, key);
|
|
10395
|
+
if (!state) throw new Error(`Durable eval run ${result.runId} is missing`);
|
|
10396
|
+
const byBatch = result.batchId ? state.batches.find((candidate) => candidate.id === result.batchId) : void 0;
|
|
10397
|
+
const byExternal = result.externalId ? state.batches.find(
|
|
10398
|
+
(candidate) => candidate.externalId === result.externalId
|
|
10399
|
+
) : void 0;
|
|
10400
|
+
if (byBatch && byExternal && byBatch.id !== byExternal.id) {
|
|
10401
|
+
throw new Error("batchId and externalId identify different batches");
|
|
10402
|
+
}
|
|
10403
|
+
const batch = byBatch ?? byExternal;
|
|
10404
|
+
if (!batch) throw new Error("No submitted batch matches this result");
|
|
10405
|
+
if (batch.status !== "complete") {
|
|
10406
|
+
const records = await collectBatch(definition, state, batch);
|
|
10407
|
+
batch.status = "complete";
|
|
10408
|
+
await writeCaseRecords(store, key, records);
|
|
10409
|
+
await writeBatchRecords(store, key, [batch]);
|
|
10410
|
+
}
|
|
10411
|
+
return advanceDurableEval(
|
|
10412
|
+
definition,
|
|
10413
|
+
await readRunState(definition, store, key),
|
|
10414
|
+
store,
|
|
10415
|
+
key
|
|
10416
|
+
);
|
|
10417
|
+
}
|
|
10418
|
+
async function pollDurableEval(definition, options) {
|
|
10419
|
+
const store = definition.evaluator.store;
|
|
10420
|
+
const key = runKey(
|
|
10421
|
+
definition.projectName,
|
|
10422
|
+
definition.evalName,
|
|
10423
|
+
options.runId
|
|
10424
|
+
);
|
|
10425
|
+
const state = await readRunState(definition, store, key);
|
|
10426
|
+
if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
|
|
10427
|
+
const batches = state.batches.filter((batch) => {
|
|
10428
|
+
if (batch.status === "complete") return false;
|
|
10429
|
+
return processorForStage(definition, batch.kind, batch.scorerName).completion.mode === "poll";
|
|
10430
|
+
});
|
|
10431
|
+
const results = await Promise.all(
|
|
10432
|
+
batches.map(async (batch) => ({
|
|
10433
|
+
batch,
|
|
10434
|
+
result: await processorForStage(definition, batch.kind, batch.scorerName).completion.poll(batch.submissionData, {
|
|
10435
|
+
runId: state.runId,
|
|
10436
|
+
batchId: batch.id
|
|
10437
|
+
})
|
|
10438
|
+
}))
|
|
10439
|
+
);
|
|
10440
|
+
const changedCases = /* @__PURE__ */ new Map();
|
|
10441
|
+
const changedBatches = [];
|
|
10442
|
+
for (const { batch, result } of results) {
|
|
10443
|
+
if (result.status === "failed") throw asError(result.error);
|
|
10444
|
+
if (result.status !== "complete") continue;
|
|
10445
|
+
for (const record of await collectBatch(definition, state, batch)) {
|
|
10446
|
+
changedCases.set(record.id, record);
|
|
10447
|
+
}
|
|
10448
|
+
batch.status = "complete";
|
|
10449
|
+
changedBatches.push(batch);
|
|
10450
|
+
}
|
|
10451
|
+
if (changedBatches.length > 0) {
|
|
10452
|
+
await writeCaseRecords(store, key, [...changedCases.values()]);
|
|
10453
|
+
await writeBatchRecords(store, key, changedBatches);
|
|
10454
|
+
}
|
|
10455
|
+
const currentState = changedBatches.length > 0 ? await readRunState(definition, store, key) : state;
|
|
10456
|
+
return advanceDurableEval(definition, currentState, store, key);
|
|
10457
|
+
}
|
|
10458
|
+
async function openDurableExperiment(definition, state) {
|
|
10459
|
+
const data = [];
|
|
10460
|
+
return await _internalInitEvaluatorExperiment(
|
|
10461
|
+
definition.projectName,
|
|
10462
|
+
{ ...definition.evaluator, data },
|
|
10463
|
+
data,
|
|
10464
|
+
{
|
|
10465
|
+
disabled: state.noSendLogs,
|
|
10466
|
+
experimentName: state.experimentName,
|
|
10467
|
+
update: true
|
|
10468
|
+
}
|
|
10469
|
+
);
|
|
10470
|
+
}
|
|
10471
|
+
async function advanceDurableEval(definition, state, store, key, existingExperiment) {
|
|
10472
|
+
if (state.status === "completed") return currentStatus(definition, state);
|
|
10473
|
+
const experiment = existingExperiment === void 0 ? await openDurableExperiment(definition, state) : existingExperiment;
|
|
10474
|
+
await runTaskStage(definition, state, store, key, experiment);
|
|
10475
|
+
await logCompletedTasks(definition, state, store, key, experiment);
|
|
10476
|
+
state = await readRunState(definition, store, key) ?? state;
|
|
10477
|
+
if (state.cases.some((record) => !record.taskComplete || !record.taskLogged)) {
|
|
10478
|
+
return currentStatus(definition, state);
|
|
10479
|
+
}
|
|
10480
|
+
await runScoreStages(definition, state, store, key, experiment);
|
|
10481
|
+
state = await readRunState(definition, store, key) ?? state;
|
|
10482
|
+
const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
|
|
10483
|
+
({ name }) => name
|
|
10484
|
+
);
|
|
10485
|
+
const classifierNames = (definition.evaluator.classifiers ?? []).map(
|
|
10486
|
+
classifierName
|
|
10487
|
+
);
|
|
10488
|
+
if (state.cases.some(
|
|
10489
|
+
(record) => scorerNames.some(
|
|
10490
|
+
(name) => !Object.hasOwn(record.scores, name) || !Object.hasOwn(record.loggedScores, name)
|
|
10491
|
+
) || classifierNames.some(
|
|
10492
|
+
(name) => !Object.hasOwn(record.loggedClassifications, name)
|
|
10493
|
+
)
|
|
10494
|
+
)) {
|
|
10495
|
+
return currentStatus(definition, state);
|
|
10496
|
+
}
|
|
10497
|
+
if (!await claimAction(store, key, "finish")) {
|
|
10498
|
+
const latest = await readRunState(definition, store, key);
|
|
10499
|
+
return currentStatus(definition, latest ?? state);
|
|
10500
|
+
}
|
|
10501
|
+
state.summary = await finishExperiment(definition, state, experiment);
|
|
10502
|
+
state.status = "completed";
|
|
10503
|
+
await writeRunRecord(store, key, state);
|
|
10504
|
+
return currentStatus(definition, state);
|
|
10505
|
+
}
|
|
10506
|
+
function currentStatus(definition, state) {
|
|
10507
|
+
if (state.status === "completed") {
|
|
10508
|
+
if (!state.summary) {
|
|
10509
|
+
throw new Error(`Durable eval run ${state.runId} has no saved summary`);
|
|
10510
|
+
}
|
|
10511
|
+
return {
|
|
10512
|
+
status: "completed",
|
|
10513
|
+
runId: state.runId,
|
|
10514
|
+
pending: { poll: 0, webhook: 0 },
|
|
10515
|
+
summary: state.summary
|
|
10516
|
+
};
|
|
10517
|
+
}
|
|
10518
|
+
const pending = { poll: 0, webhook: 0 };
|
|
10519
|
+
for (const batch of state.batches) {
|
|
10520
|
+
if (batch.status === "complete") continue;
|
|
10521
|
+
pending[processorForStage(definition, batch.kind, batch.scorerName).completion.mode]++;
|
|
10522
|
+
}
|
|
10523
|
+
return { status: "waiting", runId: state.runId, pending };
|
|
10524
|
+
}
|
|
10525
|
+
async function startCaseRoot(definition, state, record, experiment) {
|
|
10526
|
+
if (!experiment) return NOOP_SPAN;
|
|
10527
|
+
const datum = record.datum;
|
|
10528
|
+
return _internalStartSpanWithInitialMerge({
|
|
10529
|
+
...definition.evaluator.state ? { state: definition.evaluator.state } : {},
|
|
10530
|
+
parent: await experiment.export(),
|
|
10531
|
+
name: "eval",
|
|
10532
|
+
spanId: deterministicId(`${state.runId}:${record.id}:span`),
|
|
10533
|
+
spanAttributes: { type: "eval" /* EVAL */ },
|
|
10534
|
+
event: {
|
|
10535
|
+
id: deterministicId(`${state.runId}:${record.id}:row`),
|
|
10536
|
+
input: datum.input,
|
|
10537
|
+
expected: "expected" in datum ? datum.expected : void 0,
|
|
10538
|
+
tags: datum.tags,
|
|
10539
|
+
origin: datum.origin
|
|
10540
|
+
}
|
|
10541
|
+
});
|
|
10542
|
+
}
|
|
10543
|
+
async function logTaskResult(definition, state, record, experiment, task) {
|
|
10544
|
+
const datum = record.datum;
|
|
10545
|
+
const root = await startCaseRoot(definition, state, record, experiment);
|
|
10546
|
+
try {
|
|
10547
|
+
if (task) {
|
|
10548
|
+
const result = await root.traced(
|
|
10549
|
+
(span) => _internalRunEvaluatorTask(
|
|
10550
|
+
task,
|
|
10551
|
+
datum,
|
|
10552
|
+
record.trialIndex,
|
|
10553
|
+
state.parameters,
|
|
10554
|
+
span
|
|
10555
|
+
),
|
|
10556
|
+
{
|
|
10557
|
+
name: "task",
|
|
10558
|
+
spanId: deterministicId(`${state.runId}:${record.id}:task`),
|
|
10559
|
+
spanAttributes: { type: "task" /* TASK */ },
|
|
10560
|
+
event: { input: datum.input }
|
|
10561
|
+
}
|
|
10562
|
+
);
|
|
10563
|
+
record.output = assertJsonValue(
|
|
10564
|
+
result.output,
|
|
10565
|
+
`task output for ${record.caseId}`
|
|
10566
|
+
);
|
|
10567
|
+
record.metadata = assertJsonValue(result.metadata, "task metadata");
|
|
10568
|
+
record.tags = result.tags;
|
|
10569
|
+
record.taskComplete = true;
|
|
10570
|
+
} else {
|
|
10571
|
+
await root.traced((span) => span.log({ output: record.output }), {
|
|
10572
|
+
name: "task",
|
|
10573
|
+
spanId: deterministicId(`${state.runId}:${record.id}:task`),
|
|
10574
|
+
spanAttributes: { type: "task" /* TASK */ },
|
|
10575
|
+
event: { input: datum.input }
|
|
10576
|
+
});
|
|
10577
|
+
}
|
|
10578
|
+
root.log({
|
|
10579
|
+
output: record.output,
|
|
10580
|
+
expected: "expected" in datum ? datum.expected : void 0,
|
|
10581
|
+
metadata: {
|
|
10582
|
+
...record.metadata,
|
|
10583
|
+
durable_eval: {
|
|
10584
|
+
run_id: state.runId,
|
|
10585
|
+
case_id: record.caseId,
|
|
10586
|
+
trial_index: record.trialIndex
|
|
10587
|
+
}
|
|
10588
|
+
},
|
|
10589
|
+
tags: record.tags
|
|
10590
|
+
});
|
|
10591
|
+
record.rootSpan = await root.export();
|
|
10592
|
+
record.taskLogged = true;
|
|
10593
|
+
} catch (error) {
|
|
10594
|
+
logError(root, error);
|
|
10595
|
+
throw error;
|
|
10596
|
+
} finally {
|
|
10597
|
+
root.end();
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10600
|
+
async function logCompletedTasks(definition, state, store, key, experiment) {
|
|
10601
|
+
const changed = [];
|
|
10602
|
+
for (const record of state.cases) {
|
|
10603
|
+
if (!record.taskComplete || record.taskLogged) continue;
|
|
10604
|
+
if (!await claimAction(store, key, "task-log", record.id)) continue;
|
|
10605
|
+
await logTaskResult(definition, state, record, experiment);
|
|
10606
|
+
changed.push(record);
|
|
10607
|
+
}
|
|
10608
|
+
if (changed.length > 0) {
|
|
10609
|
+
await experiment?.flush();
|
|
10610
|
+
await writeCaseRecords(store, key, changed);
|
|
10611
|
+
}
|
|
10612
|
+
}
|
|
10613
|
+
async function runTaskStage(definition, state, store, key, experiment) {
|
|
10614
|
+
if (isBatchTask(definition.evaluator.task)) {
|
|
10615
|
+
await ensureBatches(definition, state, store, key, "task");
|
|
10616
|
+
return;
|
|
10617
|
+
}
|
|
10618
|
+
const task = definition.evaluator.task;
|
|
10619
|
+
const changed = [];
|
|
10620
|
+
for (const record of state.cases) {
|
|
10621
|
+
if (record.taskComplete) continue;
|
|
10622
|
+
if (!await claimAction(store, key, "task", record.id)) continue;
|
|
10623
|
+
await logTaskResult(definition, state, record, experiment, task);
|
|
10624
|
+
changed.push(record);
|
|
10625
|
+
}
|
|
10626
|
+
if (changed.length > 0) {
|
|
10627
|
+
await experiment?.flush();
|
|
10628
|
+
await writeCaseRecords(store, key, changed);
|
|
10629
|
+
}
|
|
10630
|
+
}
|
|
10631
|
+
async function runScoreStages(definition, state, store, key, experiment) {
|
|
10632
|
+
const scorers = resolveScorers(definition.evaluator.scores ?? []);
|
|
10633
|
+
const changed = /* @__PURE__ */ new Map();
|
|
10634
|
+
const persistChangedCases = async () => {
|
|
10635
|
+
if (changed.size === 0) return;
|
|
10636
|
+
await experiment?.flush();
|
|
10637
|
+
await writeCaseRecords(store, key, [...changed.values()]);
|
|
10638
|
+
changed.clear();
|
|
10639
|
+
};
|
|
10640
|
+
for (const { name, scorer } of scorers) {
|
|
10641
|
+
if (isBatchScorer(scorer)) {
|
|
10642
|
+
for (const record of state.cases) {
|
|
10643
|
+
if (Object.hasOwn(record.scores, name) && !Object.hasOwn(record.loggedScores, name)) {
|
|
10644
|
+
if (!await claimAction(store, key, "score-log", record.id, name)) {
|
|
10645
|
+
continue;
|
|
10646
|
+
}
|
|
10647
|
+
await evaluateAndLogScore(
|
|
10648
|
+
definition,
|
|
10649
|
+
state,
|
|
10650
|
+
record,
|
|
10651
|
+
name,
|
|
10652
|
+
experiment
|
|
10653
|
+
);
|
|
10654
|
+
changed.set(record.id, record);
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10657
|
+
await persistChangedCases();
|
|
10658
|
+
await ensureBatches(definition, state, store, key, "score", name);
|
|
10659
|
+
continue;
|
|
10660
|
+
}
|
|
10661
|
+
for (const record of state.cases) {
|
|
10662
|
+
if (Object.hasOwn(record.loggedScores, name)) continue;
|
|
10663
|
+
if (!await claimAction(store, key, "score", record.id, name)) continue;
|
|
10664
|
+
await evaluateAndLogScore(
|
|
10665
|
+
definition,
|
|
10666
|
+
state,
|
|
10667
|
+
record,
|
|
10668
|
+
name,
|
|
10669
|
+
experiment,
|
|
10670
|
+
scorer
|
|
10671
|
+
);
|
|
10672
|
+
changed.set(record.id, record);
|
|
10673
|
+
}
|
|
10674
|
+
}
|
|
10675
|
+
for (const [index, classifier] of (definition.evaluator.classifiers ?? []).entries()) {
|
|
10676
|
+
const name = classifierName(classifier, index);
|
|
10677
|
+
for (const record of state.cases) {
|
|
10678
|
+
if (Object.hasOwn(record.loggedClassifications, name)) continue;
|
|
10679
|
+
if (!await claimAction(store, key, "classification", record.id, name)) {
|
|
10680
|
+
continue;
|
|
10681
|
+
}
|
|
10682
|
+
await evaluateAndLogClassification(
|
|
10683
|
+
definition,
|
|
10684
|
+
state,
|
|
10685
|
+
record,
|
|
10686
|
+
name,
|
|
10687
|
+
classifier,
|
|
10688
|
+
experiment
|
|
10689
|
+
);
|
|
10690
|
+
changed.set(record.id, record);
|
|
10691
|
+
}
|
|
10692
|
+
}
|
|
10693
|
+
await persistChangedCases();
|
|
10694
|
+
}
|
|
10695
|
+
function scorerArgs(record) {
|
|
10696
|
+
const datum = record.datum;
|
|
10697
|
+
return {
|
|
10698
|
+
...datum,
|
|
10699
|
+
metadata: record.metadata,
|
|
10700
|
+
output: record.output
|
|
10701
|
+
};
|
|
10702
|
+
}
|
|
10703
|
+
function resumeCaseRoot(definition, record, experiment) {
|
|
10704
|
+
if (!experiment) return NOOP_SPAN;
|
|
10705
|
+
if (!record.rootSpan) {
|
|
10706
|
+
throw new Error(`Durable eval case ${record.caseId} has no root span`);
|
|
10707
|
+
}
|
|
10708
|
+
return _internalResumeSpan({
|
|
10709
|
+
exported: record.rootSpan,
|
|
10710
|
+
state: definition.evaluator.state
|
|
10711
|
+
});
|
|
10712
|
+
}
|
|
10713
|
+
async function evaluateAndLogScore(definition, state, record, name, experiment, scorer) {
|
|
10714
|
+
const root = resumeCaseRoot(definition, record, experiment);
|
|
10715
|
+
try {
|
|
10716
|
+
const rootExport = await root.export();
|
|
10717
|
+
const prepared = await root.traced(
|
|
10718
|
+
async (span) => {
|
|
10719
|
+
const value = scorer ? await scorer(scorerArgs(record)) : record.scores[name];
|
|
10720
|
+
if (scorer) {
|
|
10721
|
+
record.scores[name] = assertJsonValue(value, `scorer ${name} output`);
|
|
10722
|
+
}
|
|
10723
|
+
const result = _internalPrepareEvaluatorScore(value, name);
|
|
10724
|
+
if (result.results !== null) {
|
|
10725
|
+
span.log({
|
|
10726
|
+
output: result.output,
|
|
10727
|
+
metadata: result.metadata,
|
|
10728
|
+
scores: result.scores
|
|
10729
|
+
});
|
|
10730
|
+
}
|
|
10731
|
+
return result;
|
|
10732
|
+
},
|
|
10733
|
+
{
|
|
10734
|
+
name,
|
|
10735
|
+
spanId: deterministicId(`${state.runId}:${record.id}:score:${name}`),
|
|
10736
|
+
spanAttributes: {
|
|
10737
|
+
type: "score" /* SCORE */,
|
|
10738
|
+
purpose: "scorer"
|
|
10739
|
+
},
|
|
10740
|
+
propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
|
|
10741
|
+
event: { input: scorerArgs(record) }
|
|
10742
|
+
}
|
|
10743
|
+
);
|
|
10744
|
+
if (prepared.scores) root.log({ scores: prepared.scores });
|
|
10745
|
+
record.loggedScores[name] = true;
|
|
10746
|
+
} catch (error) {
|
|
10747
|
+
logError(root, error);
|
|
10748
|
+
throw error;
|
|
10749
|
+
} finally {
|
|
10750
|
+
root.end();
|
|
10751
|
+
}
|
|
10752
|
+
}
|
|
10753
|
+
async function evaluateAndLogClassification(definition, state, record, name, classifier, experiment) {
|
|
10754
|
+
const root = resumeCaseRoot(definition, record, experiment);
|
|
10755
|
+
try {
|
|
10756
|
+
const rootExport = await root.export();
|
|
10757
|
+
const prepared = await root.traced(
|
|
10758
|
+
async (span) => {
|
|
10759
|
+
const value = await classifier(scorerArgs(record));
|
|
10760
|
+
record.classifications[name] = assertJsonValue(
|
|
10761
|
+
value,
|
|
10762
|
+
`classifier ${name} output`
|
|
10763
|
+
);
|
|
10764
|
+
const result = _internalPrepareEvaluatorClassification(value, name);
|
|
10765
|
+
if (result.results !== null) {
|
|
10766
|
+
span.log({ output: result.output, metadata: result.metadata });
|
|
10767
|
+
}
|
|
10768
|
+
return result;
|
|
10769
|
+
},
|
|
10770
|
+
{
|
|
10771
|
+
name,
|
|
10772
|
+
spanId: deterministicId(
|
|
10773
|
+
`${state.runId}:${record.id}:classification:${name}`
|
|
10774
|
+
),
|
|
10775
|
+
spanAttributes: {
|
|
10776
|
+
type: "classifier" /* CLASSIFIER */,
|
|
10777
|
+
purpose: "scorer"
|
|
10778
|
+
},
|
|
10779
|
+
propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
|
|
10780
|
+
event: { input: scorerArgs(record) }
|
|
10781
|
+
}
|
|
10782
|
+
);
|
|
10783
|
+
if (prepared.classifications) {
|
|
10784
|
+
root.log({ classifications: prepared.classifications });
|
|
10785
|
+
}
|
|
10786
|
+
record.loggedClassifications[name] = true;
|
|
10787
|
+
} catch (error) {
|
|
10788
|
+
logError(root, error);
|
|
10789
|
+
throw error;
|
|
10790
|
+
} finally {
|
|
10791
|
+
root.end();
|
|
10792
|
+
}
|
|
10793
|
+
}
|
|
10794
|
+
async function ensureBatches(definition, state, store, key, kind, scorerName2) {
|
|
10795
|
+
const processor = processorForStage(definition, kind, scorerName2);
|
|
10796
|
+
const plans = plannedBatches(
|
|
10797
|
+
definition,
|
|
10798
|
+
state.runId,
|
|
10799
|
+
state.cases.map(({ id }) => id)
|
|
10800
|
+
);
|
|
10801
|
+
const casesById = new Map(state.cases.map((record) => [record.id, record]));
|
|
10802
|
+
for (const plan of plans) {
|
|
10803
|
+
if (plan.kind !== kind || plan.scorerName !== scorerName2) continue;
|
|
10804
|
+
if (state.batches.some(({ id }) => id === plan.id)) continue;
|
|
10805
|
+
const records = plan.itemIds.map((id) => casesById.get(id));
|
|
10806
|
+
const ready = records.every(
|
|
10807
|
+
(record) => kind === "task" ? !record.taskComplete : !Object.hasOwn(record.scores, scorerName2)
|
|
10808
|
+
);
|
|
10809
|
+
if (!ready) continue;
|
|
10810
|
+
const batchId = plan.id;
|
|
10811
|
+
const claim = await store.getOrSet(
|
|
10812
|
+
claimRecordKey(key, "batch", batchId),
|
|
10813
|
+
encoder.encode(batchId)
|
|
10814
|
+
);
|
|
10815
|
+
if (!claim.created) continue;
|
|
10816
|
+
const context = { runId: state.runId, batchId };
|
|
10817
|
+
const items = records.map(
|
|
10818
|
+
(record) => kind === "task" ? taskBatchItem(record, state.parameters) : scorerBatchItem(record)
|
|
10819
|
+
);
|
|
10820
|
+
const submissionData = assertJsonValue(
|
|
10821
|
+
await processor.submit(items, context),
|
|
10822
|
+
`submission data for batch ${batchId}`
|
|
10823
|
+
);
|
|
10824
|
+
const externalId = processor.completion.mode === "webhook" ? processor.completion.getExternalId(submissionData, context) : void 0;
|
|
10825
|
+
if (externalId !== void 0 && !externalId.trim()) {
|
|
10826
|
+
throw new Error(`Batch ${batchId} produced an empty externalId`);
|
|
10827
|
+
}
|
|
10828
|
+
const batch = {
|
|
10829
|
+
id: batchId,
|
|
10830
|
+
kind,
|
|
10831
|
+
scorerName: scorerName2,
|
|
10832
|
+
itemIds: records.map((record) => record.id),
|
|
10833
|
+
submissionData,
|
|
10834
|
+
externalId,
|
|
10835
|
+
status: "submitted"
|
|
10836
|
+
};
|
|
10837
|
+
state.batches.push(batch);
|
|
10838
|
+
await writeBatchRecords(store, key, [batch]);
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
async function collectBatch(definition, state, batch) {
|
|
10842
|
+
const processor = processorForStage(definition, batch.kind, batch.scorerName);
|
|
10843
|
+
const context = { runId: state.runId, batchId: batch.id };
|
|
10844
|
+
const results = await processor.collect(batch.submissionData, context);
|
|
10845
|
+
if (!Array.isArray(results)) {
|
|
10846
|
+
throw new Error(`collect for batch ${batch.id} must return an array`);
|
|
10847
|
+
}
|
|
10848
|
+
const expectedIds = new Set(batch.itemIds);
|
|
10849
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10850
|
+
const records = [];
|
|
10851
|
+
for (const result of results) {
|
|
10852
|
+
const id = resultItemId(result);
|
|
10853
|
+
if (!expectedIds.has(id)) {
|
|
10854
|
+
throw new Error(`Batch ${batch.id} returned unknown item ${id}`);
|
|
10855
|
+
}
|
|
10856
|
+
if (seen.has(id)) {
|
|
10857
|
+
throw new Error(`Batch ${batch.id} returned item ${id} more than once`);
|
|
10858
|
+
}
|
|
10859
|
+
seen.add(id);
|
|
10860
|
+
const record = state.cases.find((candidate) => candidate.id === id);
|
|
10861
|
+
records.push(record);
|
|
10862
|
+
if (batch.kind === "task") {
|
|
10863
|
+
record.output = assertJsonValue(
|
|
10864
|
+
result.output,
|
|
10865
|
+
`task output for item ${id}`
|
|
10866
|
+
);
|
|
10867
|
+
if ("metadata" in result && result.metadata !== void 0) {
|
|
10868
|
+
record.metadata = assertJsonValue(
|
|
10869
|
+
{
|
|
10870
|
+
...record.metadata,
|
|
10871
|
+
...result.metadata
|
|
10872
|
+
},
|
|
10873
|
+
`metadata for ${id}`
|
|
10874
|
+
);
|
|
10875
|
+
}
|
|
10876
|
+
if ("tags" in result && result.tags !== void 0)
|
|
10877
|
+
record.tags = result.tags;
|
|
10878
|
+
record.taskComplete = true;
|
|
10879
|
+
} else {
|
|
10880
|
+
record.scores[batch.scorerName] = assertJsonValue(
|
|
10881
|
+
result.score,
|
|
10882
|
+
`score output for item ${id}`
|
|
10883
|
+
);
|
|
10884
|
+
}
|
|
10885
|
+
}
|
|
10886
|
+
const missing = batch.itemIds.filter((id) => !seen.has(id));
|
|
10887
|
+
if (missing.length > 0) {
|
|
10888
|
+
throw new Error(
|
|
10889
|
+
`Batch ${batch.id} did not return results for: ${missing.join(", ")}`
|
|
10890
|
+
);
|
|
10891
|
+
}
|
|
10892
|
+
return records;
|
|
10893
|
+
}
|
|
10894
|
+
function processorForStage(definition, kind, scorerName2) {
|
|
10895
|
+
if (kind === "task") {
|
|
10896
|
+
if (!isBatchTask(definition.evaluator.task)) {
|
|
10897
|
+
throw new Error("Definition no longer contains the batch task");
|
|
10898
|
+
}
|
|
10899
|
+
return definition.evaluator.task.processor;
|
|
10900
|
+
}
|
|
10901
|
+
const scorer = resolveScorers(definition.evaluator.scores ?? []).find(
|
|
10902
|
+
({ name }) => name === scorerName2
|
|
10903
|
+
)?.scorer;
|
|
10904
|
+
if (!isBatchScorer(scorer)) {
|
|
10905
|
+
throw new Error(`Definition no longer contains scorer ${scorerName2}`);
|
|
10906
|
+
}
|
|
10907
|
+
return scorer.processor;
|
|
10908
|
+
}
|
|
10909
|
+
async function materializeCases(definition, data, experiment) {
|
|
10910
|
+
const evaluator = definition.evaluator;
|
|
10911
|
+
const iterable = await _internalResolveEvaluatorData(
|
|
10912
|
+
{
|
|
10913
|
+
data,
|
|
10914
|
+
projectName: definition.projectName,
|
|
10915
|
+
projectId: evaluator.projectId,
|
|
10916
|
+
state: evaluator.state
|
|
10917
|
+
},
|
|
10918
|
+
experiment
|
|
10919
|
+
);
|
|
10920
|
+
const records = [];
|
|
10921
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10922
|
+
for await (const datum of iterable) {
|
|
10923
|
+
const caseId = datum.id ?? datum.upsert_id ?? (evaluator.caseId ? await evaluator.caseId(datum) : void 0);
|
|
10924
|
+
if (!caseId) {
|
|
10925
|
+
throw new Error(
|
|
10926
|
+
"Every durable eval case requires id, upsert_id, or caseId"
|
|
10927
|
+
);
|
|
10928
|
+
}
|
|
10929
|
+
if (seen.has(caseId))
|
|
10930
|
+
throw new Error(`Duplicate durable eval case id: ${caseId}`);
|
|
10931
|
+
seen.add(caseId);
|
|
10932
|
+
const trialCount = datum.trialCount ?? evaluator.trialCount ?? 1;
|
|
10933
|
+
if (!Number.isInteger(trialCount) || trialCount < 1) {
|
|
10934
|
+
throw new Error(`Invalid trialCount for durable eval case ${caseId}`);
|
|
10935
|
+
}
|
|
10936
|
+
for (let trialIndex = 0; trialIndex < trialCount; trialIndex++) {
|
|
10937
|
+
records.push({
|
|
10938
|
+
id: `${caseId}:trial:${trialIndex}`,
|
|
10939
|
+
caseId,
|
|
10940
|
+
trialIndex,
|
|
10941
|
+
datum: assertJsonValue(datum, `case ${caseId}`),
|
|
10942
|
+
metadata: assertJsonValue(
|
|
10943
|
+
"metadata" in datum ? datum.metadata : {},
|
|
10944
|
+
`metadata for ${caseId}`
|
|
10945
|
+
),
|
|
10946
|
+
tags: datum.tags,
|
|
10947
|
+
taskComplete: false,
|
|
10948
|
+
taskLogged: false,
|
|
10949
|
+
scores: /* @__PURE__ */ Object.create(null),
|
|
10950
|
+
loggedScores: /* @__PURE__ */ Object.create(null),
|
|
10951
|
+
classifications: /* @__PURE__ */ Object.create(null),
|
|
10952
|
+
loggedClassifications: /* @__PURE__ */ Object.create(null)
|
|
10953
|
+
});
|
|
10954
|
+
}
|
|
10955
|
+
}
|
|
10956
|
+
return records;
|
|
10957
|
+
}
|
|
10958
|
+
function taskBatchItem(record, parameters) {
|
|
10959
|
+
const datum = record.datum;
|
|
10960
|
+
return {
|
|
10961
|
+
id: record.id,
|
|
10962
|
+
input: datum.input,
|
|
10963
|
+
expected: "expected" in datum ? datum.expected : void 0,
|
|
10964
|
+
metadata: record.metadata,
|
|
10965
|
+
tags: record.tags,
|
|
10966
|
+
parameters,
|
|
10967
|
+
trialIndex: record.trialIndex
|
|
10968
|
+
};
|
|
10969
|
+
}
|
|
10970
|
+
function scorerBatchItem(record) {
|
|
10971
|
+
const datum = record.datum;
|
|
10972
|
+
return {
|
|
10973
|
+
id: record.id,
|
|
10974
|
+
input: datum.input,
|
|
10975
|
+
output: record.output,
|
|
10976
|
+
expected: "expected" in datum ? datum.expected : void 0,
|
|
10977
|
+
metadata: record.metadata,
|
|
10978
|
+
tags: record.tags,
|
|
10979
|
+
trialIndex: record.trialIndex
|
|
10980
|
+
};
|
|
10981
|
+
}
|
|
10982
|
+
async function finishExperiment(definition, state, experiment) {
|
|
10983
|
+
const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
|
|
10984
|
+
({ name }) => name
|
|
10985
|
+
);
|
|
10986
|
+
const results = state.cases.map((record) => {
|
|
10987
|
+
const datum = record.datum;
|
|
10988
|
+
const scores = Object.fromEntries(
|
|
10989
|
+
scorerNames.flatMap(
|
|
10990
|
+
(name) => Object.entries(
|
|
10991
|
+
_internalPrepareEvaluatorScore(
|
|
10992
|
+
record.scores[name],
|
|
10993
|
+
name
|
|
10994
|
+
).scores ?? {}
|
|
10995
|
+
)
|
|
10996
|
+
)
|
|
10997
|
+
);
|
|
10998
|
+
const classifications = Object.fromEntries(
|
|
10999
|
+
Object.entries(record.classifications).flatMap(
|
|
11000
|
+
([name, value]) => Object.entries(
|
|
11001
|
+
_internalPrepareEvaluatorClassification(value, name).classifications ?? {}
|
|
11002
|
+
)
|
|
11003
|
+
)
|
|
11004
|
+
);
|
|
11005
|
+
return {
|
|
11006
|
+
...datum,
|
|
11007
|
+
output: record.output,
|
|
11008
|
+
metadata: record.metadata,
|
|
11009
|
+
tags: record.tags,
|
|
11010
|
+
scores,
|
|
11011
|
+
error: void 0,
|
|
11012
|
+
...Object.keys(classifications).length > 0 ? { classifications } : {}
|
|
11013
|
+
};
|
|
11014
|
+
});
|
|
11015
|
+
if (!experiment) {
|
|
11016
|
+
return buildLocalSummary(
|
|
11017
|
+
{
|
|
11018
|
+
...definition.evaluator,
|
|
11019
|
+
projectName: definition.projectName,
|
|
11020
|
+
evalName: state.experimentName
|
|
11021
|
+
},
|
|
11022
|
+
results
|
|
11023
|
+
);
|
|
11024
|
+
}
|
|
11025
|
+
await experiment.flush();
|
|
11026
|
+
let comparisonExperimentId = definition.evaluator.baseExperimentId;
|
|
11027
|
+
if (!comparisonExperimentId) {
|
|
11028
|
+
try {
|
|
11029
|
+
comparisonExperimentId = await experiment._getBaseExperimentId();
|
|
11030
|
+
} catch {
|
|
11031
|
+
comparisonExperimentId = void 0;
|
|
11032
|
+
}
|
|
11033
|
+
}
|
|
11034
|
+
return await experiment.summarize({
|
|
11035
|
+
summarizeScores: definition.evaluator.summarizeScores,
|
|
11036
|
+
...comparisonExperimentId ? { comparisonExperimentId } : {}
|
|
11037
|
+
});
|
|
11038
|
+
}
|
|
11039
|
+
function resolveScorers(scorers) {
|
|
11040
|
+
return scorers.map((scorer, index) => ({
|
|
11041
|
+
name: isBatchScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
|
|
11042
|
+
scorer
|
|
11043
|
+
}));
|
|
11044
|
+
}
|
|
11045
|
+
function runKey(projectName, evalName, runId) {
|
|
11046
|
+
return `durable-eval/v1/runs/${contentVersion(encoder.encode(`${projectName}\0${evalName}\0${runId}`))}`;
|
|
11047
|
+
}
|
|
11048
|
+
function encodedKeyPart(value) {
|
|
11049
|
+
return uint8ArrayToBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
11050
|
+
}
|
|
11051
|
+
function caseRecordKey(key, caseId, kind, ...names) {
|
|
11052
|
+
const suffix = names.map(encodedKeyPart).join("/");
|
|
11053
|
+
return `${key}/cases/${encodedKeyPart(caseId)}/${kind}${suffix ? `/${suffix}` : ""}`;
|
|
11054
|
+
}
|
|
11055
|
+
function batchRecordKey(key, batchId) {
|
|
11056
|
+
return `${key}/batches/${encodedKeyPart(batchId)}`;
|
|
11057
|
+
}
|
|
11058
|
+
function claimRecordKey(key, kind, ...parts) {
|
|
11059
|
+
const identity = stableStringify([kind, parts]);
|
|
11060
|
+
return `${key}/claims/${contentVersion(encoder.encode(identity))}`;
|
|
11061
|
+
}
|
|
11062
|
+
async function claimAction(store, key, kind, ...parts) {
|
|
11063
|
+
return (await store.getOrSet(
|
|
11064
|
+
claimRecordKey(key, kind, ...parts),
|
|
11065
|
+
encoder.encode("claimed")
|
|
11066
|
+
)).created;
|
|
11067
|
+
}
|
|
11068
|
+
function plannedBatches(definition, runId, caseIds) {
|
|
11069
|
+
const stages = [];
|
|
11070
|
+
if (isBatchTask(definition.evaluator.task)) stages.push({ kind: "task" });
|
|
11071
|
+
for (const { name, scorer } of resolveScorers(
|
|
11072
|
+
definition.evaluator.scores ?? []
|
|
11073
|
+
)) {
|
|
11074
|
+
if (isBatchScorer(scorer)) {
|
|
11075
|
+
stages.push({ kind: "score", scorerName: name });
|
|
11076
|
+
}
|
|
11077
|
+
}
|
|
11078
|
+
const plans = [];
|
|
11079
|
+
for (const { kind, scorerName: scorerName2 } of stages) {
|
|
11080
|
+
const batchSize = processorForStage(definition, kind, scorerName2).batchSize ?? DEFAULT_BATCH_SIZE;
|
|
11081
|
+
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
11082
|
+
throw new Error(
|
|
11083
|
+
`Invalid batchSize for ${scorerName2 ?? "task"}: ${batchSize}`
|
|
11084
|
+
);
|
|
11085
|
+
}
|
|
11086
|
+
for (let offset = 0; offset < caseIds.length; offset += batchSize) {
|
|
11087
|
+
const itemIds = caseIds.slice(offset, offset + batchSize);
|
|
11088
|
+
plans.push({
|
|
11089
|
+
id: deterministicId(
|
|
11090
|
+
stableStringify([runId, kind, scorerName2, itemIds])
|
|
11091
|
+
),
|
|
11092
|
+
kind,
|
|
11093
|
+
scorerName: scorerName2,
|
|
11094
|
+
itemIds
|
|
11095
|
+
});
|
|
11096
|
+
}
|
|
11097
|
+
}
|
|
11098
|
+
return plans;
|
|
11099
|
+
}
|
|
11100
|
+
async function readCaseRecord(definition, store, key, id) {
|
|
11101
|
+
const scorers = resolveScorers(definition.evaluator.scores ?? []);
|
|
11102
|
+
const classifiers = (definition.evaluator.classifiers ?? []).map(
|
|
11103
|
+
classifierName
|
|
11104
|
+
);
|
|
11105
|
+
const [
|
|
11106
|
+
base,
|
|
11107
|
+
task,
|
|
11108
|
+
taskLog,
|
|
11109
|
+
scoreValues,
|
|
11110
|
+
scoreLogValues,
|
|
11111
|
+
classificationValues,
|
|
11112
|
+
classificationLogValues
|
|
11113
|
+
] = await Promise.all([
|
|
11114
|
+
readJson(store, caseRecordKey(key, id, "base")),
|
|
11115
|
+
readJson(store, caseRecordKey(key, id, "task")),
|
|
11116
|
+
readJson(store, caseRecordKey(key, id, "task-log")),
|
|
11117
|
+
Promise.all(
|
|
11118
|
+
scorers.map(async ({ name }) => ({
|
|
11119
|
+
name,
|
|
11120
|
+
value: await readJson(
|
|
11121
|
+
store,
|
|
11122
|
+
caseRecordKey(key, id, "score", name)
|
|
11123
|
+
)
|
|
11124
|
+
}))
|
|
11125
|
+
),
|
|
11126
|
+
Promise.all(
|
|
11127
|
+
scorers.map(async ({ name }) => ({
|
|
11128
|
+
name,
|
|
11129
|
+
value: await readJson(
|
|
11130
|
+
store,
|
|
11131
|
+
caseRecordKey(key, id, "score-log", name)
|
|
11132
|
+
)
|
|
11133
|
+
}))
|
|
11134
|
+
),
|
|
11135
|
+
Promise.all(
|
|
11136
|
+
classifiers.map(async (name) => ({
|
|
11137
|
+
name,
|
|
11138
|
+
value: await readJson(
|
|
11139
|
+
store,
|
|
11140
|
+
caseRecordKey(key, id, "classification", name)
|
|
11141
|
+
)
|
|
11142
|
+
}))
|
|
11143
|
+
),
|
|
11144
|
+
Promise.all(
|
|
11145
|
+
classifiers.map(async (name) => ({
|
|
11146
|
+
name,
|
|
11147
|
+
value: await readJson(
|
|
11148
|
+
store,
|
|
11149
|
+
caseRecordKey(key, id, "classification-log", name)
|
|
11150
|
+
)
|
|
11151
|
+
}))
|
|
11152
|
+
)
|
|
11153
|
+
]);
|
|
11154
|
+
if (!base) throw new Error(`Durable eval case ${id} is missing`);
|
|
11155
|
+
const scores = /* @__PURE__ */ Object.create(null);
|
|
11156
|
+
for (const { name, value } of scoreValues) {
|
|
11157
|
+
if (value !== void 0) scores[name] = value;
|
|
11158
|
+
}
|
|
11159
|
+
const loggedScores = /* @__PURE__ */ Object.create(null);
|
|
11160
|
+
for (const { name, value } of scoreLogValues) {
|
|
11161
|
+
if (value) loggedScores[name] = true;
|
|
11162
|
+
}
|
|
11163
|
+
const classifications = /* @__PURE__ */ Object.create(null);
|
|
11164
|
+
for (const { name, value } of classificationValues) {
|
|
11165
|
+
if (value !== void 0) classifications[name] = value;
|
|
11166
|
+
}
|
|
11167
|
+
const loggedClassifications = /* @__PURE__ */ Object.create(null);
|
|
11168
|
+
for (const { name, value } of classificationLogValues) {
|
|
11169
|
+
if (value) loggedClassifications[name] = true;
|
|
11170
|
+
}
|
|
11171
|
+
return {
|
|
11172
|
+
...base,
|
|
11173
|
+
metadata: task?.metadata ?? base.metadata,
|
|
11174
|
+
tags: task ? task.tags : base.tags,
|
|
11175
|
+
taskComplete: task !== void 0,
|
|
11176
|
+
taskLogged: taskLog !== void 0,
|
|
11177
|
+
output: task?.output,
|
|
11178
|
+
rootSpan: taskLog?.rootSpan,
|
|
11179
|
+
scores,
|
|
11180
|
+
loggedScores,
|
|
11181
|
+
classifications,
|
|
11182
|
+
loggedClassifications
|
|
11183
|
+
};
|
|
11184
|
+
}
|
|
11185
|
+
async function readRunState(definition, store, key) {
|
|
11186
|
+
const record = await readJson(store, key);
|
|
11187
|
+
if (!record) return void 0;
|
|
11188
|
+
const plans = plannedBatches(definition, record.runId, record.caseIds);
|
|
11189
|
+
const [cases, batchRecords] = await Promise.all([
|
|
11190
|
+
Promise.all(
|
|
11191
|
+
record.caseIds.map((id) => readCaseRecord(definition, store, key, id))
|
|
11192
|
+
),
|
|
11193
|
+
Promise.all(
|
|
11194
|
+
plans.map(async ({ id }) => {
|
|
11195
|
+
return readJson(store, batchRecordKey(key, id));
|
|
11196
|
+
})
|
|
11197
|
+
)
|
|
11198
|
+
]);
|
|
11199
|
+
const batches = batchRecords.filter(
|
|
11200
|
+
(value) => value !== void 0
|
|
11201
|
+
);
|
|
11202
|
+
const { caseIds: _caseIds, ...state } = record;
|
|
11203
|
+
return { ...state, cases, batches };
|
|
11204
|
+
}
|
|
11205
|
+
async function writeRunRecord(store, key, state) {
|
|
11206
|
+
const { cases, batches: _batches, ...record } = state;
|
|
11207
|
+
await writeJson(store, key, {
|
|
11208
|
+
...record,
|
|
11209
|
+
caseIds: cases.map(({ id }) => id)
|
|
11210
|
+
});
|
|
11211
|
+
}
|
|
11212
|
+
async function writeCaseBaseRecords(store, key, records) {
|
|
11213
|
+
await Promise.all(
|
|
11214
|
+
records.map(
|
|
11215
|
+
({ id, caseId, trialIndex, datum, metadata, tags }) => writeJson(store, caseRecordKey(key, id, "base"), {
|
|
11216
|
+
id,
|
|
11217
|
+
caseId,
|
|
11218
|
+
trialIndex,
|
|
11219
|
+
datum,
|
|
11220
|
+
metadata,
|
|
11221
|
+
tags
|
|
11222
|
+
})
|
|
11223
|
+
)
|
|
11224
|
+
);
|
|
11225
|
+
}
|
|
11226
|
+
async function writeCaseRecords(store, key, records) {
|
|
11227
|
+
const writes = [];
|
|
11228
|
+
for (const record of records) {
|
|
11229
|
+
if (record.taskComplete) {
|
|
11230
|
+
writes.push(
|
|
11231
|
+
writeJson(store, caseRecordKey(key, record.id, "task"), {
|
|
11232
|
+
output: record.output,
|
|
11233
|
+
metadata: record.metadata,
|
|
11234
|
+
tags: record.tags,
|
|
11235
|
+
taskComplete: true
|
|
11236
|
+
})
|
|
11237
|
+
);
|
|
11238
|
+
}
|
|
11239
|
+
if (record.taskLogged) {
|
|
11240
|
+
writes.push(
|
|
11241
|
+
writeJson(store, caseRecordKey(key, record.id, "task-log"), {
|
|
11242
|
+
rootSpan: record.rootSpan,
|
|
11243
|
+
taskLogged: true
|
|
11244
|
+
})
|
|
11245
|
+
);
|
|
11246
|
+
}
|
|
11247
|
+
for (const [name, value] of Object.entries(record.scores)) {
|
|
11248
|
+
writes.push(
|
|
11249
|
+
writeJson(store, caseRecordKey(key, record.id, "score", name), value)
|
|
11250
|
+
);
|
|
11251
|
+
}
|
|
11252
|
+
for (const name of Object.keys(record.loggedScores)) {
|
|
11253
|
+
writes.push(
|
|
11254
|
+
writeJson(
|
|
11255
|
+
store,
|
|
11256
|
+
caseRecordKey(key, record.id, "score-log", name),
|
|
11257
|
+
true
|
|
11258
|
+
)
|
|
11259
|
+
);
|
|
11260
|
+
}
|
|
11261
|
+
for (const [name, value] of Object.entries(record.classifications)) {
|
|
11262
|
+
writes.push(
|
|
11263
|
+
writeJson(
|
|
11264
|
+
store,
|
|
11265
|
+
caseRecordKey(key, record.id, "classification", name),
|
|
11266
|
+
value
|
|
11267
|
+
)
|
|
11268
|
+
);
|
|
11269
|
+
}
|
|
11270
|
+
for (const name of Object.keys(record.loggedClassifications)) {
|
|
11271
|
+
writes.push(
|
|
11272
|
+
writeJson(
|
|
11273
|
+
store,
|
|
11274
|
+
caseRecordKey(key, record.id, "classification-log", name),
|
|
11275
|
+
true
|
|
11276
|
+
)
|
|
11277
|
+
);
|
|
11278
|
+
}
|
|
11279
|
+
}
|
|
11280
|
+
await Promise.all(writes);
|
|
11281
|
+
}
|
|
11282
|
+
async function writeBatchRecords(store, key, records) {
|
|
11283
|
+
await Promise.all(
|
|
11284
|
+
records.map(
|
|
11285
|
+
(record) => writeJson(store, batchRecordKey(key, record.id), record)
|
|
11286
|
+
)
|
|
11287
|
+
);
|
|
11288
|
+
}
|
|
11289
|
+
function deterministicId(value) {
|
|
11290
|
+
const hex = contentVersion(encoder.encode(value)).padEnd(32, "0").slice(0, 32);
|
|
11291
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-5${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
11292
|
+
}
|
|
11293
|
+
function contentVersion(value) {
|
|
11294
|
+
if (isomorph_default.hash) return isomorph_default.hash(decoder.decode(value));
|
|
11295
|
+
let hash = 2166136261;
|
|
11296
|
+
for (const byte of value) {
|
|
11297
|
+
hash ^= byte;
|
|
11298
|
+
hash = Math.imul(hash, 16777619);
|
|
11299
|
+
}
|
|
11300
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
11301
|
+
}
|
|
11302
|
+
async function readJson(store, key) {
|
|
11303
|
+
const value = await store.read(key);
|
|
11304
|
+
return value ? JSON.parse(decoder.decode(value)) : void 0;
|
|
11305
|
+
}
|
|
11306
|
+
async function writeJson(store, key, value) {
|
|
11307
|
+
await store.write(key, encoder.encode(stableStringify(value)));
|
|
11308
|
+
}
|
|
11309
|
+
function stableStringify(value) {
|
|
11310
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
11311
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
11312
|
+
return Object.fromEntries(
|
|
11313
|
+
Object.entries(nested).sort(
|
|
11314
|
+
([left], [right]) => left.localeCompare(right)
|
|
11315
|
+
)
|
|
11316
|
+
);
|
|
11317
|
+
}
|
|
11318
|
+
return nested;
|
|
11319
|
+
});
|
|
11320
|
+
}
|
|
11321
|
+
function assertJsonValue(value, label) {
|
|
11322
|
+
try {
|
|
11323
|
+
const serialized = JSON.stringify(value);
|
|
11324
|
+
if (serialized === void 0)
|
|
11325
|
+
throw new Error("value serializes to undefined");
|
|
11326
|
+
return JSON.parse(serialized);
|
|
11327
|
+
} catch (error) {
|
|
11328
|
+
throw new Error(`${label} must be JSON serializable`, { cause: error });
|
|
11329
|
+
}
|
|
11330
|
+
}
|
|
11331
|
+
function resultItemId(value) {
|
|
11332
|
+
if (typeof value !== "object" || value === null || !("id" in value) || typeof value.id !== "string") {
|
|
11333
|
+
throw new Error("Batch results must contain a string id");
|
|
11334
|
+
}
|
|
11335
|
+
return value.id;
|
|
11336
|
+
}
|
|
11337
|
+
function isBatchTask(value) {
|
|
11338
|
+
return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_TASK_KIND;
|
|
11339
|
+
}
|
|
11340
|
+
function isBatchScorer(value) {
|
|
11341
|
+
return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_SCORER_KIND;
|
|
11342
|
+
}
|
|
11343
|
+
function asError(error) {
|
|
11344
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
11345
|
+
}
|
|
11346
|
+
|
|
9576
11347
|
// src/agent-assertions.ts
|
|
9577
11348
|
function agentAssertionScorer(callback, options = {}) {
|
|
9578
11349
|
return async (args) => {
|
|
@@ -10505,6 +12276,8 @@ export {
|
|
|
10505
12276
|
BRAINTRUST_PARENT_KEY,
|
|
10506
12277
|
BaseAttachment,
|
|
10507
12278
|
BaseExperiment,
|
|
12279
|
+
BatchScorer,
|
|
12280
|
+
BatchTask,
|
|
10508
12281
|
BraintrustLangChainCallbackHandler,
|
|
10509
12282
|
BraintrustMiddleware,
|
|
10510
12283
|
BraintrustObservabilityExporter,
|
|
@@ -10518,6 +12291,8 @@ export {
|
|
|
10518
12291
|
DEFAULT_MAX_REQUEST_SIZE,
|
|
10519
12292
|
Dataset,
|
|
10520
12293
|
DatasetPipeline,
|
|
12294
|
+
DurableEvalMemoryStore,
|
|
12295
|
+
DurableEvalRedisStore,
|
|
10521
12296
|
ERR_PERMALINK,
|
|
10522
12297
|
Eval,
|
|
10523
12298
|
EvalResultWithSummary,
|
|
@@ -10576,6 +12351,7 @@ export {
|
|
|
10576
12351
|
deepCopyEvent,
|
|
10577
12352
|
exports_exports as default,
|
|
10578
12353
|
defaultErrorScoreHandler,
|
|
12354
|
+
defineDurableEval,
|
|
10579
12355
|
deserializePlainStringAsJSON,
|
|
10580
12356
|
devNullWritableStream,
|
|
10581
12357
|
evaluatorDefinitionSchema,
|
|
@@ -10671,5 +12447,6 @@ export {
|
|
|
10671
12447
|
wrapPiCodingAgentSDK,
|
|
10672
12448
|
wrapStrandsAgentSDK,
|
|
10673
12449
|
wrapTraced,
|
|
10674
|
-
wrapVitest
|
|
12450
|
+
wrapVitest,
|
|
12451
|
+
wrapVoyageAI
|
|
10675
12452
|
};
|