braintrust 3.5.0 → 3.6.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.
Files changed (41) hide show
  1. package/dev/dist/index.d.mts +4 -2
  2. package/dev/dist/index.d.ts +4 -2
  3. package/dev/dist/index.js +1353 -211
  4. package/dev/dist/index.mjs +1170 -28
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +81 -0
  6. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/rollup.cjs +81 -0
  8. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/vite.cjs +81 -0
  10. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  11. package/dist/auto-instrumentations/bundler/webpack.cjs +81 -0
  12. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  13. package/dist/auto-instrumentations/{chunk-DQTPSXJB.mjs → chunk-F7WAXFNM.mjs} +82 -1
  14. package/dist/auto-instrumentations/{chunk-F3TJZ3Z2.mjs → chunk-WOUC73KB.mjs} +3 -1
  15. package/dist/auto-instrumentations/hook.mjs +139 -49
  16. package/dist/auto-instrumentations/index.cjs +82 -0
  17. package/dist/auto-instrumentations/index.d.mts +3 -1
  18. package/dist/auto-instrumentations/index.d.ts +3 -1
  19. package/dist/auto-instrumentations/index.mjs +3 -1
  20. package/dist/browser.d.mts +17 -4
  21. package/dist/browser.d.ts +17 -4
  22. package/dist/browser.js +1479 -232
  23. package/dist/browser.mjs +1479 -232
  24. package/dist/cli.js +1162 -20
  25. package/dist/edge-light.d.mts +1 -1
  26. package/dist/edge-light.d.ts +1 -1
  27. package/dist/edge-light.js +1412 -222
  28. package/dist/edge-light.mjs +1412 -222
  29. package/dist/index.d.mts +30 -17
  30. package/dist/index.d.ts +30 -17
  31. package/dist/index.js +1763 -514
  32. package/dist/index.mjs +1458 -209
  33. package/dist/instrumentation/index.d.mts +3 -0
  34. package/dist/instrumentation/index.d.ts +3 -0
  35. package/dist/instrumentation/index.js +1102 -19
  36. package/dist/instrumentation/index.mjs +1102 -19
  37. package/dist/workerd.d.mts +1 -1
  38. package/dist/workerd.d.ts +1 -1
  39. package/dist/workerd.js +1412 -222
  40. package/dist/workerd.mjs +1412 -222
  41. package/package.json +1 -1
@@ -2,6 +2,64 @@
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  import * as diagnostics_channel from "node:diagnostics_channel";
4
4
  import * as path from "node:path";
5
+
6
+ // src/auto-instrumentations/patch-tracing-channel.ts
7
+ function patchTracingChannel(tracingChannelFn) {
8
+ const dummyChannel = tracingChannelFn("__braintrust_probe__");
9
+ const TracingChannel = dummyChannel?.constructor;
10
+ if (!TracingChannel?.prototype) {
11
+ return;
12
+ }
13
+ if (!Object.getOwnPropertyDescriptor(TracingChannel.prototype, "hasSubscribers")) {
14
+ Object.defineProperty(TracingChannel.prototype, "hasSubscribers", {
15
+ configurable: true,
16
+ enumerable: false,
17
+ get() {
18
+ return Boolean(
19
+ this.start?.hasSubscribers || this.end?.hasSubscribers || this.asyncStart?.hasSubscribers || this.asyncEnd?.hasSubscribers || this.error?.hasSubscribers
20
+ );
21
+ }
22
+ });
23
+ }
24
+ if (TracingChannel.prototype.tracePromise) {
25
+ TracingChannel.prototype.tracePromise = function(fn, context = {}, thisArg, ...args) {
26
+ const { start, end, asyncStart, asyncEnd, error } = this;
27
+ function reject2(err) {
28
+ context.error = err;
29
+ error?.publish(context);
30
+ asyncStart?.publish(context);
31
+ asyncEnd?.publish(context);
32
+ return Promise.reject(err);
33
+ }
34
+ function resolve(result) {
35
+ context.result = result;
36
+ asyncStart?.publish(context);
37
+ asyncEnd?.publish(context);
38
+ return result;
39
+ }
40
+ return start.runStores(context, () => {
41
+ try {
42
+ const result = Reflect.apply(fn, thisArg, args);
43
+ end?.publish(context);
44
+ if (result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
45
+ return result.then(resolve, reject2);
46
+ }
47
+ context.result = result;
48
+ asyncStart?.publish(context);
49
+ asyncEnd?.publish(context);
50
+ return result;
51
+ } catch (err) {
52
+ context.error = err;
53
+ error?.publish(context);
54
+ end?.publish(context);
55
+ throw err;
56
+ }
57
+ });
58
+ };
59
+ }
60
+ }
61
+
62
+ // src/node/config.ts
5
63
  import * as fs from "node:fs/promises";
6
64
  import * as os from "node:os";
7
65
  import * as fsSync from "node:fs";
@@ -4506,6 +4564,9 @@ var BRAINTRUST_ATTACHMENT = BraintrustAttachmentReference.shape.type.value;
4506
4564
  var EXTERNAL_ATTACHMENT = ExternalAttachmentReference.shape.type.value;
4507
4565
  var LOGS3_OVERFLOW_REFERENCE_TYPE = "logs3_overflow";
4508
4566
  var BRAINTRUST_PARAMS = Object.keys(BraintrustModelParams.shape);
4567
+ var RESET_CONTEXT_MANAGER_STATE = Symbol.for(
4568
+ "braintrust.resetContextManagerState"
4569
+ );
4509
4570
  var DEFAULT_MAX_REQUEST_SIZE = 6 * 1024 * 1024;
4510
4571
  var parametersRowSchema = z8.object({
4511
4572
  id: z8.string().uuid(),
@@ -4568,13 +4629,18 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
4568
4629
  return `ERROR: Failed to mask field '${fieldName}' - ${errorType}`;
4569
4630
  }
4570
4631
  }
4632
+ var BRAINTRUST_CURRENT_SPAN_STORE = Symbol.for(
4633
+ "braintrust.currentSpanStore"
4634
+ );
4571
4635
  var ContextManager = class {
4572
4636
  };
4573
4637
  var BraintrustContextManager = class extends ContextManager {
4574
4638
  _currentSpan;
4639
+ [BRAINTRUST_CURRENT_SPAN_STORE];
4575
4640
  constructor() {
4576
4641
  super();
4577
4642
  this._currentSpan = isomorph_default.newAsyncLocalStorage();
4643
+ this[BRAINTRUST_CURRENT_SPAN_STORE] = this._currentSpan;
4578
4644
  }
4579
4645
  getParentSpanIds() {
4580
4646
  const currentSpan2 = this._currentSpan.getStore();
@@ -4781,6 +4847,9 @@ var BraintrustState = class _BraintrustState {
4781
4847
  resetIdGenState() {
4782
4848
  this._idGenerator = null;
4783
4849
  }
4850
+ [RESET_CONTEXT_MANAGER_STATE]() {
4851
+ this._contextManager = null;
4852
+ }
4784
4853
  get idGenerator() {
4785
4854
  if (this._idGenerator === null) {
4786
4855
  this._idGenerator = getIdGenerator();
@@ -9283,6 +9352,36 @@ function startSpanForEvent(config, event, channelName) {
9283
9352
  }
9284
9353
  return { span, startTime };
9285
9354
  }
9355
+ function ensureSpanStateForEvent(states, config, event, channelName) {
9356
+ const key = event;
9357
+ const existing = states.get(key);
9358
+ if (existing) {
9359
+ return existing;
9360
+ }
9361
+ const created = startSpanForEvent(config, event, channelName);
9362
+ states.set(key, created);
9363
+ return created;
9364
+ }
9365
+ function bindCurrentSpanStoreToStart(tracingChannel2, states, config, channelName) {
9366
+ const state = _internalGetGlobalState();
9367
+ const startChannel = tracingChannel2.start;
9368
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
9369
+ if (!currentSpanStore || !startChannel) {
9370
+ return void 0;
9371
+ }
9372
+ startChannel.bindStore(
9373
+ currentSpanStore,
9374
+ (event) => ensureSpanStateForEvent(
9375
+ states,
9376
+ config,
9377
+ event,
9378
+ channelName
9379
+ ).span
9380
+ );
9381
+ return () => {
9382
+ startChannel.unbindStore(currentSpanStore);
9383
+ };
9384
+ }
9286
9385
  function logErrorAndEnd(states, event) {
9287
9386
  const spanData = states.get(event);
9288
9387
  if (!spanData) {
@@ -9298,15 +9397,19 @@ function traceAsyncChannel(channel2, config) {
9298
9397
  const tracingChannel2 = channel2.tracingChannel();
9299
9398
  const states = /* @__PURE__ */ new WeakMap();
9300
9399
  const channelName = channel2.channelName;
9400
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
9401
+ tracingChannel2,
9402
+ states,
9403
+ config,
9404
+ channelName
9405
+ );
9301
9406
  const handlers = {
9302
9407
  start: (event) => {
9303
- states.set(
9408
+ ensureSpanStateForEvent(
9409
+ states,
9410
+ config,
9304
9411
  event,
9305
- startSpanForEvent(
9306
- config,
9307
- event,
9308
- channelName
9309
- )
9412
+ channelName
9310
9413
  );
9311
9414
  },
9312
9415
  asyncEnd: (event) => {
@@ -9348,6 +9451,7 @@ function traceAsyncChannel(channel2, config) {
9348
9451
  };
9349
9452
  tracingChannel2.subscribe(handlers);
9350
9453
  return () => {
9454
+ unbindCurrentSpanStore?.();
9351
9455
  tracingChannel2.unsubscribe(handlers);
9352
9456
  };
9353
9457
  }
@@ -9355,15 +9459,19 @@ function traceStreamingChannel(channel2, config) {
9355
9459
  const tracingChannel2 = channel2.tracingChannel();
9356
9460
  const states = /* @__PURE__ */ new WeakMap();
9357
9461
  const channelName = channel2.channelName;
9462
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
9463
+ tracingChannel2,
9464
+ states,
9465
+ config,
9466
+ channelName
9467
+ );
9358
9468
  const handlers = {
9359
9469
  start: (event) => {
9360
- states.set(
9470
+ ensureSpanStateForEvent(
9471
+ states,
9472
+ config,
9361
9473
  event,
9362
- startSpanForEvent(
9363
- config,
9364
- event,
9365
- channelName
9366
- )
9474
+ channelName
9367
9475
  );
9368
9476
  },
9369
9477
  asyncEnd: (event) => {
@@ -9479,6 +9587,7 @@ function traceStreamingChannel(channel2, config) {
9479
9587
  };
9480
9588
  tracingChannel2.subscribe(handlers);
9481
9589
  return () => {
9590
+ unbindCurrentSpanStore?.();
9482
9591
  tracingChannel2.unsubscribe(handlers);
9483
9592
  };
9484
9593
  }
@@ -9486,15 +9595,19 @@ function traceSyncStreamChannel(channel2, config) {
9486
9595
  const tracingChannel2 = channel2.tracingChannel();
9487
9596
  const states = /* @__PURE__ */ new WeakMap();
9488
9597
  const channelName = channel2.channelName;
9598
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
9599
+ tracingChannel2,
9600
+ states,
9601
+ config,
9602
+ channelName
9603
+ );
9489
9604
  const handlers = {
9490
9605
  start: (event) => {
9491
- states.set(
9606
+ ensureSpanStateForEvent(
9607
+ states,
9608
+ config,
9492
9609
  event,
9493
- startSpanForEvent(
9494
- config,
9495
- event,
9496
- channelName
9497
- )
9610
+ channelName
9498
9611
  );
9499
9612
  },
9500
9613
  end: (event) => {
@@ -9583,6 +9696,7 @@ function traceSyncStreamChannel(channel2, config) {
9583
9696
  };
9584
9697
  tracingChannel2.subscribe(handlers);
9585
9698
  return () => {
9699
+ unbindCurrentSpanStore?.();
9586
9700
  tracingChannel2.unsubscribe(handlers);
9587
9701
  };
9588
9702
  }
@@ -12144,6 +12258,1023 @@ function tryToDict(obj) {
12144
12258
  return null;
12145
12259
  }
12146
12260
 
12261
+ // src/instrumentation/plugins/openrouter-channels.ts
12262
+ var openRouterChannels = defineChannels("@openrouter/sdk", {
12263
+ chatSend: channel({
12264
+ channelName: "chat.send",
12265
+ kind: "async"
12266
+ }),
12267
+ embeddingsGenerate: channel({
12268
+ channelName: "embeddings.generate",
12269
+ kind: "async"
12270
+ }),
12271
+ betaResponsesSend: channel({
12272
+ channelName: "beta.responses.send",
12273
+ kind: "async"
12274
+ }),
12275
+ callModel: channel({
12276
+ channelName: "callModel",
12277
+ kind: "sync-stream"
12278
+ }),
12279
+ toolExecute: channel({
12280
+ channelName: "tool.execute",
12281
+ kind: "async"
12282
+ })
12283
+ });
12284
+
12285
+ // src/openrouter-utils.ts
12286
+ var TOKEN_NAME_MAP2 = {
12287
+ promptTokens: "prompt_tokens",
12288
+ inputTokens: "prompt_tokens",
12289
+ completionTokens: "completion_tokens",
12290
+ outputTokens: "completion_tokens",
12291
+ totalTokens: "tokens",
12292
+ prompt_tokens: "prompt_tokens",
12293
+ input_tokens: "prompt_tokens",
12294
+ completion_tokens: "completion_tokens",
12295
+ output_tokens: "completion_tokens",
12296
+ total_tokens: "tokens"
12297
+ };
12298
+ var TOKEN_DETAIL_PREFIX_MAP = {
12299
+ promptTokensDetails: "prompt",
12300
+ inputTokensDetails: "prompt",
12301
+ completionTokensDetails: "completion",
12302
+ outputTokensDetails: "completion",
12303
+ costDetails: "cost",
12304
+ prompt_tokens_details: "prompt",
12305
+ input_tokens_details: "prompt",
12306
+ completion_tokens_details: "completion",
12307
+ output_tokens_details: "completion",
12308
+ cost_details: "cost"
12309
+ };
12310
+ function camelToSnake(value) {
12311
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
12312
+ }
12313
+ function parseOpenRouterMetricsFromUsage(usage) {
12314
+ if (!isObject(usage)) {
12315
+ return {};
12316
+ }
12317
+ const metrics = {};
12318
+ for (const [name, value] of Object.entries(usage)) {
12319
+ if (typeof value === "number") {
12320
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
12321
+ continue;
12322
+ }
12323
+ if (!isObject(value)) {
12324
+ continue;
12325
+ }
12326
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
12327
+ if (!prefix) {
12328
+ continue;
12329
+ }
12330
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
12331
+ if (typeof nestedValue !== "number") {
12332
+ continue;
12333
+ }
12334
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
12335
+ }
12336
+ }
12337
+ return metrics;
12338
+ }
12339
+ function extractOpenRouterUsageMetadata(usage) {
12340
+ if (!isObject(usage)) {
12341
+ return void 0;
12342
+ }
12343
+ const metadata = {};
12344
+ if (typeof usage.isByok === "boolean") {
12345
+ metadata.is_byok = usage.isByok;
12346
+ } else if (typeof usage.is_byok === "boolean") {
12347
+ metadata.is_byok = usage.is_byok;
12348
+ }
12349
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12350
+ }
12351
+
12352
+ // src/openrouter-logging.ts
12353
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
12354
+ "execute",
12355
+ "render",
12356
+ "nextTurnParams",
12357
+ "requireApproval"
12358
+ ]);
12359
+ function parseOpenRouterModelString(model) {
12360
+ if (typeof model !== "string") {
12361
+ return { model };
12362
+ }
12363
+ const slashIndex = model.indexOf("/");
12364
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
12365
+ return {
12366
+ provider: model.substring(0, slashIndex),
12367
+ model: model.substring(slashIndex + 1)
12368
+ };
12369
+ }
12370
+ return { model };
12371
+ }
12372
+ function isZodSchema2(value) {
12373
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
12374
+ }
12375
+ function serializeZodSchema2(schema) {
12376
+ try {
12377
+ return zodToJsonSchema(schema);
12378
+ } catch {
12379
+ return {
12380
+ type: "object",
12381
+ description: "Zod schema (conversion failed)"
12382
+ };
12383
+ }
12384
+ }
12385
+ function serializeOpenRouterTool(tool) {
12386
+ if (!isObject(tool)) {
12387
+ return tool;
12388
+ }
12389
+ const serialized = {};
12390
+ for (const [key, value] of Object.entries(tool)) {
12391
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12392
+ continue;
12393
+ }
12394
+ if (key === "function" && isObject(value)) {
12395
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
12396
+ continue;
12397
+ }
12398
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
12399
+ }
12400
+ return serialized;
12401
+ }
12402
+ function serializeOpenRouterToolsForLogging(tools) {
12403
+ if (!Array.isArray(tools)) {
12404
+ return void 0;
12405
+ }
12406
+ return tools.map((tool) => serializeOpenRouterTool(tool));
12407
+ }
12408
+ function sanitizeOpenRouterLoggedValue(value) {
12409
+ if (isZodSchema2(value)) {
12410
+ return serializeZodSchema2(value);
12411
+ }
12412
+ if (typeof value === "function") {
12413
+ return "[Function]";
12414
+ }
12415
+ if (Array.isArray(value)) {
12416
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
12417
+ }
12418
+ if (!isObject(value)) {
12419
+ return value;
12420
+ }
12421
+ const sanitized = {};
12422
+ for (const [key, entry] of Object.entries(value)) {
12423
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12424
+ continue;
12425
+ }
12426
+ if (key === "tools" && Array.isArray(entry)) {
12427
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
12428
+ continue;
12429
+ }
12430
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12431
+ }
12432
+ return sanitized;
12433
+ }
12434
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
12435
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12436
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12437
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
12438
+ const normalizedModel = parseOpenRouterModelString(model);
12439
+ return {
12440
+ ...rest,
12441
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12442
+ ...providerRouting !== void 0 ? { providerRouting } : {},
12443
+ ...httpReferer !== void 0 ? { httpReferer } : {},
12444
+ ...xTitle !== void 0 ? { xTitle } : {},
12445
+ provider: normalizedModel.provider || "openrouter"
12446
+ };
12447
+ }
12448
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
12449
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
12450
+ return typeof normalized.model === "string" ? {
12451
+ ...normalized,
12452
+ embedding_model: normalized.model
12453
+ } : normalized;
12454
+ }
12455
+ function extractOpenRouterCallModelInput(request) {
12456
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12457
+ }
12458
+ function extractOpenRouterCallModelMetadata(request) {
12459
+ if (!isObject(request)) {
12460
+ return { provider: "openrouter" };
12461
+ }
12462
+ const { input: _input, ...metadata } = request;
12463
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
12464
+ }
12465
+ function extractOpenRouterResponseMetadata(result) {
12466
+ if (!isObject(result)) {
12467
+ return void 0;
12468
+ }
12469
+ const { output: _output, data: _data, usage, ...metadata } = result;
12470
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12471
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12472
+ const { model, provider, ...rest } = metadataRecord;
12473
+ const normalizedModel = parseOpenRouterModelString(model);
12474
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
12475
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
12476
+ const combined = {
12477
+ ...rest,
12478
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12479
+ ...usageMetadata || {},
12480
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
12481
+ };
12482
+ return Object.keys(combined).length > 0 ? combined : void 0;
12483
+ }
12484
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
12485
+ if (isObject(response) && "output" in response && response.output !== void 0) {
12486
+ return sanitizeOpenRouterLoggedValue(response.output);
12487
+ }
12488
+ if (fallbackOutput !== void 0) {
12489
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
12490
+ }
12491
+ return void 0;
12492
+ }
12493
+
12494
+ // src/openrouter-tool-wrapping.ts
12495
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
12496
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
12497
+ "braintrust.openrouter.wrappedCallModelResult"
12498
+ );
12499
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
12500
+ "getFullResponsesStream",
12501
+ "getItemsStream",
12502
+ "getNewMessagesStream",
12503
+ "getReasoningStream",
12504
+ "getTextStream",
12505
+ "getToolCallsStream",
12506
+ "getToolStream"
12507
+ ];
12508
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
12509
+ "cancel",
12510
+ "getPendingToolCalls",
12511
+ "getState",
12512
+ "getToolCalls",
12513
+ "requiresApproval"
12514
+ ];
12515
+ function patchOpenRouterCallModelRequestTools(request) {
12516
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
12517
+ return void 0;
12518
+ }
12519
+ const originalTools = request.tools;
12520
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
12521
+ const didPatch = wrappedTools.some(
12522
+ (tool, index) => tool !== originalTools[index]
12523
+ );
12524
+ if (!didPatch) {
12525
+ return void 0;
12526
+ }
12527
+ request.tools = wrappedTools;
12528
+ return () => {
12529
+ request.tools = originalTools;
12530
+ };
12531
+ }
12532
+ function patchOpenRouterCallModelResult(span, result, request) {
12533
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
12534
+ return false;
12535
+ }
12536
+ const resultLike = result;
12537
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
12538
+ (methodName) => typeof resultLike[methodName] === "function"
12539
+ );
12540
+ if (!hasInstrumentableMethod) {
12541
+ return false;
12542
+ }
12543
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
12544
+ value: true,
12545
+ enumerable: false,
12546
+ configurable: false
12547
+ });
12548
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
12549
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
12550
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
12551
+ let ended = false;
12552
+ let tracedTurnCount = 0;
12553
+ const endSpanWithResult = async (response, fallbackOutput) => {
12554
+ if (ended) {
12555
+ return;
12556
+ }
12557
+ ended = true;
12558
+ const finalResponse = getFinalOpenRouterCallModelResponse(
12559
+ resultLike,
12560
+ response
12561
+ );
12562
+ if (finalResponse) {
12563
+ const rounds = getOpenRouterCallModelRounds(resultLike);
12564
+ const metadata = extractOpenRouterCallModelResultMetadata(
12565
+ finalResponse,
12566
+ rounds.length + 1
12567
+ );
12568
+ span.log({
12569
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
12570
+ ...metadata ? { metadata } : {},
12571
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
12572
+ });
12573
+ span.end();
12574
+ return;
12575
+ }
12576
+ if (fallbackOutput !== void 0) {
12577
+ span.log({
12578
+ output: fallbackOutput
12579
+ });
12580
+ }
12581
+ span.end();
12582
+ };
12583
+ const endSpanWithError = (error) => {
12584
+ if (ended) {
12585
+ return;
12586
+ }
12587
+ ended = true;
12588
+ span.log({
12589
+ error: normalizeError(error).message
12590
+ });
12591
+ span.end();
12592
+ };
12593
+ const finalizeFromResponse = async (fallbackOutput) => {
12594
+ if (!originalGetResponse) {
12595
+ await endSpanWithResult(void 0, fallbackOutput);
12596
+ return;
12597
+ }
12598
+ try {
12599
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
12600
+ } catch {
12601
+ await endSpanWithResult(void 0, fallbackOutput);
12602
+ }
12603
+ };
12604
+ if (originalGetResponse) {
12605
+ resultLike.getResponse = async (...args) => {
12606
+ return await withCurrent(span, async () => {
12607
+ try {
12608
+ const response = await originalGetResponse(...args);
12609
+ await endSpanWithResult(response);
12610
+ return response;
12611
+ } catch (error) {
12612
+ endSpanWithError(error);
12613
+ throw error;
12614
+ }
12615
+ });
12616
+ };
12617
+ }
12618
+ if (typeof resultLike.getText === "function") {
12619
+ const originalGetText = resultLike.getText.bind(resultLike);
12620
+ resultLike.getText = async (...args) => {
12621
+ return await withCurrent(span, async () => {
12622
+ try {
12623
+ const text = await originalGetText(...args);
12624
+ await finalizeFromResponse(text);
12625
+ return text;
12626
+ } catch (error) {
12627
+ endSpanWithError(error);
12628
+ throw error;
12629
+ }
12630
+ });
12631
+ };
12632
+ }
12633
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12634
+ if (typeof resultLike[methodName] !== "function") {
12635
+ continue;
12636
+ }
12637
+ const originalMethod = resultLike[methodName];
12638
+ resultLike[methodName] = async (...args) => {
12639
+ return await withCurrent(span, async () => {
12640
+ return await originalMethod.apply(resultLike, args);
12641
+ });
12642
+ };
12643
+ }
12644
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12645
+ if (typeof resultLike[methodName] !== "function") {
12646
+ continue;
12647
+ }
12648
+ const originalMethod = resultLike[methodName];
12649
+ resultLike[methodName] = (...args) => {
12650
+ const stream = withCurrent(
12651
+ span,
12652
+ () => originalMethod.apply(resultLike, args)
12653
+ );
12654
+ if (!isAsyncIterable2(stream)) {
12655
+ return stream;
12656
+ }
12657
+ return wrapAsyncIterableWithSpan({
12658
+ finalize: finalizeFromResponse,
12659
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
12660
+ onError: endSpanWithError,
12661
+ span
12662
+ });
12663
+ };
12664
+ }
12665
+ if (originalGetInitialResponse) {
12666
+ let initialTurnTraced = false;
12667
+ resultLike.getInitialResponse = async (...args) => {
12668
+ if (initialTurnTraced) {
12669
+ return await withCurrent(span, async () => {
12670
+ return await originalGetInitialResponse(...args);
12671
+ });
12672
+ }
12673
+ initialTurnTraced = true;
12674
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12675
+ const childSpan = startOpenRouterCallModelTurnSpan({
12676
+ request: resolvedRequest,
12677
+ step: tracedTurnCount + 1,
12678
+ stepType: tracedTurnCount === 0 ? "initial" : "continue"
12679
+ });
12680
+ return await withCurrent(childSpan, async () => {
12681
+ try {
12682
+ const response = await originalGetInitialResponse(...args);
12683
+ tracedTurnCount++;
12684
+ finishOpenRouterCallModelTurnSpan({
12685
+ response,
12686
+ step: tracedTurnCount,
12687
+ stepType: tracedTurnCount === 1 ? "initial" : "continue",
12688
+ span: childSpan
12689
+ });
12690
+ return response;
12691
+ } catch (error) {
12692
+ childSpan.log({
12693
+ error: normalizeError(error).message
12694
+ });
12695
+ childSpan.end();
12696
+ throw error;
12697
+ }
12698
+ });
12699
+ };
12700
+ }
12701
+ if (originalMakeFollowupRequest) {
12702
+ resultLike.makeFollowupRequest = async (...args) => {
12703
+ const currentResponse = args[0];
12704
+ const toolResults = Array.isArray(args[1]) ? args[1] : [];
12705
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12706
+ const followupRequest = buildOpenRouterFollowupRequest(
12707
+ resolvedRequest,
12708
+ currentResponse,
12709
+ toolResults
12710
+ );
12711
+ const childSpan = startOpenRouterCallModelTurnSpan({
12712
+ request: followupRequest,
12713
+ step: tracedTurnCount + 1,
12714
+ stepType: "continue"
12715
+ });
12716
+ return await withCurrent(childSpan, async () => {
12717
+ try {
12718
+ const response = await originalMakeFollowupRequest(...args);
12719
+ tracedTurnCount++;
12720
+ finishOpenRouterCallModelTurnSpan({
12721
+ response,
12722
+ step: tracedTurnCount,
12723
+ stepType: "continue",
12724
+ span: childSpan
12725
+ });
12726
+ return response;
12727
+ } catch (error) {
12728
+ childSpan.log({
12729
+ error: normalizeError(error).message
12730
+ });
12731
+ childSpan.end();
12732
+ throw error;
12733
+ }
12734
+ });
12735
+ };
12736
+ }
12737
+ return true;
12738
+ }
12739
+ function wrapOpenRouterTool(tool) {
12740
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12741
+ return tool;
12742
+ }
12743
+ const toolName = tool.function.name || "tool";
12744
+ const originalExecute = tool.function.execute;
12745
+ const wrappedTool = {
12746
+ ...tool,
12747
+ function: {
12748
+ ...tool.function,
12749
+ execute(...args) {
12750
+ return traceToolExecution({
12751
+ args,
12752
+ execute: () => Reflect.apply(originalExecute, this, args),
12753
+ toolCallId: getToolCallId(args[1]),
12754
+ toolName
12755
+ });
12756
+ }
12757
+ }
12758
+ };
12759
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12760
+ value: true,
12761
+ enumerable: false,
12762
+ configurable: false
12763
+ });
12764
+ return wrappedTool;
12765
+ }
12766
+ function isWrappedTool(tool) {
12767
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12768
+ }
12769
+ function isWrappedCallModelResult(value) {
12770
+ return Boolean(
12771
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
12772
+ );
12773
+ }
12774
+ function traceToolExecution(args) {
12775
+ const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
12776
+ const input = args.args.length > 0 ? args.args[0] : void 0;
12777
+ const event = {
12778
+ arguments: [input],
12779
+ span_info: {
12780
+ name: args.toolName
12781
+ },
12782
+ toolCallId: args.toolCallId,
12783
+ toolName: args.toolName
12784
+ };
12785
+ tracingChannel2.start.publish(event);
12786
+ try {
12787
+ const result = args.execute();
12788
+ return publishToolResult(tracingChannel2, event, result);
12789
+ } catch (error) {
12790
+ event.error = normalizeError(error);
12791
+ tracingChannel2.error.publish(event);
12792
+ throw error;
12793
+ }
12794
+ }
12795
+ function publishToolResult(tracingChannel2, event, result) {
12796
+ if (isPromiseLike(result)) {
12797
+ return result.then(
12798
+ (resolved) => {
12799
+ event.result = resolved;
12800
+ tracingChannel2.asyncEnd.publish(event);
12801
+ return resolved;
12802
+ },
12803
+ (error) => {
12804
+ event.error = normalizeError(error);
12805
+ tracingChannel2.error.publish(event);
12806
+ throw error;
12807
+ }
12808
+ );
12809
+ }
12810
+ event.result = result;
12811
+ tracingChannel2.asyncEnd.publish(event);
12812
+ return result;
12813
+ }
12814
+ function getToolCallId(context) {
12815
+ const toolContext = context;
12816
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
12817
+ }
12818
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
12819
+ const combined = {
12820
+ ...extractOpenRouterResponseMetadata(response) || {},
12821
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
12822
+ };
12823
+ return Object.keys(combined).length > 0 ? combined : void 0;
12824
+ }
12825
+ function getFinalOpenRouterCallModelResponse(result, response) {
12826
+ if (isObject(response)) {
12827
+ return response;
12828
+ }
12829
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
12830
+ }
12831
+ function getOpenRouterCallModelRounds(result) {
12832
+ if (!Array.isArray(result.allToolExecutionRounds)) {
12833
+ return [];
12834
+ }
12835
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
12836
+ response: isObject(round.response) ? round.response : void 0,
12837
+ round: typeof round.round === "number" ? round.round : void 0,
12838
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
12839
+ })).filter((round) => round.response !== void 0);
12840
+ }
12841
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
12842
+ const metrics = {};
12843
+ const responses = [
12844
+ ...rounds.map((round) => round.response).filter(isObject),
12845
+ finalResponse
12846
+ ];
12847
+ for (const response of responses) {
12848
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
12849
+ for (const [name, value] of Object.entries(responseMetrics)) {
12850
+ metrics[name] = (metrics[name] || 0) + value;
12851
+ }
12852
+ }
12853
+ return metrics;
12854
+ }
12855
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
12856
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
12857
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
12858
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
12859
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
12860
+ );
12861
+ }
12862
+ function startOpenRouterCallModelTurnSpan(args) {
12863
+ const requestRecord = isObject(args.request) ? args.request : void 0;
12864
+ const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
12865
+ if (isObject(metadata) && "tools" in metadata) {
12866
+ delete metadata.tools;
12867
+ }
12868
+ return startSpan({
12869
+ name: "openrouter.beta.responses.send",
12870
+ spanAttributes: {
12871
+ type: "llm" /* LLM */
12872
+ },
12873
+ event: {
12874
+ input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
12875
+ metadata: {
12876
+ ...metadata,
12877
+ step: args.step,
12878
+ step_type: args.stepType
12879
+ }
12880
+ }
12881
+ });
12882
+ }
12883
+ function finishOpenRouterCallModelTurnSpan(args) {
12884
+ if (!isObject(args.response)) {
12885
+ args.span.end();
12886
+ return;
12887
+ }
12888
+ args.span.log({
12889
+ output: extractOpenRouterResponseOutput(args.response),
12890
+ ...extractOpenRouterResponseMetadata(args.response) ? {
12891
+ metadata: {
12892
+ ...extractOpenRouterResponseMetadata(args.response),
12893
+ ...args.step !== void 0 ? { step: args.step } : {},
12894
+ ...args.stepType ? { step_type: args.stepType } : {}
12895
+ }
12896
+ } : {},
12897
+ metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
12898
+ });
12899
+ args.span.end();
12900
+ }
12901
+ function getOpenRouterResolvedRequest(result, request) {
12902
+ if (isObject(result.resolvedRequest)) {
12903
+ return result.resolvedRequest;
12904
+ }
12905
+ return request;
12906
+ }
12907
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
12908
+ if (!request) {
12909
+ return void 0;
12910
+ }
12911
+ return {
12912
+ ...request,
12913
+ input: buildNextOpenRouterCallModelInput(
12914
+ extractOpenRouterCallModelInput(request),
12915
+ isObject(currentResponse) ? currentResponse : {},
12916
+ toolResults
12917
+ ),
12918
+ stream: false
12919
+ };
12920
+ }
12921
+ function wrapAsyncIterableWithSpan(args) {
12922
+ return {
12923
+ [Symbol.asyncIterator]() {
12924
+ const iterator = args.iteratorFactory();
12925
+ return {
12926
+ next(value) {
12927
+ return withCurrent(
12928
+ args.span,
12929
+ () => value === void 0 ? iterator.next() : iterator.next(value)
12930
+ ).then(
12931
+ async (result) => {
12932
+ if (result.done) {
12933
+ await args.finalize();
12934
+ }
12935
+ return result;
12936
+ },
12937
+ (error) => {
12938
+ args.onError(error);
12939
+ throw error;
12940
+ }
12941
+ );
12942
+ },
12943
+ return(value) {
12944
+ if (typeof iterator.return !== "function") {
12945
+ return args.finalize().then(() => ({
12946
+ done: true,
12947
+ value
12948
+ }));
12949
+ }
12950
+ return withCurrent(args.span, () => iterator.return(value)).then(
12951
+ async (result) => {
12952
+ await args.finalize();
12953
+ return result;
12954
+ },
12955
+ (error) => {
12956
+ args.onError(error);
12957
+ throw error;
12958
+ }
12959
+ );
12960
+ },
12961
+ throw(error) {
12962
+ args.onError(error);
12963
+ if (typeof iterator.throw !== "function") {
12964
+ return Promise.reject(error);
12965
+ }
12966
+ return withCurrent(args.span, () => iterator.throw(error));
12967
+ },
12968
+ [Symbol.asyncIterator]() {
12969
+ return this;
12970
+ }
12971
+ };
12972
+ }
12973
+ };
12974
+ }
12975
+ function isAsyncIterable2(value) {
12976
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12977
+ }
12978
+ function isPromiseLike(value) {
12979
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12980
+ }
12981
+ function normalizeError(error) {
12982
+ return error instanceof Error ? error : new Error(String(error));
12983
+ }
12984
+
12985
+ // src/instrumentation/plugins/openrouter-plugin.ts
12986
+ var OpenRouterPlugin = class extends BasePlugin {
12987
+ onEnable() {
12988
+ this.subscribeToOpenRouterChannels();
12989
+ }
12990
+ onDisable() {
12991
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
12992
+ }
12993
+ subscribeToOpenRouterChannels() {
12994
+ this.unsubscribers.push(
12995
+ traceStreamingChannel(openRouterChannels.chatSend, {
12996
+ name: "openrouter.chat.send",
12997
+ type: "llm" /* LLM */,
12998
+ extractInput: (args) => {
12999
+ const request = getOpenRouterRequestArg(args);
13000
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
13001
+ const httpReferer = request?.httpReferer;
13002
+ const xTitle = request?.xTitle;
13003
+ const { messages, ...metadata } = chatGenerationParams;
13004
+ return {
13005
+ input: messages,
13006
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13007
+ };
13008
+ },
13009
+ extractOutput: (result) => {
13010
+ return isObject(result) ? result.choices : void 0;
13011
+ },
13012
+ extractMetrics: (result, startTime) => {
13013
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13014
+ if (startTime) {
13015
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13016
+ }
13017
+ return metrics;
13018
+ },
13019
+ aggregateChunks: aggregateOpenRouterChatChunks
13020
+ })
13021
+ );
13022
+ this.unsubscribers.push(
13023
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
13024
+ name: "openrouter.embeddings.generate",
13025
+ type: "llm" /* LLM */,
13026
+ extractInput: (args) => {
13027
+ const request = getOpenRouterRequestArg(args);
13028
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
13029
+ const httpReferer = request?.httpReferer;
13030
+ const xTitle = request?.xTitle;
13031
+ const { input, ...metadata } = requestBody;
13032
+ return {
13033
+ input,
13034
+ metadata: buildOpenRouterEmbeddingMetadata(
13035
+ metadata,
13036
+ httpReferer,
13037
+ xTitle
13038
+ )
13039
+ };
13040
+ },
13041
+ extractOutput: (result) => {
13042
+ if (!isObject(result)) {
13043
+ return void 0;
13044
+ }
13045
+ const embedding = result.data?.[0]?.embedding;
13046
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
13047
+ },
13048
+ extractMetadata: (result) => {
13049
+ if (!isObject(result)) {
13050
+ return void 0;
13051
+ }
13052
+ return extractOpenRouterResponseMetadata(result);
13053
+ },
13054
+ extractMetrics: (result) => {
13055
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
13056
+ }
13057
+ })
13058
+ );
13059
+ this.unsubscribers.push(
13060
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
13061
+ name: "openrouter.beta.responses.send",
13062
+ type: "llm" /* LLM */,
13063
+ extractInput: (args) => {
13064
+ const request = getOpenRouterRequestArg(args);
13065
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
13066
+ const httpReferer = request?.httpReferer;
13067
+ const xTitle = request?.xTitle;
13068
+ const { input, ...metadata } = openResponsesRequest;
13069
+ return {
13070
+ input,
13071
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13072
+ };
13073
+ },
13074
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
13075
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13076
+ extractMetrics: (result, startTime) => {
13077
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13078
+ if (startTime) {
13079
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13080
+ }
13081
+ return metrics;
13082
+ },
13083
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
13084
+ })
13085
+ );
13086
+ this.unsubscribers.push(
13087
+ traceSyncStreamChannel(openRouterChannels.callModel, {
13088
+ name: "openrouter.callModel",
13089
+ type: "llm" /* LLM */,
13090
+ extractInput: (args) => {
13091
+ const request = getOpenRouterCallModelRequestArg(args);
13092
+ return {
13093
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
13094
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13095
+ };
13096
+ },
13097
+ patchResult: ({ endEvent, result, span }) => {
13098
+ return patchOpenRouterCallModelResult(
13099
+ span,
13100
+ result,
13101
+ getOpenRouterCallModelRequestArg(endEvent.arguments)
13102
+ );
13103
+ }
13104
+ })
13105
+ );
13106
+ this.unsubscribers.push(
13107
+ traceStreamingChannel(openRouterChannels.toolExecute, {
13108
+ name: "openrouter.tool",
13109
+ type: "tool" /* TOOL */,
13110
+ extractInput: (args, event) => ({
13111
+ input: args[0],
13112
+ metadata: {
13113
+ provider: "openrouter",
13114
+ tool_name: event.toolName,
13115
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
13116
+ }
13117
+ }),
13118
+ extractOutput: (result) => result,
13119
+ extractMetrics: () => ({}),
13120
+ aggregateChunks: (chunks) => ({
13121
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
13122
+ metrics: {}
13123
+ })
13124
+ })
13125
+ );
13126
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
13127
+ const callModelHandlers = {
13128
+ start: (event) => {
13129
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
13130
+ if (!request) {
13131
+ return;
13132
+ }
13133
+ patchOpenRouterCallModelRequestTools(request);
13134
+ }
13135
+ };
13136
+ callModelChannel.subscribe(callModelHandlers);
13137
+ this.unsubscribers.push(() => {
13138
+ callModelChannel.unsubscribe(callModelHandlers);
13139
+ });
13140
+ }
13141
+ };
13142
+ function normalizeArgs(args) {
13143
+ if (Array.isArray(args)) {
13144
+ return args;
13145
+ }
13146
+ if (isArrayLike(args)) {
13147
+ return Array.from(args);
13148
+ }
13149
+ return [args];
13150
+ }
13151
+ function isArrayLike(value) {
13152
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13153
+ }
13154
+ function getOpenRouterRequestArg(args) {
13155
+ const normalizedArgs = normalizeArgs(args);
13156
+ const keyedCandidate = normalizedArgs.find(
13157
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13158
+ );
13159
+ if (isObject(keyedCandidate)) {
13160
+ return keyedCandidate;
13161
+ }
13162
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
13163
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
13164
+ }
13165
+ function getOpenRouterCallModelRequestArg(args) {
13166
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13167
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
13168
+ }
13169
+ function aggregateOpenRouterChatChunks(chunks) {
13170
+ let role;
13171
+ let content = "";
13172
+ let toolCalls;
13173
+ let finishReason;
13174
+ let metrics = {};
13175
+ for (const chunk of chunks) {
13176
+ metrics = {
13177
+ ...metrics,
13178
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
13179
+ };
13180
+ const choice = chunk?.choices?.[0];
13181
+ const delta = choice?.delta;
13182
+ if (!delta) {
13183
+ if (choice?.finish_reason !== void 0) {
13184
+ finishReason = choice.finish_reason;
13185
+ }
13186
+ continue;
13187
+ }
13188
+ if (!role && delta.role) {
13189
+ role = delta.role;
13190
+ }
13191
+ if (typeof delta.content === "string") {
13192
+ content += delta.content;
13193
+ }
13194
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
13195
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
13196
+ if (choiceFinishReason !== void 0) {
13197
+ finishReason = choiceFinishReason;
13198
+ } else if (deltaFinishReason !== void 0) {
13199
+ finishReason = deltaFinishReason;
13200
+ }
13201
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
13202
+ if (!toolCallDeltas) {
13203
+ continue;
13204
+ }
13205
+ for (const toolDelta of toolCallDeltas) {
13206
+ if (!toolDelta?.function) {
13207
+ continue;
13208
+ }
13209
+ const toolIndex = toolDelta.index ?? 0;
13210
+ const existingToolCall = toolCalls?.[toolIndex];
13211
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
13212
+ const nextToolCalls = [...toolCalls || []];
13213
+ nextToolCalls[toolIndex] = {
13214
+ index: toolIndex,
13215
+ id: toolDelta.id,
13216
+ type: toolDelta.type,
13217
+ function: {
13218
+ name: toolDelta.function.name,
13219
+ arguments: toolDelta.function.arguments || ""
13220
+ }
13221
+ };
13222
+ toolCalls = nextToolCalls;
13223
+ continue;
13224
+ }
13225
+ const current = existingToolCall;
13226
+ if (toolDelta.id && !current.id) {
13227
+ current.id = toolDelta.id;
13228
+ }
13229
+ if (toolDelta.type && !current.type) {
13230
+ current.type = toolDelta.type;
13231
+ }
13232
+ if (toolDelta.function.name && !current.function.name) {
13233
+ current.function.name = toolDelta.function.name;
13234
+ }
13235
+ current.function.arguments += toolDelta.function.arguments || "";
13236
+ }
13237
+ }
13238
+ return {
13239
+ output: [
13240
+ {
13241
+ index: 0,
13242
+ message: {
13243
+ role,
13244
+ content: content || void 0,
13245
+ ...toolCalls ? { tool_calls: toolCalls } : {}
13246
+ },
13247
+ logprobs: null,
13248
+ finish_reason: finishReason
13249
+ }
13250
+ ],
13251
+ metrics
13252
+ };
13253
+ }
13254
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
13255
+ let finalResponse;
13256
+ for (const chunk of chunks) {
13257
+ const response = chunk?.response;
13258
+ if (!response) {
13259
+ continue;
13260
+ }
13261
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
13262
+ finalResponse = response;
13263
+ }
13264
+ }
13265
+ if (!finalResponse) {
13266
+ return {
13267
+ output: void 0,
13268
+ metrics: {}
13269
+ };
13270
+ }
13271
+ return {
13272
+ output: extractOpenRouterResponseOutput(finalResponse),
13273
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
13274
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
13275
+ };
13276
+ }
13277
+
12147
13278
  // src/instrumentation/braintrust-plugin.ts
12148
13279
  var BraintrustPlugin = class extends BasePlugin {
12149
13280
  config;
@@ -12152,6 +13283,7 @@ var BraintrustPlugin = class extends BasePlugin {
12152
13283
  aiSDKPlugin = null;
12153
13284
  claudeAgentSDKPlugin = null;
12154
13285
  googleGenAIPlugin = null;
13286
+ openRouterPlugin = null;
12155
13287
  constructor(config = {}) {
12156
13288
  super();
12157
13289
  this.config = config;
@@ -12178,6 +13310,10 @@ var BraintrustPlugin = class extends BasePlugin {
12178
13310
  this.googleGenAIPlugin = new GoogleGenAIPlugin();
12179
13311
  this.googleGenAIPlugin.enable();
12180
13312
  }
13313
+ if (integrations.openrouter !== false) {
13314
+ this.openRouterPlugin = new OpenRouterPlugin();
13315
+ this.openRouterPlugin.enable();
13316
+ }
12181
13317
  }
12182
13318
  onDisable() {
12183
13319
  if (this.openaiPlugin) {
@@ -12200,6 +13336,10 @@ var BraintrustPlugin = class extends BasePlugin {
12200
13336
  this.googleGenAIPlugin.disable();
12201
13337
  this.googleGenAIPlugin = null;
12202
13338
  }
13339
+ if (this.openRouterPlugin) {
13340
+ this.openRouterPlugin.disable();
13341
+ this.openRouterPlugin = null;
13342
+ }
12203
13343
  }
12204
13344
  };
12205
13345
 
@@ -12271,7 +13411,8 @@ var PluginRegistry = class {
12271
13411
  vercel: true,
12272
13412
  aisdk: true,
12273
13413
  google: true,
12274
- claudeAgentSDK: true
13414
+ claudeAgentSDK: true,
13415
+ openrouter: true
12275
13416
  };
12276
13417
  }
12277
13418
  /**
@@ -12301,6 +13442,7 @@ function configureNode() {
12301
13442
  isomorph_default.getCallerLocation = getCallerLocation;
12302
13443
  isomorph_default.newAsyncLocalStorage = () => new AsyncLocalStorage();
12303
13444
  isomorph_default.newTracingChannel = (nameOrChannels) => diagnostics_channel.tracingChannel(nameOrChannels);
13445
+ patchTracingChannel(diagnostics_channel.tracingChannel);
12304
13446
  isomorph_default.processOn = (event, handler) => {
12305
13447
  process.on(event, handler);
12306
13448
  };
@@ -12405,7 +13547,7 @@ function isAsync(fn) {
12405
13547
  function isAsyncGenerator2(fn) {
12406
13548
  return fn[Symbol.toStringTag] === "AsyncGenerator";
12407
13549
  }
12408
- function isAsyncIterable2(obj) {
13550
+ function isAsyncIterable3(obj) {
12409
13551
  return typeof obj[Symbol.asyncIterator] === "function";
12410
13552
  }
12411
13553
  function wrapAsync(asyncFn) {
@@ -12455,7 +13597,7 @@ function _asyncMap(eachfn, arr, iteratee, callback) {
12455
13597
  callback(err, results);
12456
13598
  });
12457
13599
  }
12458
- function isArrayLike(value) {
13600
+ function isArrayLike2(value) {
12459
13601
  return value && typeof value.length === "number" && value.length >= 0 && value.length % 1 === 0;
12460
13602
  }
12461
13603
  var breakLoop = {};
@@ -12503,7 +13645,7 @@ function createObjectIterator(obj) {
12503
13645
  };
12504
13646
  }
12505
13647
  function createIterator(coll) {
12506
- if (isArrayLike(coll)) {
13648
+ if (isArrayLike2(coll)) {
12507
13649
  return createArrayIterator(coll);
12508
13650
  }
12509
13651
  var iterator = getIterator(coll);
@@ -12577,7 +13719,7 @@ var eachOfLimit$2 = (limit) => {
12577
13719
  if (isAsyncGenerator2(obj)) {
12578
13720
  return asyncEachOfLimit(obj, limit, iteratee, callback);
12579
13721
  }
12580
- if (isAsyncIterable2(obj)) {
13722
+ if (isAsyncIterable3(obj)) {
12581
13723
  return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
12582
13724
  }
12583
13725
  var nextElem = createIterator(obj);
@@ -12649,7 +13791,7 @@ function eachOfGeneric(coll, iteratee, callback) {
12649
13791
  return eachOfLimit$1(coll, Infinity, iteratee, callback);
12650
13792
  }
12651
13793
  function eachOf(coll, iteratee, callback) {
12652
- var eachOfImplementation = isArrayLike(coll) ? eachOfArrayLike : eachOfGeneric;
13794
+ var eachOfImplementation = isArrayLike2(coll) ? eachOfArrayLike : eachOfGeneric;
12653
13795
  return eachOfImplementation(coll, wrapAsync(iteratee), callback);
12654
13796
  }
12655
13797
  var eachOf$1 = awaitify(eachOf, 3);
@@ -13164,7 +14306,7 @@ function filterGeneric(eachfn, coll, iteratee, callback) {
13164
14306
  });
13165
14307
  }
13166
14308
  function _filter(eachfn, coll, iteratee, callback) {
13167
- var filter2 = isArrayLike(coll) ? filterArray : filterGeneric;
14309
+ var filter2 = isArrayLike2(coll) ? filterArray : filterGeneric;
13168
14310
  return filter2(eachfn, coll, wrapAsync(iteratee), callback);
13169
14311
  }
13170
14312
  function filter(coll, iteratee, callback) {
@@ -13239,7 +14381,7 @@ if (hasNextTick) {
13239
14381
  }
13240
14382
  var nextTick = wrap(_defer);
13241
14383
  var _parallel = awaitify((eachfn, tasks, callback) => {
13242
- var results = isArrayLike(tasks) ? [] : {};
14384
+ var results = isArrayLike2(tasks) ? [] : {};
13243
14385
  eachfn(tasks, (task, key, taskCb) => {
13244
14386
  wrapAsync(task)((err, ...result) => {
13245
14387
  if (result.length < 2) {
@@ -13920,7 +15062,7 @@ function callEvaluatorData(data) {
13920
15062
  baseExperiment
13921
15063
  };
13922
15064
  }
13923
- function isAsyncIterable3(value) {
15065
+ function isAsyncIterable4(value) {
13924
15066
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
13925
15067
  }
13926
15068
  function isIterable(value) {
@@ -14125,7 +15267,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
14125
15267
  }
14126
15268
  const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
14127
15269
  const dataIterable = (() => {
14128
- if (isAsyncIterable3(resolvedDataResult)) {
15270
+ if (isAsyncIterable4(resolvedDataResult)) {
14129
15271
  return resolvedDataResult;
14130
15272
  }
14131
15273
  if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {