braintrust 3.6.0 → 3.7.1

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 (33) hide show
  1. package/dev/dist/index.js +2692 -1472
  2. package/dev/dist/index.mjs +2616 -1396
  3. package/dist/auto-instrumentations/bundler/esbuild.cjs +46 -21
  4. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  5. package/dist/auto-instrumentations/bundler/rollup.cjs +46 -21
  6. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/vite.cjs +46 -21
  8. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +952 -0
  10. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +53 -0
  11. package/dist/auto-instrumentations/bundler/webpack.cjs +46 -21
  12. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  13. package/dist/auto-instrumentations/{chunk-WOUC73KB.mjs → chunk-NY4CGTN6.mjs} +1 -1
  14. package/dist/auto-instrumentations/{chunk-F7WAXFNM.mjs → chunk-YCKND42U.mjs} +46 -21
  15. package/dist/auto-instrumentations/hook.mjs +77 -26
  16. package/dist/auto-instrumentations/index.cjs +46 -21
  17. package/dist/auto-instrumentations/index.mjs +1 -1
  18. package/dist/browser.d.mts +8 -30
  19. package/dist/browser.d.ts +8 -30
  20. package/dist/browser.js +5051 -6344
  21. package/dist/browser.mjs +5051 -6344
  22. package/dist/cli.js +2622 -1398
  23. package/dist/edge-light.js +9456 -10773
  24. package/dist/edge-light.mjs +9456 -10773
  25. package/dist/index.d.mts +8 -30
  26. package/dist/index.d.ts +8 -30
  27. package/dist/index.js +5078 -6371
  28. package/dist/index.mjs +4870 -6163
  29. package/dist/instrumentation/index.js +2491 -1319
  30. package/dist/instrumentation/index.mjs +2491 -1319
  31. package/dist/workerd.js +9456 -10773
  32. package/dist/workerd.mjs +9456 -10773
  33. package/package.json +6 -2
@@ -111,7 +111,7 @@ var DefaultTracingChannel = class {
111
111
  }
112
112
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
113
  tracePromise(fn, _message, thisArg, ...args) {
114
- return Promise.resolve(fn.apply(thisArg, args));
114
+ return fn.apply(thisArg, args);
115
115
  }
116
116
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
117
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -131,6 +131,7 @@ var iso = {
131
131
  processOn: (_0, _1) => {
132
132
  },
133
133
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
134
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
134
135
  writeln: (text) => console.log(text)
135
136
  };
136
137
  var isomorph_default = iso;
@@ -166,7 +167,7 @@ function patchStreamIfNeeded(stream, options) {
166
167
  if (!completed) {
167
168
  completed = true;
168
169
  try {
169
- options.onComplete(chunks);
170
+ await options.onComplete(chunks);
170
171
  } catch (error) {
171
172
  console.error("Error in stream onComplete handler:", error);
172
173
  }
@@ -178,7 +179,7 @@ function patchStreamIfNeeded(stream, options) {
178
179
  chunks.push(chunk);
179
180
  if (options.onChunk) {
180
181
  try {
181
- options.onChunk(chunk);
182
+ await options.onChunk(chunk);
182
183
  } catch (error) {
183
184
  console.error("Error in stream onChunk handler:", error);
184
185
  }
@@ -191,7 +192,7 @@ function patchStreamIfNeeded(stream, options) {
191
192
  completed = true;
192
193
  if (options.onError) {
193
194
  try {
194
- options.onError(
195
+ await options.onError(
195
196
  error instanceof Error ? error : new Error(String(error)),
196
197
  chunks
197
198
  );
@@ -209,7 +210,7 @@ function patchStreamIfNeeded(stream, options) {
209
210
  if (!completed) {
210
211
  completed = true;
211
212
  try {
212
- options.onComplete(chunks);
213
+ await options.onComplete(chunks);
213
214
  } catch (error) {
214
215
  console.error("Error in stream onComplete handler:", error);
215
216
  }
@@ -226,7 +227,7 @@ function patchStreamIfNeeded(stream, options) {
226
227
  const error = rawError instanceof Error ? rawError : new Error(String(rawError));
227
228
  if (options.onError) {
228
229
  try {
229
- options.onError(error, chunks);
230
+ await options.onError(error, chunks);
230
231
  } catch (handlerError) {
231
232
  console.error("Error in stream onError handler:", handlerError);
232
233
  }
@@ -1578,6 +1579,15 @@ function addAzureBlobHeaders(headers, url) {
1578
1579
  headers["x-ms-blob-type"] = "BlockBlob";
1579
1580
  }
1580
1581
  }
1582
+ function filterFrom(record, keys) {
1583
+ const out = {};
1584
+ for (const k of Object.keys(record)) {
1585
+ if (!keys.includes(k)) {
1586
+ out[k] = record[k];
1587
+ }
1588
+ }
1589
+ return out;
1590
+ }
1581
1591
 
1582
1592
  // src/generated_types.ts
1583
1593
  var import_v36 = require("zod/v3");
@@ -8371,51 +8381,28 @@ function traceSyncStreamChannel(channel2, config) {
8371
8381
  }
8372
8382
  const { span, startTime } = spanData;
8373
8383
  const endEvent = event;
8374
- if (config.patchResult?.({
8375
- channelName,
8376
- endEvent,
8377
- result: endEvent.result,
8378
- span,
8379
- startTime
8380
- })) {
8381
- return;
8382
- }
8383
- const stream = endEvent.result;
8384
- if (!isSyncStreamLike(stream)) {
8385
- span.end();
8386
- states.delete(event);
8387
- return;
8388
- }
8389
- let first = true;
8390
- stream.on("chunk", () => {
8391
- if (first) {
8392
- span.log({
8393
- metrics: {
8394
- time_to_first_token: getCurrentUnixTimestamp() - startTime
8395
- }
8396
- });
8397
- first = false;
8398
- }
8399
- });
8400
- stream.on("chatCompletion", (completion) => {
8401
- try {
8402
- if (hasChoices(completion)) {
8403
- span.log({
8404
- output: completion.choices
8405
- });
8406
- }
8407
- } catch (error) {
8408
- console.error(
8409
- `Error extracting chatCompletion for ${channelName}:`,
8410
- error
8411
- );
8384
+ const handleResolvedResult = (result) => {
8385
+ const resolvedEndEvent = {
8386
+ ...endEvent,
8387
+ result
8388
+ };
8389
+ if (config.patchResult?.({
8390
+ channelName,
8391
+ endEvent: resolvedEndEvent,
8392
+ result,
8393
+ span,
8394
+ startTime
8395
+ })) {
8396
+ return;
8412
8397
  }
8413
- });
8414
- stream.on("event", (streamEvent) => {
8415
- if (!config.extractFromEvent) {
8398
+ const stream = result;
8399
+ if (!isSyncStreamLike(stream)) {
8400
+ span.end();
8401
+ states.delete(event);
8416
8402
  return;
8417
8403
  }
8418
- try {
8404
+ let first = true;
8405
+ stream.on("chunk", () => {
8419
8406
  if (first) {
8420
8407
  span.log({
8421
8408
  metrics: {
@@ -8424,25 +8411,55 @@ function traceSyncStreamChannel(channel2, config) {
8424
8411
  });
8425
8412
  first = false;
8426
8413
  }
8427
- const extracted = config.extractFromEvent(streamEvent);
8428
- if (extracted && Object.keys(extracted).length > 0) {
8429
- span.log(extracted);
8414
+ });
8415
+ stream.on("chatCompletion", (completion) => {
8416
+ try {
8417
+ if (hasChoices(completion)) {
8418
+ span.log({
8419
+ output: completion.choices
8420
+ });
8421
+ }
8422
+ } catch (error) {
8423
+ console.error(
8424
+ `Error extracting chatCompletion for ${channelName}:`,
8425
+ error
8426
+ );
8430
8427
  }
8431
- } catch (error) {
8432
- console.error(`Error extracting event for ${channelName}:`, error);
8433
- }
8434
- });
8435
- stream.on("end", () => {
8436
- span.end();
8437
- states.delete(event);
8438
- });
8439
- stream.on("error", (error) => {
8440
- span.log({
8441
- error: error.message
8442
8428
  });
8443
- span.end();
8444
- states.delete(event);
8445
- });
8429
+ stream.on("event", (streamEvent) => {
8430
+ if (!config.extractFromEvent) {
8431
+ return;
8432
+ }
8433
+ try {
8434
+ if (first) {
8435
+ span.log({
8436
+ metrics: {
8437
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8438
+ }
8439
+ });
8440
+ first = false;
8441
+ }
8442
+ const extracted = config.extractFromEvent(streamEvent);
8443
+ if (extracted && Object.keys(extracted).length > 0) {
8444
+ span.log(extracted);
8445
+ }
8446
+ } catch (error) {
8447
+ console.error(`Error extracting event for ${channelName}:`, error);
8448
+ }
8449
+ });
8450
+ stream.on("end", () => {
8451
+ span.end();
8452
+ states.delete(event);
8453
+ });
8454
+ stream.on("error", (error) => {
8455
+ span.log({
8456
+ error: error.message
8457
+ });
8458
+ span.end();
8459
+ states.delete(event);
8460
+ });
8461
+ };
8462
+ handleResolvedResult(endEvent.result);
8446
8463
  },
8447
8464
  error: (event) => {
8448
8465
  logErrorAndEnd(states, event);
@@ -9222,7 +9239,7 @@ var AnthropicPlugin = class extends BasePlugin {
9222
9239
  this.unsubscribers.push(
9223
9240
  traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
9224
9241
  ...anthropicConfig,
9225
- name: "anthropic.beta.messages.create"
9242
+ name: "anthropic.messages.create"
9226
9243
  })
9227
9244
  );
9228
9245
  }
@@ -9245,9 +9262,12 @@ function parseMetricsFromUsage2(usage) {
9245
9262
  return metrics;
9246
9263
  }
9247
9264
  function aggregateAnthropicStreamChunks(chunks) {
9248
- const deltas = [];
9265
+ const fallbackTextDeltas = [];
9266
+ const contentBlocks = {};
9267
+ const contentBlockDeltas = {};
9249
9268
  let metrics = {};
9250
9269
  let metadata = {};
9270
+ let role;
9251
9271
  for (const event of chunks) {
9252
9272
  switch (event?.type) {
9253
9273
  case "message_start":
@@ -9255,15 +9275,55 @@ function aggregateAnthropicStreamChunks(chunks) {
9255
9275
  const initialMetrics = parseMetricsFromUsage2(event.message.usage);
9256
9276
  metrics = { ...metrics, ...initialMetrics };
9257
9277
  }
9278
+ if (typeof event.message?.role === "string") {
9279
+ role = event.message.role;
9280
+ }
9281
+ break;
9282
+ case "content_block_start":
9283
+ if (event.content_block) {
9284
+ contentBlocks[event.index] = event.content_block;
9285
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
9286
+ }
9258
9287
  break;
9259
- case "content_block_delta":
9260
- if (event.delta?.type === "text_delta") {
9261
- const text = event.delta.text;
9288
+ case "content_block_delta": {
9289
+ const acc = contentBlockDeltas[event.index];
9290
+ const delta = event.delta;
9291
+ if (!delta) break;
9292
+ if (delta.type === "text_delta" && "text" in delta) {
9293
+ const text = delta.text;
9262
9294
  if (text) {
9263
- deltas.push(text);
9295
+ if (acc !== void 0) {
9296
+ acc.textDeltas.push(text);
9297
+ } else {
9298
+ fallbackTextDeltas.push(text);
9299
+ }
9300
+ }
9301
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
9302
+ const partialJson = delta.partial_json;
9303
+ if (partialJson && acc !== void 0) {
9304
+ acc.textDeltas.push(partialJson);
9305
+ }
9306
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
9307
+ const thinking = delta.thinking;
9308
+ if (thinking && acc !== void 0) {
9309
+ acc.textDeltas.push(thinking);
9310
+ }
9311
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
9312
+ const citation = delta.citation;
9313
+ if (citation && acc !== void 0) {
9314
+ acc.citations.push(citation);
9264
9315
  }
9265
9316
  }
9266
9317
  break;
9318
+ }
9319
+ case "content_block_stop":
9320
+ finalizeContentBlock(
9321
+ event.index,
9322
+ contentBlocks,
9323
+ contentBlockDeltas,
9324
+ fallbackTextDeltas
9325
+ );
9326
+ break;
9267
9327
  case "message_delta":
9268
9328
  if (event.usage) {
9269
9329
  const finalMetrics = parseMetricsFromUsage2(event.usage);
@@ -9275,7 +9335,21 @@ function aggregateAnthropicStreamChunks(chunks) {
9275
9335
  break;
9276
9336
  }
9277
9337
  }
9278
- const output = deltas.join("");
9338
+ const orderedContent = Object.entries(contentBlocks).map(([index, block]) => ({
9339
+ block,
9340
+ index: Number(index)
9341
+ })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
9342
+ let output = fallbackTextDeltas.join("");
9343
+ if (orderedContent.length > 0) {
9344
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
9345
+ output = orderedContent.map((block) => block.text).join("");
9346
+ } else {
9347
+ output = {
9348
+ ...role ? { role } : {},
9349
+ content: orderedContent
9350
+ };
9351
+ }
9352
+ }
9279
9353
  const finalized = finalizeAnthropicTokens(metrics);
9280
9354
  const filteredMetrics = Object.fromEntries(
9281
9355
  Object.entries(finalized).filter(
@@ -9288,6 +9362,61 @@ function aggregateAnthropicStreamChunks(chunks) {
9288
9362
  metadata
9289
9363
  };
9290
9364
  }
9365
+ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallbackTextDeltas) {
9366
+ const contentBlock = contentBlocks[index];
9367
+ if (!contentBlock) {
9368
+ return;
9369
+ }
9370
+ const acc = contentBlockDeltas[index];
9371
+ const text = acc?.textDeltas.join("") ?? "";
9372
+ if (isToolUseContentBlock(contentBlock)) {
9373
+ if (!text) {
9374
+ return;
9375
+ }
9376
+ try {
9377
+ contentBlocks[index] = {
9378
+ ...contentBlock,
9379
+ input: JSON.parse(text)
9380
+ };
9381
+ } catch {
9382
+ fallbackTextDeltas.push(text);
9383
+ delete contentBlocks[index];
9384
+ }
9385
+ return;
9386
+ }
9387
+ if (isTextContentBlock(contentBlock)) {
9388
+ if (!text) {
9389
+ delete contentBlocks[index];
9390
+ return;
9391
+ }
9392
+ const updated = { ...contentBlock, text };
9393
+ if (acc?.citations.length) {
9394
+ updated.citations = acc.citations;
9395
+ }
9396
+ contentBlocks[index] = updated;
9397
+ return;
9398
+ }
9399
+ if (isThinkingContentBlock(contentBlock)) {
9400
+ if (!text) {
9401
+ delete contentBlocks[index];
9402
+ return;
9403
+ }
9404
+ contentBlocks[index] = {
9405
+ ...contentBlock,
9406
+ thinking: text
9407
+ };
9408
+ return;
9409
+ }
9410
+ }
9411
+ function isTextContentBlock(contentBlock) {
9412
+ return contentBlock.type === "text";
9413
+ }
9414
+ function isToolUseContentBlock(contentBlock) {
9415
+ return contentBlock.type === "tool_use";
9416
+ }
9417
+ function isThinkingContentBlock(contentBlock) {
9418
+ return contentBlock.type === "thinking";
9419
+ }
9291
9420
  function isAnthropicBase64ContentBlock(input) {
9292
9421
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9293
9422
  }
@@ -9342,15 +9471,6 @@ function coalesceInput(messages, system) {
9342
9471
  }
9343
9472
  return input;
9344
9473
  }
9345
- function filterFrom(obj, fieldsToRemove) {
9346
- const result = {};
9347
- for (const [key, value] of Object.entries(obj)) {
9348
- if (!fieldsToRemove.includes(key)) {
9349
- result[key] = value;
9350
- }
9351
- }
9352
- return result;
9353
- }
9354
9474
 
9355
9475
  // src/wrappers/ai-sdk/normalize-logged-output.ts
9356
9476
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -9464,10 +9584,6 @@ var aiSDKChannels = defineChannels("ai", {
9464
9584
  channelName: "streamText",
9465
9585
  kind: "async"
9466
9586
  }),
9467
- streamTextSync: channel({
9468
- channelName: "streamText.sync",
9469
- kind: "sync-stream"
9470
- }),
9471
9587
  generateObject: channel({
9472
9588
  channelName: "generateObject",
9473
9589
  kind: "async"
@@ -9476,10 +9592,6 @@ var aiSDKChannels = defineChannels("ai", {
9476
9592
  channelName: "streamObject",
9477
9593
  kind: "async"
9478
9594
  }),
9479
- streamObjectSync: channel({
9480
- channelName: "streamObject.sync",
9481
- kind: "sync-stream"
9482
- }),
9483
9595
  agentGenerate: channel({
9484
9596
  channelName: "Agent.generate",
9485
9597
  kind: "async"
@@ -9515,6 +9627,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9515
9627
  ];
9516
9628
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
9517
9629
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
9630
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
9631
+ "braintrust.ai-sdk.deny-output-paths"
9632
+ );
9518
9633
  var AISDKPlugin = class extends BasePlugin {
9519
9634
  config;
9520
9635
  constructor(config = {}) {
@@ -9536,7 +9651,10 @@ var AISDKPlugin = class extends BasePlugin {
9536
9651
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9537
9652
  extractOutput: (result, endEvent) => {
9538
9653
  finalizeAISDKChildTracing(endEvent);
9539
- return processAISDKOutput(result, denyOutputPaths);
9654
+ return processAISDKOutput(
9655
+ result,
9656
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9657
+ );
9540
9658
  },
9541
9659
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9542
9660
  aggregateChunks: aggregateAISDKChunks
@@ -9547,25 +9665,14 @@ var AISDKPlugin = class extends BasePlugin {
9547
9665
  name: "streamText",
9548
9666
  type: "llm" /* LLM */,
9549
9667
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9550
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9668
+ extractOutput: (result, endEvent) => processAISDKOutput(
9669
+ result,
9670
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9671
+ ),
9551
9672
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9552
9673
  aggregateChunks: aggregateAISDKChunks,
9553
9674
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9554
- denyOutputPaths,
9555
- endEvent,
9556
- result,
9557
- span,
9558
- startTime
9559
- })
9560
- })
9561
- );
9562
- this.unsubscribers.push(
9563
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
9564
- name: "streamText",
9565
- type: "llm" /* LLM */,
9566
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9567
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9568
- denyOutputPaths,
9675
+ defaultDenyOutputPaths: denyOutputPaths,
9569
9676
  endEvent,
9570
9677
  result,
9571
9678
  span,
@@ -9580,7 +9687,10 @@ var AISDKPlugin = class extends BasePlugin {
9580
9687
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9581
9688
  extractOutput: (result, endEvent) => {
9582
9689
  finalizeAISDKChildTracing(endEvent);
9583
- return processAISDKOutput(result, denyOutputPaths);
9690
+ return processAISDKOutput(
9691
+ result,
9692
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9693
+ );
9584
9694
  },
9585
9695
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9586
9696
  aggregateChunks: aggregateAISDKChunks
@@ -9591,25 +9701,14 @@ var AISDKPlugin = class extends BasePlugin {
9591
9701
  name: "streamObject",
9592
9702
  type: "llm" /* LLM */,
9593
9703
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9594
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9704
+ extractOutput: (result, endEvent) => processAISDKOutput(
9705
+ result,
9706
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9707
+ ),
9595
9708
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9596
9709
  aggregateChunks: aggregateAISDKChunks,
9597
9710
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9598
- denyOutputPaths,
9599
- endEvent,
9600
- result,
9601
- span,
9602
- startTime
9603
- })
9604
- })
9605
- );
9606
- this.unsubscribers.push(
9607
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
9608
- name: "streamObject",
9609
- type: "llm" /* LLM */,
9610
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9611
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9612
- denyOutputPaths,
9711
+ defaultDenyOutputPaths: denyOutputPaths,
9613
9712
  endEvent,
9614
9713
  result,
9615
9714
  span,
@@ -9624,7 +9723,10 @@ var AISDKPlugin = class extends BasePlugin {
9624
9723
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9625
9724
  extractOutput: (result, endEvent) => {
9626
9725
  finalizeAISDKChildTracing(endEvent);
9627
- return processAISDKOutput(result, denyOutputPaths);
9726
+ return processAISDKOutput(
9727
+ result,
9728
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9729
+ );
9628
9730
  },
9629
9731
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9630
9732
  aggregateChunks: aggregateAISDKChunks
@@ -9635,11 +9737,14 @@ var AISDKPlugin = class extends BasePlugin {
9635
9737
  name: "Agent.stream",
9636
9738
  type: "llm" /* LLM */,
9637
9739
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9638
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9740
+ extractOutput: (result, endEvent) => processAISDKOutput(
9741
+ result,
9742
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9743
+ ),
9639
9744
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9640
9745
  aggregateChunks: aggregateAISDKChunks,
9641
9746
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9642
- denyOutputPaths,
9747
+ defaultDenyOutputPaths: denyOutputPaths,
9643
9748
  endEvent,
9644
9749
  result,
9645
9750
  span,
@@ -9654,7 +9759,10 @@ var AISDKPlugin = class extends BasePlugin {
9654
9759
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9655
9760
  extractOutput: (result, endEvent) => {
9656
9761
  finalizeAISDKChildTracing(endEvent);
9657
- return processAISDKOutput(result, denyOutputPaths);
9762
+ return processAISDKOutput(
9763
+ result,
9764
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9765
+ );
9658
9766
  },
9659
9767
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9660
9768
  aggregateChunks: aggregateAISDKChunks
@@ -9665,11 +9773,14 @@ var AISDKPlugin = class extends BasePlugin {
9665
9773
  name: "ToolLoopAgent.stream",
9666
9774
  type: "llm" /* LLM */,
9667
9775
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9668
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9776
+ extractOutput: (result, endEvent) => processAISDKOutput(
9777
+ result,
9778
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9779
+ ),
9669
9780
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9670
9781
  aggregateChunks: aggregateAISDKChunks,
9671
9782
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9672
- denyOutputPaths,
9783
+ defaultDenyOutputPaths: denyOutputPaths,
9673
9784
  endEvent,
9674
9785
  result,
9675
9786
  span,
@@ -9679,80 +9790,378 @@ var AISDKPlugin = class extends BasePlugin {
9679
9790
  );
9680
9791
  }
9681
9792
  };
9682
- function processAISDKInput(params) {
9683
- if (!params) return params;
9684
- const input = processInputAttachments(params);
9685
- if (!input || typeof input !== "object" || Array.isArray(input)) {
9686
- return input;
9793
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
9794
+ if (Array.isArray(event?.denyOutputPaths)) {
9795
+ return event.denyOutputPaths;
9687
9796
  }
9688
- const { tools: _tools, ...rest } = input;
9689
- return rest;
9690
- }
9691
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
9692
- const input = processAISDKInput(params);
9693
- const metadata = extractMetadataFromParams(params, event.self);
9694
- const childTracing = prepareAISDKChildTracing(
9695
- params,
9696
- event.self,
9697
- span,
9698
- denyOutputPaths
9699
- );
9700
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
9701
- if (childTracing.cleanup) {
9702
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
9797
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
9798
+ if (!firstArgument || typeof firstArgument !== "object") {
9799
+ return defaultDenyOutputPaths;
9703
9800
  }
9704
- return {
9705
- input,
9706
- metadata
9707
- };
9708
- }
9709
- function extractTopLevelAISDKMetrics(result, event, startTime) {
9710
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
9711
- if (startTime) {
9712
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9801
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
9802
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
9803
+ return runtimeDenyOutputPaths;
9713
9804
  }
9714
- return metrics;
9715
- }
9716
- function hasModelChildTracing(event) {
9717
- return event?.__braintrust_ai_sdk_model_wrapped === true;
9805
+ return defaultDenyOutputPaths;
9718
9806
  }
9719
- function extractMetadataFromParams(params, self) {
9720
- const metadata = {
9721
- braintrust: {
9722
- integration_name: "ai-sdk",
9723
- sdk_language: "typescript"
9807
+ var isZodSchema2 = (value) => {
9808
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
9809
+ };
9810
+ var serializeZodSchema2 = (schema) => {
9811
+ try {
9812
+ return zodToJsonSchema(schema);
9813
+ } catch {
9814
+ return {
9815
+ type: "object",
9816
+ description: "Zod schema (conversion failed)"
9817
+ };
9818
+ }
9819
+ };
9820
+ var isOutputObject = (value) => {
9821
+ if (value == null || typeof value !== "object") {
9822
+ return false;
9823
+ }
9824
+ const output = value;
9825
+ if (!("responseFormat" in output)) {
9826
+ return false;
9827
+ }
9828
+ if (output.type === "object" || output.type === "text") {
9829
+ return true;
9830
+ }
9831
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
9832
+ return true;
9833
+ }
9834
+ return false;
9835
+ };
9836
+ var serializeOutputObject = (output, model) => {
9837
+ try {
9838
+ const result = {
9839
+ response_format: null
9840
+ };
9841
+ if (output.type) {
9842
+ result.type = output.type;
9843
+ }
9844
+ let responseFormat;
9845
+ if (typeof output.responseFormat === "function") {
9846
+ const mockModelForSchema = {
9847
+ supportsStructuredOutputs: true,
9848
+ ...model && typeof model === "object" ? model : {}
9849
+ };
9850
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
9851
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
9852
+ responseFormat = output.responseFormat;
9853
+ }
9854
+ if (responseFormat) {
9855
+ if (typeof responseFormat.then === "function") {
9856
+ result.response_format = Promise.resolve(responseFormat).then(
9857
+ (resolved) => {
9858
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
9859
+ return {
9860
+ ...resolved,
9861
+ schema: serializeZodSchema2(resolved.schema)
9862
+ };
9863
+ }
9864
+ return resolved;
9865
+ }
9866
+ );
9867
+ } else {
9868
+ const syncResponseFormat = responseFormat;
9869
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
9870
+ responseFormat = {
9871
+ ...syncResponseFormat,
9872
+ schema: serializeZodSchema2(syncResponseFormat.schema)
9873
+ };
9874
+ }
9875
+ result.response_format = responseFormat;
9876
+ }
9724
9877
  }
9725
- };
9726
- const agentModel = self && typeof self === "object" && "model" in self && self.model ? self.model : self && typeof self === "object" && "settings" in self && self.settings?.model ? self.settings?.model : void 0;
9727
- const { model, provider } = serializeModelWithProvider(
9728
- params.model ?? agentModel
9729
- );
9730
- if (model) {
9731
- metadata.model = model;
9878
+ return result;
9879
+ } catch {
9880
+ return {
9881
+ response_format: null
9882
+ };
9732
9883
  }
9733
- if (provider) {
9734
- metadata.provider = provider;
9884
+ };
9885
+ var processInputAttachmentsSync = (input) => {
9886
+ if (!input) return { input };
9887
+ const processed = { ...input };
9888
+ if (input.messages && Array.isArray(input.messages)) {
9889
+ processed.messages = input.messages.map(processMessage);
9890
+ }
9891
+ if (input.prompt && typeof input.prompt === "object") {
9892
+ if (Array.isArray(input.prompt)) {
9893
+ processed.prompt = input.prompt.map(processMessage);
9894
+ } else {
9895
+ processed.prompt = processPromptContent(input.prompt);
9896
+ }
9735
9897
  }
9736
- const tools = serializeAISDKToolsForLogging(params.tools);
9737
- if (tools) {
9738
- metadata.tools = tools;
9898
+ if (input.schema && isZodSchema2(input.schema)) {
9899
+ processed.schema = serializeZodSchema2(input.schema);
9739
9900
  }
9740
- return metadata;
9741
- }
9742
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9743
- const cleanup = [];
9744
- const patchedModels = /* @__PURE__ */ new WeakSet();
9745
- const patchedTools = /* @__PURE__ */ new WeakSet();
9746
- let modelWrapped = false;
9747
- const patchModel = (model) => {
9748
- const resolvedModel = resolveAISDKModel(model);
9749
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9750
- return;
9901
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
9902
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
9903
+ }
9904
+ if (input.tools) {
9905
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
9906
+ }
9907
+ let outputPromise;
9908
+ if (input.output && isOutputObject(input.output)) {
9909
+ const serialized = serializeOutputObject(input.output, input.model);
9910
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
9911
+ processed.output = { ...serialized, response_format: {} };
9912
+ outputPromise = serialized.response_format.then(
9913
+ (resolvedFormat) => ({
9914
+ output: { ...serialized, response_format: resolvedFormat }
9915
+ })
9916
+ );
9917
+ } else {
9918
+ processed.output = serialized;
9751
9919
  }
9752
- patchedModels.add(resolvedModel);
9753
- resolvedModel[AUTO_PATCHED_MODEL] = true;
9754
- modelWrapped = true;
9755
- const originalDoGenerate = resolvedModel.doGenerate;
9920
+ }
9921
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
9922
+ processed.prepareCall = "[Function]";
9923
+ }
9924
+ return { input: processed, outputPromise };
9925
+ };
9926
+ var processMessage = (message) => {
9927
+ if (!message || typeof message !== "object") return message;
9928
+ if (Array.isArray(message.content)) {
9929
+ return {
9930
+ ...message,
9931
+ content: message.content.map(processContentPart)
9932
+ };
9933
+ }
9934
+ if (typeof message.content === "object" && message.content !== null) {
9935
+ return {
9936
+ ...message,
9937
+ content: processContentPart(message.content)
9938
+ };
9939
+ }
9940
+ return message;
9941
+ };
9942
+ var processPromptContent = (prompt) => {
9943
+ if (Array.isArray(prompt)) {
9944
+ return prompt.map(processContentPart);
9945
+ }
9946
+ if (prompt.content) {
9947
+ if (Array.isArray(prompt.content)) {
9948
+ return {
9949
+ ...prompt,
9950
+ content: prompt.content.map(processContentPart)
9951
+ };
9952
+ } else if (typeof prompt.content === "object") {
9953
+ return {
9954
+ ...prompt,
9955
+ content: processContentPart(prompt.content)
9956
+ };
9957
+ }
9958
+ }
9959
+ return prompt;
9960
+ };
9961
+ var processContentPart = (part) => {
9962
+ if (!part || typeof part !== "object") return part;
9963
+ try {
9964
+ if (part.type === "image" && part.image) {
9965
+ const imageAttachment = convertImageToAttachment(
9966
+ part.image,
9967
+ part.mimeType || part.mediaType
9968
+ );
9969
+ if (imageAttachment) {
9970
+ return {
9971
+ ...part,
9972
+ image: imageAttachment
9973
+ };
9974
+ }
9975
+ }
9976
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
9977
+ const fileAttachment = convertDataToAttachment(
9978
+ part.data,
9979
+ part.mimeType || part.mediaType,
9980
+ part.name || part.filename
9981
+ );
9982
+ if (fileAttachment) {
9983
+ return {
9984
+ ...part,
9985
+ data: fileAttachment
9986
+ };
9987
+ }
9988
+ }
9989
+ if (part.type === "image_url" && part.image_url) {
9990
+ if (typeof part.image_url === "object" && part.image_url.url) {
9991
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
9992
+ if (imageAttachment) {
9993
+ return {
9994
+ ...part,
9995
+ image_url: {
9996
+ ...part.image_url,
9997
+ url: imageAttachment
9998
+ }
9999
+ };
10000
+ }
10001
+ }
10002
+ }
10003
+ } catch (error) {
10004
+ console.warn("Error processing content part:", error);
10005
+ }
10006
+ return part;
10007
+ };
10008
+ var convertImageToAttachment = (image, explicitMimeType) => {
10009
+ try {
10010
+ if (typeof image === "string" && image.startsWith("data:")) {
10011
+ const [mimeTypeSection, base64Data] = image.split(",");
10012
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
10013
+ if (mimeType && base64Data) {
10014
+ const blob = convertDataToBlob(base64Data, mimeType);
10015
+ if (blob) {
10016
+ return new Attachment({
10017
+ data: blob,
10018
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
10019
+ contentType: mimeType
10020
+ });
10021
+ }
10022
+ }
10023
+ }
10024
+ if (explicitMimeType) {
10025
+ if (image instanceof Uint8Array) {
10026
+ return new Attachment({
10027
+ data: new Blob([image], { type: explicitMimeType }),
10028
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10029
+ contentType: explicitMimeType
10030
+ });
10031
+ }
10032
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
10033
+ return new Attachment({
10034
+ data: new Blob([image], { type: explicitMimeType }),
10035
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10036
+ contentType: explicitMimeType
10037
+ });
10038
+ }
10039
+ }
10040
+ if (image instanceof Blob && image.type) {
10041
+ return new Attachment({
10042
+ data: image,
10043
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
10044
+ contentType: image.type
10045
+ });
10046
+ }
10047
+ if (image instanceof Attachment) {
10048
+ return image;
10049
+ }
10050
+ } catch (error) {
10051
+ console.warn("Error converting image to attachment:", error);
10052
+ }
10053
+ return null;
10054
+ };
10055
+ var convertDataToAttachment = (data, mimeType, filename) => {
10056
+ if (!mimeType) return null;
10057
+ try {
10058
+ let blob = null;
10059
+ if (typeof data === "string" && data.startsWith("data:")) {
10060
+ const [, base64Data] = data.split(",");
10061
+ if (base64Data) {
10062
+ blob = convertDataToBlob(base64Data, mimeType);
10063
+ }
10064
+ } else if (typeof data === "string" && data.length > 0) {
10065
+ blob = convertDataToBlob(data, mimeType);
10066
+ } else if (data instanceof Uint8Array) {
10067
+ blob = new Blob([data], { type: mimeType });
10068
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
10069
+ blob = new Blob([data], { type: mimeType });
10070
+ } else if (data instanceof Blob) {
10071
+ blob = data;
10072
+ }
10073
+ if (blob) {
10074
+ return new Attachment({
10075
+ data: blob,
10076
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
10077
+ contentType: mimeType
10078
+ });
10079
+ }
10080
+ } catch (error) {
10081
+ console.warn("Error converting data to attachment:", error);
10082
+ }
10083
+ return null;
10084
+ };
10085
+ function processAISDKInput(params) {
10086
+ return processInputAttachmentsSync(params);
10087
+ }
10088
+ function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
10089
+ const { input, outputPromise } = processAISDKInput(params);
10090
+ if (outputPromise && input && typeof input === "object") {
10091
+ outputPromise.then((resolvedData) => {
10092
+ span.log({
10093
+ input: {
10094
+ ...input,
10095
+ ...resolvedData
10096
+ }
10097
+ });
10098
+ }).catch(() => {
10099
+ });
10100
+ }
10101
+ const metadata = extractMetadataFromParams(params, event.self);
10102
+ const childTracing = prepareAISDKChildTracing(
10103
+ params,
10104
+ event.self,
10105
+ span,
10106
+ defaultDenyOutputPaths,
10107
+ event.aiSDK
10108
+ );
10109
+ event.modelWrapped = childTracing.modelWrapped;
10110
+ if (childTracing.cleanup) {
10111
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
10112
+ }
10113
+ return {
10114
+ input,
10115
+ metadata
10116
+ };
10117
+ }
10118
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
10119
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
10120
+ if (startTime) {
10121
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10122
+ }
10123
+ return metrics;
10124
+ }
10125
+ function hasModelChildTracing(event) {
10126
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
10127
+ }
10128
+ function extractMetadataFromParams(params, self) {
10129
+ const metadata = {
10130
+ braintrust: {
10131
+ integration_name: "ai-sdk",
10132
+ sdk_language: "typescript"
10133
+ }
10134
+ };
10135
+ const agentModel = self && typeof self === "object" && "model" in self && self.model ? self.model : self && typeof self === "object" && "settings" in self && self.settings?.model ? self.settings?.model : void 0;
10136
+ const { model, provider } = serializeModelWithProvider(
10137
+ params.model ?? agentModel
10138
+ );
10139
+ if (model) {
10140
+ metadata.model = model;
10141
+ }
10142
+ if (provider) {
10143
+ metadata.provider = provider;
10144
+ }
10145
+ const tools = serializeAISDKToolsForLogging(params.tools);
10146
+ if (tools) {
10147
+ metadata.tools = tools;
10148
+ }
10149
+ return metadata;
10150
+ }
10151
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
10152
+ const cleanup = [];
10153
+ const patchedModels = /* @__PURE__ */ new WeakSet();
10154
+ const patchedTools = /* @__PURE__ */ new WeakSet();
10155
+ let modelWrapped = false;
10156
+ const patchModel = (model) => {
10157
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
10158
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
10159
+ return resolvedModel;
10160
+ }
10161
+ patchedModels.add(resolvedModel);
10162
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
10163
+ modelWrapped = true;
10164
+ const originalDoGenerate = resolvedModel.doGenerate;
9756
10165
  const originalDoStream = resolvedModel.doStream;
9757
10166
  const baseMetadata = buildAISDKChildMetadata(resolvedModel);
9758
10167
  resolvedModel.doGenerate = async function doGeneratePatched(options) {
@@ -9776,7 +10185,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9776
10185
  type: "llm" /* LLM */
9777
10186
  },
9778
10187
  event: {
9779
- input: processAISDKInput(options),
10188
+ input: processAISDKInput(options).input,
9780
10189
  metadata: baseMetadata
9781
10190
  }
9782
10191
  }
@@ -9790,7 +10199,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9790
10199
  type: "llm" /* LLM */
9791
10200
  },
9792
10201
  event: {
9793
- input: processAISDKInput(options),
10202
+ input: processAISDKInput(options).input,
9794
10203
  metadata: baseMetadata
9795
10204
  }
9796
10205
  });
@@ -9798,6 +10207,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9798
10207
  span,
9799
10208
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
9800
10209
  );
10210
+ const streamStartTime = getCurrentUnixTimestamp();
10211
+ let firstChunkTime;
9801
10212
  const output = {};
9802
10213
  let text = "";
9803
10214
  let reasoning = "";
@@ -9805,6 +10216,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9805
10216
  let object = void 0;
9806
10217
  const transformStream = new TransformStream({
9807
10218
  transform(chunk, controller) {
10219
+ if (firstChunkTime === void 0) {
10220
+ firstChunkTime = getCurrentUnixTimestamp();
10221
+ }
9808
10222
  switch (chunk.type) {
9809
10223
  case "text-delta":
9810
10224
  text += extractTextDelta(chunk);
@@ -9845,12 +10259,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9845
10259
  if (object !== void 0) {
9846
10260
  output.object = object;
9847
10261
  }
10262
+ const metrics = extractTokenMetrics(output);
10263
+ if (firstChunkTime !== void 0) {
10264
+ metrics.time_to_first_token = Math.max(
10265
+ firstChunkTime - streamStartTime,
10266
+ 1e-6
10267
+ );
10268
+ }
9848
10269
  span.log({
9849
10270
  output: processAISDKOutput(
9850
10271
  output,
9851
10272
  denyOutputPaths
9852
10273
  ),
9853
- metrics: extractTokenMetrics(output),
10274
+ metrics,
9854
10275
  ...buildResolvedMetadataPayload(output)
9855
10276
  });
9856
10277
  span.end();
@@ -9872,6 +10293,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9872
10293
  }
9873
10294
  delete resolvedModel[AUTO_PATCHED_MODEL];
9874
10295
  });
10296
+ return resolvedModel;
9875
10297
  };
9876
10298
  const patchTool = (tool, name) => {
9877
10299
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -9944,17 +10366,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9944
10366
  }
9945
10367
  };
9946
10368
  if (params && typeof params === "object") {
9947
- patchModel(params.model);
10369
+ const patchedParamModel = patchModel(params.model);
10370
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
10371
+ params.model = patchedParamModel;
10372
+ }
9948
10373
  patchTools(params.tools);
9949
10374
  }
9950
10375
  if (self && typeof self === "object") {
9951
10376
  const selfRecord = self;
9952
10377
  if (selfRecord.model !== void 0) {
9953
- patchModel(selfRecord.model);
10378
+ const patchedSelfModel = patchModel(selfRecord.model);
10379
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
10380
+ selfRecord.model = patchedSelfModel;
10381
+ }
9954
10382
  }
9955
10383
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
9956
10384
  if (selfRecord.settings.model !== void 0) {
9957
- patchModel(selfRecord.settings.model);
10385
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
10386
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
10387
+ selfRecord.settings.model = patchedSettingsModel;
10388
+ }
9958
10389
  }
9959
10390
  if (selfRecord.settings.tools !== void 0) {
9960
10391
  patchTools(selfRecord.settings.tools);
@@ -9978,63 +10409,173 @@ function finalizeAISDKChildTracing(event) {
9978
10409
  }
9979
10410
  }
9980
10411
  function patchAISDKStreamingResult(args) {
9981
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
10412
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
9982
10413
  if (!result || typeof result !== "object") {
9983
10414
  return false;
9984
10415
  }
9985
10416
  const resultRecord = result;
9986
- if (!isReadableStreamLike(resultRecord.baseStream)) {
10417
+ attachKnownResultPromiseHandlers(resultRecord);
10418
+ if (isReadableStreamLike(resultRecord.baseStream)) {
10419
+ let firstChunkTime2;
10420
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10421
+ new TransformStream({
10422
+ transform(chunk, controller) {
10423
+ if (firstChunkTime2 === void 0) {
10424
+ firstChunkTime2 = getCurrentUnixTimestamp();
10425
+ }
10426
+ controller.enqueue(chunk);
10427
+ },
10428
+ async flush() {
10429
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10430
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
10431
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
10432
+ }
10433
+ const output = await processAISDKStreamingOutput(
10434
+ result,
10435
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10436
+ );
10437
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10438
+ span.log({
10439
+ output,
10440
+ ...metadata ? { metadata } : {},
10441
+ metrics
10442
+ });
10443
+ finalizeAISDKChildTracing(endEvent);
10444
+ span.end();
10445
+ }
10446
+ })
10447
+ );
10448
+ Object.defineProperty(resultRecord, "baseStream", {
10449
+ configurable: true,
10450
+ enumerable: true,
10451
+ value: wrappedBaseStream,
10452
+ writable: true
10453
+ });
10454
+ return true;
10455
+ }
10456
+ const streamField = findAsyncIterableField(resultRecord, [
10457
+ "partialObjectStream",
10458
+ "textStream",
10459
+ "fullStream",
10460
+ "stream"
10461
+ ]);
10462
+ if (!streamField) {
9987
10463
  return false;
9988
10464
  }
9989
10465
  let firstChunkTime;
9990
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
9991
- new TransformStream({
9992
- transform(chunk, controller) {
9993
- if (firstChunkTime === void 0) {
9994
- firstChunkTime = getCurrentUnixTimestamp();
9995
- }
9996
- controller.enqueue(chunk);
9997
- },
9998
- async flush() {
9999
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10000
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10001
- metrics.time_to_first_token = firstChunkTime - startTime;
10002
- }
10003
- const output = await processAISDKStreamingOutput(
10004
- result,
10005
- denyOutputPaths
10006
- );
10007
- const metadata = buildResolvedMetadataPayload(result).metadata;
10008
- span.log({
10009
- output,
10010
- ...metadata ? { metadata } : {},
10011
- metrics
10012
- });
10013
- finalizeAISDKChildTracing(endEvent);
10014
- span.end();
10466
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
10467
+ onChunk: () => {
10468
+ if (firstChunkTime === void 0) {
10469
+ firstChunkTime = getCurrentUnixTimestamp();
10015
10470
  }
10016
- })
10017
- );
10018
- Object.defineProperty(resultRecord, "baseStream", {
10471
+ },
10472
+ onComplete: async () => {
10473
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10474
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10475
+ metrics.time_to_first_token = firstChunkTime - startTime;
10476
+ }
10477
+ const output = await processAISDKStreamingOutput(
10478
+ result,
10479
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10480
+ );
10481
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10482
+ span.log({
10483
+ output,
10484
+ ...metadata ? { metadata } : {},
10485
+ metrics
10486
+ });
10487
+ finalizeAISDKChildTracing(endEvent);
10488
+ span.end();
10489
+ },
10490
+ onError: (error) => {
10491
+ span.log({
10492
+ error: error.message
10493
+ });
10494
+ finalizeAISDKChildTracing(endEvent);
10495
+ span.end();
10496
+ }
10497
+ });
10498
+ Object.defineProperty(resultRecord, streamField.field, {
10019
10499
  configurable: true,
10020
10500
  enumerable: true,
10021
- value: wrappedBaseStream,
10501
+ value: wrappedStream,
10022
10502
  writable: true
10023
10503
  });
10024
10504
  return true;
10025
10505
  }
10506
+ function attachKnownResultPromiseHandlers(result) {
10507
+ const promiseLikeFields = [
10508
+ "content",
10509
+ "text",
10510
+ "object",
10511
+ "finishReason",
10512
+ "usage",
10513
+ "totalUsage",
10514
+ "steps"
10515
+ ];
10516
+ for (const field of promiseLikeFields) {
10517
+ try {
10518
+ if (!(field in result)) {
10519
+ continue;
10520
+ }
10521
+ const value = result[field];
10522
+ if (isPromiseLike(value)) {
10523
+ void Promise.resolve(value).catch(() => {
10524
+ });
10525
+ }
10526
+ } catch {
10527
+ }
10528
+ }
10529
+ }
10026
10530
  function isReadableStreamLike(value) {
10027
10531
  return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10028
10532
  }
10533
+ function isAsyncIterableLike(value) {
10534
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
10535
+ }
10536
+ function findAsyncIterableField(result, candidateFields) {
10537
+ for (const field of candidateFields) {
10538
+ try {
10539
+ const stream = result[field];
10540
+ if (isAsyncIterableLike(stream)) {
10541
+ return { field, stream };
10542
+ }
10543
+ } catch {
10544
+ }
10545
+ }
10546
+ return null;
10547
+ }
10548
+ function createPatchedAsyncIterable(stream, hooks) {
10549
+ return {
10550
+ async *[Symbol.asyncIterator]() {
10551
+ try {
10552
+ for await (const chunk of stream) {
10553
+ hooks.onChunk(chunk);
10554
+ yield chunk;
10555
+ }
10556
+ await hooks.onComplete();
10557
+ } catch (error) {
10558
+ hooks.onError(
10559
+ error instanceof Error ? error : new Error(String(error))
10560
+ );
10561
+ throw error;
10562
+ }
10563
+ }
10564
+ };
10565
+ }
10029
10566
  async function processAISDKStreamingOutput(result, denyOutputPaths) {
10030
10567
  const output = processAISDKOutput(result, denyOutputPaths);
10031
10568
  if (!output || typeof output !== "object") {
10032
10569
  return output;
10033
10570
  }
10034
10571
  const outputRecord = output;
10572
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
10035
10573
  try {
10036
- if ("text" in result && typeof result.text === "string") {
10037
- outputRecord.text = result.text;
10574
+ if (!isObjectStreamingResult && "text" in result) {
10575
+ const resolvedText = await Promise.resolve(result.text);
10576
+ if (typeof resolvedText === "string") {
10577
+ outputRecord.text = resolvedText;
10578
+ }
10038
10579
  }
10039
10580
  } catch {
10040
10581
  }
@@ -10047,6 +10588,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10047
10588
  }
10048
10589
  } catch {
10049
10590
  }
10591
+ try {
10592
+ if ("finishReason" in result) {
10593
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
10594
+ if (resolvedFinishReason !== void 0) {
10595
+ outputRecord.finishReason = resolvedFinishReason;
10596
+ }
10597
+ }
10598
+ } catch {
10599
+ }
10050
10600
  return outputRecord;
10051
10601
  }
10052
10602
  function buildAISDKChildMetadata(model) {
@@ -10069,16 +10619,25 @@ function buildResolvedMetadataPayload(result) {
10069
10619
  if (gatewayInfo?.model) {
10070
10620
  metadata.model = gatewayInfo.model;
10071
10621
  }
10072
- if (result.finishReason !== void 0) {
10073
- metadata.finish_reason = result.finishReason;
10622
+ let finishReason;
10623
+ try {
10624
+ finishReason = result.finishReason;
10625
+ } catch {
10626
+ finishReason = void 0;
10627
+ }
10628
+ if (isPromiseLike(finishReason)) {
10629
+ void Promise.resolve(finishReason).catch(() => {
10630
+ });
10631
+ } else if (finishReason !== void 0) {
10632
+ metadata.finish_reason = finishReason;
10074
10633
  }
10075
10634
  return Object.keys(metadata).length > 0 ? { metadata } : {};
10076
10635
  }
10077
- function resolveAISDKModel(model) {
10636
+ function resolveAISDKModel(model, aiSDK) {
10078
10637
  if (typeof model !== "string") {
10079
10638
  return model;
10080
10639
  }
10081
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
10640
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
10082
10641
  if (provider && typeof provider.languageModel === "function") {
10083
10642
  return provider.languageModel(model);
10084
10643
  }
@@ -10101,15 +10660,15 @@ function processAISDKOutput(output, denyOutputPaths) {
10101
10660
  }
10102
10661
  function extractTokenMetrics(result) {
10103
10662
  const metrics = {};
10104
- let usage = result?.totalUsage || result?.usage;
10105
- if (!usage && result) {
10106
- try {
10107
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10108
- usage = result.totalUsage;
10109
- } else if ("usage" in result && typeof result.usage !== "function") {
10110
- usage = result.usage;
10111
- }
10112
- } catch {
10663
+ let usage;
10664
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
10665
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
10666
+ usage = totalUsageValue;
10667
+ }
10668
+ if (!usage) {
10669
+ const usageValue = safeResultFieldRead(result, "usage");
10670
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
10671
+ usage = usageValue;
10113
10672
  }
10114
10673
  }
10115
10674
  if (!usage) {
@@ -10147,6 +10706,22 @@ function extractTokenMetrics(result) {
10147
10706
  }
10148
10707
  return metrics;
10149
10708
  }
10709
+ function safeResultFieldRead(result, field) {
10710
+ return safeSerializableFieldRead(result, field);
10711
+ }
10712
+ function safeSerializableFieldRead(obj, field) {
10713
+ try {
10714
+ const value = obj?.[field];
10715
+ if (isPromiseLike(value)) {
10716
+ void Promise.resolve(value).catch(() => {
10717
+ });
10718
+ return void 0;
10719
+ }
10720
+ return value;
10721
+ } catch {
10722
+ return void 0;
10723
+ }
10724
+ }
10150
10725
  function aggregateAISDKChunks(chunks, _result, endEvent) {
10151
10726
  const lastChunk = chunks[chunks.length - 1];
10152
10727
  const output = {};
@@ -10155,17 +10730,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10155
10730
  if (lastChunk) {
10156
10731
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
10157
10732
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
10158
- if (lastChunk.text !== void 0) {
10159
- output.text = lastChunk.text;
10733
+ const text = safeSerializableFieldRead(lastChunk, "text");
10734
+ if (text !== void 0) {
10735
+ output.text = text;
10160
10736
  }
10161
- if (lastChunk.object !== void 0) {
10162
- output.object = lastChunk.object;
10737
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
10738
+ if (objectValue !== void 0) {
10739
+ output.object = objectValue;
10163
10740
  }
10164
- if (lastChunk.finishReason !== void 0) {
10165
- output.finishReason = lastChunk.finishReason;
10741
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
10742
+ if (finishReason !== void 0) {
10743
+ output.finishReason = finishReason;
10166
10744
  }
10167
- if (lastChunk.toolCalls !== void 0) {
10168
- output.toolCalls = lastChunk.toolCalls;
10745
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
10746
+ if (toolCalls !== void 0) {
10747
+ output.toolCalls = toolCalls;
10169
10748
  }
10170
10749
  }
10171
10750
  finalizeAISDKChildTracing(endEvent);
@@ -10174,6 +10753,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10174
10753
  function extractGetterValues(obj) {
10175
10754
  const getterValues = {};
10176
10755
  const getterNames = [
10756
+ "content",
10177
10757
  "text",
10178
10758
  "object",
10179
10759
  "finishReason",
@@ -10189,8 +10769,17 @@ function extractGetterValues(obj) {
10189
10769
  ];
10190
10770
  for (const name of getterNames) {
10191
10771
  try {
10192
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
10193
- getterValues[name] = obj[name];
10772
+ if (!obj || !(name in obj)) {
10773
+ continue;
10774
+ }
10775
+ const value = obj[name];
10776
+ if (isPromiseLike(value)) {
10777
+ void Promise.resolve(value).catch(() => {
10778
+ });
10779
+ continue;
10780
+ }
10781
+ if (isSerializableOutputValue(value)) {
10782
+ getterValues[name] = value;
10194
10783
  }
10195
10784
  } catch {
10196
10785
  }
@@ -10212,6 +10801,11 @@ function extractSerializableOutputFields(output) {
10212
10801
  for (const name of directFieldNames) {
10213
10802
  try {
10214
10803
  const value = output?.[name];
10804
+ if (isPromiseLike(value)) {
10805
+ void Promise.resolve(value).catch(() => {
10806
+ });
10807
+ continue;
10808
+ }
10215
10809
  if (isSerializableOutputValue(value)) {
10216
10810
  serialized[name] = value;
10217
10811
  }
@@ -10223,6 +10817,9 @@ function extractSerializableOutputFields(output) {
10223
10817
  ...extractGetterValues(output)
10224
10818
  };
10225
10819
  }
10820
+ function isPromiseLike(value) {
10821
+ return value != null && typeof value === "object" && typeof value.then === "function";
10822
+ }
10226
10823
  function isSerializableOutputValue(value) {
10227
10824
  if (typeof value === "function") {
10228
10825
  return false;
@@ -10264,8 +10861,9 @@ function parseGatewayModelString(modelString) {
10264
10861
  return { model: modelString };
10265
10862
  }
10266
10863
  function extractGatewayRoutingInfo(result) {
10267
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10268
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
10864
+ const steps = safeSerializableFieldRead(result, "steps");
10865
+ if (Array.isArray(steps) && steps.length > 0) {
10866
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
10269
10867
  if (routing2) {
10270
10868
  return {
10271
10869
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -10273,7 +10871,11 @@ function extractGatewayRoutingInfo(result) {
10273
10871
  };
10274
10872
  }
10275
10873
  }
10276
- const routing = result?.providerMetadata?.gateway?.routing;
10874
+ const providerMetadata = safeSerializableFieldRead(
10875
+ result,
10876
+ "providerMetadata"
10877
+ );
10878
+ const routing = providerMetadata?.gateway?.routing;
10277
10879
  if (routing) {
10278
10880
  return {
10279
10881
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -10283,10 +10885,11 @@ function extractGatewayRoutingInfo(result) {
10283
10885
  return null;
10284
10886
  }
10285
10887
  function extractCostFromResult(result) {
10286
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10888
+ const steps = safeSerializableFieldRead(result, "steps");
10889
+ if (Array.isArray(steps) && steps.length > 0) {
10287
10890
  let totalCost = 0;
10288
10891
  let foundCost = false;
10289
- for (const step of result.steps) {
10892
+ for (const step of steps) {
10290
10893
  const gateway2 = step?.providerMetadata?.gateway;
10291
10894
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
10292
10895
  if (stepCost !== void 0 && stepCost > 0) {
@@ -10298,7 +10901,11 @@ function extractCostFromResult(result) {
10298
10901
  return totalCost;
10299
10902
  }
10300
10903
  }
10301
- const gateway = result?.providerMetadata?.gateway;
10904
+ const providerMetadata = safeSerializableFieldRead(
10905
+ result,
10906
+ "providerMetadata"
10907
+ );
10908
+ const gateway = providerMetadata?.gateway;
10302
10909
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
10303
10910
  if (directCost !== void 0 && directCost > 0) {
10304
10911
  return directCost;
@@ -10410,12 +11017,15 @@ var claudeAgentSDKChannels = defineChannels(
10410
11017
  {
10411
11018
  query: channel({
10412
11019
  channelName: "query",
10413
- kind: "async"
11020
+ kind: "sync-stream"
10414
11021
  })
10415
11022
  }
10416
11023
  );
10417
11024
 
10418
11025
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
11026
+ function isSubAgentToolName(toolName) {
11027
+ return toolName === "Agent" || toolName === "Task";
11028
+ }
10419
11029
  function filterSerializableOptions(options) {
10420
11030
  const allowedKeys = [
10421
11031
  "model",
@@ -10469,34 +11079,50 @@ function extractUsageFromMessage(message) {
10469
11079
  const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
10470
11080
  const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
10471
11081
  if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
10472
- const cacheTokens = extractAnthropicCacheTokens(
10473
- cacheReadTokens,
10474
- cacheCreationTokens
11082
+ Object.assign(
11083
+ metrics,
11084
+ extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
10475
11085
  );
10476
- Object.assign(metrics, cacheTokens);
10477
11086
  }
10478
11087
  if (Object.keys(metrics).length > 0) {
10479
11088
  Object.assign(metrics, finalizeAnthropicTokens(metrics));
10480
11089
  }
10481
11090
  return metrics;
10482
11091
  }
10483
- function buildLLMInput(prompt, conversationHistory) {
10484
- const promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
10485
- const inputParts = [
10486
- ...promptMessage ? [promptMessage] : [],
10487
- ...conversationHistory
10488
- ];
11092
+ function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
11093
+ const promptMessages = [];
11094
+ if (typeof prompt === "string") {
11095
+ promptMessages.push({ content: prompt, role: "user" });
11096
+ } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
11097
+ for (const msg of capturedPromptMessages) {
11098
+ const role = msg.message?.role;
11099
+ const content = msg.message?.content;
11100
+ if (role && content !== void 0) {
11101
+ promptMessages.push({ content, role });
11102
+ }
11103
+ }
11104
+ }
11105
+ const inputParts = [...promptMessages, ...conversationHistory];
10489
11106
  return inputParts.length > 0 ? inputParts : void 0;
10490
11107
  }
10491
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
10492
- if (messages.length === 0) return void 0;
11108
+ function formatCapturedMessages(messages) {
11109
+ return messages.length > 0 ? messages : [];
11110
+ }
11111
+ async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan) {
11112
+ if (messages.length === 0) {
11113
+ return void 0;
11114
+ }
10493
11115
  const lastMessage = messages[messages.length - 1];
10494
11116
  if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
10495
11117
  return void 0;
10496
11118
  }
10497
11119
  const model = lastMessage.message.model || options.model;
10498
11120
  const usage = extractUsageFromMessage(lastMessage);
10499
- const input = buildLLMInput(prompt, conversationHistory);
11121
+ const input = buildLLMInput(
11122
+ prompt,
11123
+ conversationHistory,
11124
+ capturedPromptMessages
11125
+ );
10500
11126
  const outputs = messages.map(
10501
11127
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
10502
11128
  ).filter(
@@ -10504,21 +11130,359 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10504
11130
  );
10505
11131
  const span = startSpan({
10506
11132
  name: "anthropic.messages.create",
11133
+ parent: parentSpan,
10507
11134
  spanAttributes: {
10508
11135
  type: "llm" /* LLM */
10509
11136
  },
10510
- startTime,
10511
- parent: parentSpan
11137
+ startTime
10512
11138
  });
10513
11139
  span.log({
10514
11140
  input,
10515
- output: outputs,
10516
11141
  metadata: model ? { model } : void 0,
10517
- metrics: usage
11142
+ metrics: usage,
11143
+ output: outputs
10518
11144
  });
10519
11145
  await span.end();
10520
11146
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
10521
11147
  }
11148
+ function getMcpServerMetadata(serverName, mcpServers) {
11149
+ if (!serverName || !mcpServers) {
11150
+ return {};
11151
+ }
11152
+ const serverConfig = mcpServers[serverName];
11153
+ if (!serverConfig) {
11154
+ return {};
11155
+ }
11156
+ const metadata = {};
11157
+ if (serverConfig.type) {
11158
+ metadata["mcp.type"] = serverConfig.type;
11159
+ } else if (typeof serverConfig === "object" && "transport" in serverConfig) {
11160
+ metadata["mcp.type"] = "sdk";
11161
+ }
11162
+ if (serverConfig.url) {
11163
+ metadata["mcp.url"] = serverConfig.url;
11164
+ }
11165
+ if (serverConfig.command) {
11166
+ metadata["mcp.command"] = serverConfig.command;
11167
+ if (serverConfig.args) {
11168
+ metadata["mcp.args"] = serverConfig.args.join(" ");
11169
+ }
11170
+ }
11171
+ return metadata;
11172
+ }
11173
+ function parseToolName(rawToolName) {
11174
+ const mcpMatch = rawToolName.match(/^mcp__([^_]+)__(.+)$/);
11175
+ if (mcpMatch) {
11176
+ const [, mcpServer, toolName] = mcpMatch;
11177
+ return {
11178
+ displayName: `tool: ${mcpServer}/${toolName}`,
11179
+ mcpServer,
11180
+ rawToolName,
11181
+ toolName
11182
+ };
11183
+ }
11184
+ return {
11185
+ displayName: `tool: ${rawToolName}`,
11186
+ rawToolName,
11187
+ toolName: rawToolName
11188
+ };
11189
+ }
11190
+ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, subAgentSpans, endedSubAgentSpans) {
11191
+ const preToolUse = async (input, toolUseID) => {
11192
+ if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
11193
+ return {};
11194
+ }
11195
+ if (isSubAgentToolName(input.tool_name)) {
11196
+ return {};
11197
+ }
11198
+ const parsed = parseToolName(input.tool_name);
11199
+ const toolSpan = startSpan({
11200
+ event: {
11201
+ input: input.tool_input,
11202
+ metadata: {
11203
+ "claude_agent_sdk.cwd": input.cwd,
11204
+ "claude_agent_sdk.raw_tool_name": parsed.rawToolName,
11205
+ "claude_agent_sdk.session_id": input.session_id,
11206
+ "gen_ai.tool.call.id": toolUseID,
11207
+ "gen_ai.tool.name": parsed.toolName,
11208
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer },
11209
+ ...getMcpServerMetadata(parsed.mcpServer, mcpServers)
11210
+ }
11211
+ },
11212
+ name: parsed.displayName,
11213
+ parent: await resolveParentSpan(toolUseID),
11214
+ spanAttributes: { type: "tool" /* TOOL */ }
11215
+ });
11216
+ activeToolSpans.set(toolUseID, toolSpan);
11217
+ return {};
11218
+ };
11219
+ const postToolUse = async (input, toolUseID) => {
11220
+ if (input.hook_event_name !== "PostToolUse" || !toolUseID) {
11221
+ return {};
11222
+ }
11223
+ const subAgentSpan = subAgentSpans.get(toolUseID);
11224
+ if (subAgentSpan) {
11225
+ try {
11226
+ const response = input.tool_response;
11227
+ const metadata = {};
11228
+ if (response?.status) {
11229
+ metadata["claude_agent_sdk.status"] = response.status;
11230
+ }
11231
+ if (response?.totalDurationMs) {
11232
+ metadata["claude_agent_sdk.duration_ms"] = response.totalDurationMs;
11233
+ }
11234
+ if (response?.totalToolUseCount !== void 0) {
11235
+ metadata["claude_agent_sdk.tool_use_count"] = response.totalToolUseCount;
11236
+ }
11237
+ subAgentSpan.log({
11238
+ metadata,
11239
+ output: response?.content
11240
+ });
11241
+ } finally {
11242
+ subAgentSpan.end();
11243
+ endedSubAgentSpans.add(toolUseID);
11244
+ }
11245
+ return {};
11246
+ }
11247
+ const toolSpan = activeToolSpans.get(toolUseID);
11248
+ if (!toolSpan) {
11249
+ return {};
11250
+ }
11251
+ try {
11252
+ toolSpan.log({ output: input.tool_response });
11253
+ } finally {
11254
+ toolSpan.end();
11255
+ activeToolSpans.delete(toolUseID);
11256
+ }
11257
+ return {};
11258
+ };
11259
+ const postToolUseFailure = async (input, toolUseID) => {
11260
+ if (input.hook_event_name !== "PostToolUseFailure" || !toolUseID) {
11261
+ return {};
11262
+ }
11263
+ const subAgentSpan = subAgentSpans.get(toolUseID);
11264
+ if (subAgentSpan) {
11265
+ try {
11266
+ subAgentSpan.log({ error: input.error });
11267
+ } finally {
11268
+ subAgentSpan.end();
11269
+ endedSubAgentSpans.add(toolUseID);
11270
+ }
11271
+ return {};
11272
+ }
11273
+ const toolSpan = activeToolSpans.get(toolUseID);
11274
+ if (!toolSpan) {
11275
+ return {};
11276
+ }
11277
+ const parsed = parseToolName(input.tool_name);
11278
+ try {
11279
+ toolSpan.log({
11280
+ error: input.error,
11281
+ metadata: {
11282
+ "claude_agent_sdk.is_interrupt": input.is_interrupt,
11283
+ "claude_agent_sdk.session_id": input.session_id,
11284
+ "gen_ai.tool.call.id": toolUseID,
11285
+ "gen_ai.tool.name": parsed.toolName,
11286
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer }
11287
+ }
11288
+ });
11289
+ } finally {
11290
+ toolSpan.end();
11291
+ activeToolSpans.delete(toolUseID);
11292
+ }
11293
+ return {};
11294
+ };
11295
+ return { postToolUse, postToolUseFailure, preToolUse };
11296
+ }
11297
+ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, subAgentSpans, endedSubAgentSpans) {
11298
+ const { preToolUse, postToolUse, postToolUseFailure } = createToolTracingHooks(
11299
+ resolveParentSpan,
11300
+ activeToolSpans,
11301
+ options.mcpServers,
11302
+ subAgentSpans,
11303
+ endedSubAgentSpans
11304
+ );
11305
+ const existingHooks = options.hooks ?? {};
11306
+ return {
11307
+ ...options,
11308
+ hooks: {
11309
+ ...existingHooks,
11310
+ PostToolUse: [
11311
+ ...existingHooks.PostToolUse ?? [],
11312
+ { hooks: [postToolUse] }
11313
+ ],
11314
+ PostToolUseFailure: [
11315
+ ...existingHooks.PostToolUseFailure ?? [],
11316
+ {
11317
+ hooks: [postToolUseFailure]
11318
+ }
11319
+ ],
11320
+ PreToolUse: [
11321
+ ...existingHooks.PreToolUse ?? [],
11322
+ { hooks: [preToolUse] }
11323
+ ]
11324
+ }
11325
+ };
11326
+ }
11327
+ async function finalizeCurrentMessageGroup(state) {
11328
+ if (state.currentMessages.length === 0) {
11329
+ return;
11330
+ }
11331
+ const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
11332
+ let parentSpan = await state.span.export();
11333
+ if (parentToolUseId) {
11334
+ const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
11335
+ if (subAgentSpan) {
11336
+ parentSpan = await subAgentSpan.export();
11337
+ }
11338
+ }
11339
+ const finalMessage = await createLLMSpanForMessages(
11340
+ state.currentMessages,
11341
+ state.originalPrompt,
11342
+ state.finalResults,
11343
+ state.options,
11344
+ state.currentMessageStartTime,
11345
+ state.capturedPromptMessages,
11346
+ parentSpan
11347
+ );
11348
+ if (finalMessage) {
11349
+ state.finalResults.push(finalMessage);
11350
+ }
11351
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
11352
+ if (lastMessage?.message?.usage) {
11353
+ state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
11354
+ }
11355
+ state.currentMessages.length = 0;
11356
+ }
11357
+ function maybeTrackToolUseContext(state, message) {
11358
+ if (message.type !== "assistant" || !Array.isArray(message.message?.content)) {
11359
+ return;
11360
+ }
11361
+ const parentToolUseId = message.parent_tool_use_id ?? null;
11362
+ for (const block of message.message.content) {
11363
+ if (typeof block !== "object" || block === null || !("type" in block) || block.type !== "tool_use" || !("id" in block) || typeof block.id !== "string") {
11364
+ continue;
11365
+ }
11366
+ state.toolUseToParent.set(block.id, parentToolUseId);
11367
+ if (block.name === "Task" && typeof block.input === "object" && block.input !== null && "subagent_type" in block.input && typeof block.input.subagent_type === "string") {
11368
+ state.pendingSubAgentNames.set(block.id, block.input.subagent_type);
11369
+ }
11370
+ }
11371
+ }
11372
+ async function maybeStartSubAgentSpan(state, message) {
11373
+ if (!("parent_tool_use_id" in message)) {
11374
+ return;
11375
+ }
11376
+ const parentToolUseId = message.parent_tool_use_id;
11377
+ if (!parentToolUseId) {
11378
+ return;
11379
+ }
11380
+ await ensureSubAgentSpan(
11381
+ state.pendingSubAgentNames,
11382
+ state.span,
11383
+ state.subAgentSpans,
11384
+ parentToolUseId
11385
+ );
11386
+ }
11387
+ async function ensureSubAgentSpan(pendingSubAgentNames, rootSpan, subAgentSpans, parentToolUseId) {
11388
+ const existingSpan = subAgentSpans.get(parentToolUseId);
11389
+ if (existingSpan) {
11390
+ return existingSpan;
11391
+ }
11392
+ const agentName = pendingSubAgentNames.get(parentToolUseId);
11393
+ const spanName = agentName ? `Agent: ${agentName}` : "Agent: sub-agent";
11394
+ const subAgentSpan = startSpan({
11395
+ event: {
11396
+ metadata: {
11397
+ ...agentName && { "claude_agent_sdk.agent_type": agentName }
11398
+ }
11399
+ },
11400
+ name: spanName,
11401
+ parent: await rootSpan.export(),
11402
+ spanAttributes: { type: "task" /* TASK */ }
11403
+ });
11404
+ subAgentSpans.set(parentToolUseId, subAgentSpan);
11405
+ return subAgentSpan;
11406
+ }
11407
+ async function handleStreamMessage(state, message) {
11408
+ maybeTrackToolUseContext(state, message);
11409
+ await maybeStartSubAgentSpan(state, message);
11410
+ const messageId = message.message?.id;
11411
+ if (messageId && messageId !== state.currentMessageId) {
11412
+ await finalizeCurrentMessageGroup(state);
11413
+ state.currentMessageId = messageId;
11414
+ state.currentMessageStartTime = getCurrentUnixTimestamp();
11415
+ }
11416
+ if (message.type === "assistant" && message.message?.usage) {
11417
+ state.currentMessages.push(message);
11418
+ }
11419
+ if (message.type !== "result" || !message.usage) {
11420
+ return;
11421
+ }
11422
+ const finalUsageMetrics = extractUsageFromMessage(message);
11423
+ if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
11424
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
11425
+ if (lastMessage?.message?.usage) {
11426
+ const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
11427
+ if (adjustedTokens >= 0) {
11428
+ lastMessage.message.usage.output_tokens = adjustedTokens;
11429
+ }
11430
+ const resultUsage = message.usage;
11431
+ if (resultUsage && typeof resultUsage === "object") {
11432
+ const cacheReadTokens = getNumberProperty(
11433
+ resultUsage,
11434
+ "cache_read_input_tokens"
11435
+ );
11436
+ if (cacheReadTokens !== void 0) {
11437
+ lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
11438
+ }
11439
+ const cacheCreationTokens = getNumberProperty(
11440
+ resultUsage,
11441
+ "cache_creation_input_tokens"
11442
+ );
11443
+ if (cacheCreationTokens !== void 0) {
11444
+ lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
11445
+ }
11446
+ }
11447
+ }
11448
+ }
11449
+ const metadata = {};
11450
+ if (message.num_turns !== void 0) {
11451
+ metadata.num_turns = message.num_turns;
11452
+ }
11453
+ if (message.session_id !== void 0) {
11454
+ metadata.session_id = message.session_id;
11455
+ }
11456
+ if (Object.keys(metadata).length > 0) {
11457
+ state.span.log({ metadata });
11458
+ }
11459
+ }
11460
+ async function finalizeQuerySpan(state) {
11461
+ try {
11462
+ await finalizeCurrentMessageGroup(state);
11463
+ state.span.log({
11464
+ output: state.finalResults.length > 0 ? state.finalResults[state.finalResults.length - 1] : void 0
11465
+ });
11466
+ if (state.capturedPromptMessages) {
11467
+ if (state.promptStarted()) {
11468
+ await state.promptDone;
11469
+ }
11470
+ if (state.capturedPromptMessages.length > 0) {
11471
+ state.span.log({
11472
+ input: formatCapturedMessages(state.capturedPromptMessages)
11473
+ });
11474
+ }
11475
+ }
11476
+ } finally {
11477
+ for (const [id, subAgentSpan] of state.subAgentSpans) {
11478
+ if (!state.endedSubAgentSpans.has(id)) {
11479
+ subAgentSpan.end();
11480
+ }
11481
+ }
11482
+ state.subAgentSpans.clear();
11483
+ state.span.end();
11484
+ }
11485
+ }
10522
11486
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
10523
11487
  onEnable() {
10524
11488
  this.subscribeToQuery();
@@ -10529,19 +11493,36 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10529
11493
  }
10530
11494
  this.unsubscribers = [];
10531
11495
  }
10532
- /**
10533
- * Subscribe to the query channel for agent interactions.
10534
- * Handles streaming responses and traces both the top-level agent task
10535
- * and individual LLM calls.
10536
- */
10537
11496
  subscribeToQuery() {
10538
11497
  const channel2 = claudeAgentSDKChannels.query.tracingChannel();
10539
11498
  const spans = /* @__PURE__ */ new WeakMap();
10540
11499
  const handlers = {
10541
11500
  start: (event) => {
10542
- const params = event.arguments[0];
10543
- const prompt = params?.prompt;
10544
- const options = params?.options ?? {};
11501
+ const params = event.arguments[0] ?? {};
11502
+ const originalPrompt = params.prompt;
11503
+ const options = params.options ?? {};
11504
+ const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
11505
+ let promptStarted = false;
11506
+ let capturedPromptMessages;
11507
+ let resolvePromptDone;
11508
+ const promptDone = new Promise((resolve) => {
11509
+ resolvePromptDone = resolve;
11510
+ });
11511
+ if (promptIsAsyncIterable) {
11512
+ capturedPromptMessages = [];
11513
+ const promptStream = originalPrompt;
11514
+ params.prompt = (async function* () {
11515
+ promptStarted = true;
11516
+ try {
11517
+ for await (const message of promptStream) {
11518
+ capturedPromptMessages.push(message);
11519
+ yield message;
11520
+ }
11521
+ } finally {
11522
+ resolvePromptDone?.();
11523
+ }
11524
+ })();
11525
+ }
10545
11526
  const span = startSpan({
10546
11527
  name: "Claude Agent",
10547
11528
  spanAttributes: {
@@ -10551,163 +11532,111 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10551
11532
  const startTime = getCurrentUnixTimestamp();
10552
11533
  try {
10553
11534
  span.log({
10554
- input: typeof prompt === "string" ? prompt : {
10555
- type: "streaming",
10556
- description: "AsyncIterable<ClaudeAgentSDKMessage>"
10557
- },
11535
+ input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
10558
11536
  metadata: filterSerializableOptions(options)
10559
11537
  });
10560
11538
  } catch (error) {
10561
11539
  console.error("Error extracting input for Claude Agent SDK:", error);
10562
11540
  }
11541
+ const activeToolSpans = /* @__PURE__ */ new Map();
11542
+ const subAgentSpans = /* @__PURE__ */ new Map();
11543
+ const endedSubAgentSpans = /* @__PURE__ */ new Set();
11544
+ const toolUseToParent = /* @__PURE__ */ new Map();
11545
+ const pendingSubAgentNames = /* @__PURE__ */ new Map();
11546
+ const optionsWithHooks = injectTracingHooks(
11547
+ options,
11548
+ async (toolUseID) => {
11549
+ const parentToolUseId = toolUseToParent.get(toolUseID);
11550
+ if (parentToolUseId) {
11551
+ const subAgentSpan = await ensureSubAgentSpan(
11552
+ pendingSubAgentNames,
11553
+ span,
11554
+ subAgentSpans,
11555
+ parentToolUseId
11556
+ );
11557
+ return subAgentSpan.export();
11558
+ }
11559
+ return span.export();
11560
+ },
11561
+ activeToolSpans,
11562
+ subAgentSpans,
11563
+ endedSubAgentSpans
11564
+ );
11565
+ params.options = optionsWithHooks;
11566
+ event.arguments[0] = params;
10563
11567
  spans.set(event, {
10564
- span,
10565
- startTime,
10566
- conversationHistory: [],
10567
- currentMessages: [],
11568
+ accumulatedOutputTokens: 0,
11569
+ activeToolSpans,
11570
+ capturedPromptMessages,
10568
11571
  currentMessageId: void 0,
10569
11572
  currentMessageStartTime: startTime,
10570
- accumulatedOutputTokens: 0
11573
+ currentMessages: [],
11574
+ endedSubAgentSpans,
11575
+ finalResults: [],
11576
+ options: optionsWithHooks,
11577
+ originalPrompt,
11578
+ pendingSubAgentNames,
11579
+ processing: Promise.resolve(),
11580
+ promptDone,
11581
+ promptStarted: () => promptStarted,
11582
+ span,
11583
+ subAgentSpans,
11584
+ toolUseToParent
10571
11585
  });
10572
11586
  },
10573
- asyncEnd: (event) => {
10574
- const spanData = spans.get(event);
10575
- if (!spanData) {
11587
+ end: (event) => {
11588
+ const state = spans.get(event);
11589
+ if (!state) {
10576
11590
  return;
10577
11591
  }
10578
11592
  const eventResult = event.result;
10579
11593
  if (eventResult === void 0) {
10580
- spanData.span.end();
11594
+ state.span.end();
10581
11595
  spans.delete(event);
10582
11596
  return;
10583
11597
  }
10584
11598
  if (isAsyncIterable(eventResult)) {
10585
11599
  patchStreamIfNeeded(eventResult, {
10586
- onChunk: async (message) => {
10587
- const currentTime = getCurrentUnixTimestamp();
10588
- const params = event.arguments[0];
10589
- const prompt = params?.prompt;
10590
- const options = params?.options ?? {};
10591
- const messageId = message.message?.id;
10592
- if (messageId && messageId !== spanData.currentMessageId) {
10593
- if (spanData.currentMessages.length > 0) {
10594
- const finalMessage = await createLLMSpanForMessages(
10595
- spanData.currentMessages,
10596
- prompt,
10597
- spanData.conversationHistory,
10598
- options,
10599
- spanData.currentMessageStartTime,
10600
- await spanData.span.export()
10601
- );
10602
- if (finalMessage) {
10603
- spanData.conversationHistory.push(finalMessage);
10604
- }
10605
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
10606
- if (lastMessage?.message?.usage) {
10607
- const outputTokens = getNumberProperty(
10608
- lastMessage.message.usage,
10609
- "output_tokens"
10610
- ) || 0;
10611
- spanData.accumulatedOutputTokens += outputTokens;
10612
- }
10613
- spanData.currentMessages = [];
10614
- }
10615
- spanData.currentMessageId = messageId;
10616
- spanData.currentMessageStartTime = currentTime;
10617
- }
10618
- if (message.type === "assistant" && message.message?.usage) {
10619
- spanData.currentMessages.push(message);
10620
- }
10621
- if (message.type === "result" && message.usage) {
10622
- const finalUsageMetrics = extractUsageFromMessage(message);
10623
- if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
10624
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
10625
- if (lastMessage?.message?.usage) {
10626
- const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
10627
- if (adjustedTokens >= 0) {
10628
- lastMessage.message.usage.output_tokens = adjustedTokens;
10629
- }
10630
- }
10631
- }
10632
- const result_metadata = {};
10633
- if (message.num_turns !== void 0) {
10634
- result_metadata.num_turns = message.num_turns;
10635
- }
10636
- if (message.session_id !== void 0) {
10637
- result_metadata.session_id = message.session_id;
10638
- }
10639
- if (Object.keys(result_metadata).length > 0) {
10640
- spanData.span.log({
10641
- metadata: result_metadata
10642
- });
10643
- }
10644
- }
10645
- },
10646
- onComplete: async () => {
10647
- try {
10648
- const params = event.arguments[0];
10649
- const prompt = params?.prompt;
10650
- const options = params?.options ?? {};
10651
- if (spanData.currentMessages.length > 0) {
10652
- const finalMessage = await createLLMSpanForMessages(
10653
- spanData.currentMessages,
10654
- prompt,
10655
- spanData.conversationHistory,
10656
- options,
10657
- spanData.currentMessageStartTime,
10658
- await spanData.span.export()
10659
- );
10660
- if (finalMessage) {
10661
- spanData.conversationHistory.push(finalMessage);
10662
- }
10663
- }
10664
- spanData.span.log({
10665
- output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
10666
- });
10667
- } catch (error) {
11600
+ onChunk: (message) => {
11601
+ maybeTrackToolUseContext(state, message);
11602
+ state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
10668
11603
  console.error(
10669
- "Error extracting output for Claude Agent SDK:",
11604
+ "Error processing Claude Agent SDK stream chunk:",
10670
11605
  error
10671
11606
  );
10672
- } finally {
10673
- spanData.span.end();
10674
- spans.delete(event);
10675
- }
11607
+ });
10676
11608
  },
10677
- onError: (error) => {
10678
- spanData.span.log({
11609
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11610
+ spans.delete(event);
11611
+ }),
11612
+ onError: (error) => state.processing.then(() => {
11613
+ state.span.log({
10679
11614
  error: error.message
10680
11615
  });
10681
- spanData.span.end();
11616
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
10682
11617
  spans.delete(event);
10683
- }
11618
+ })
10684
11619
  });
10685
- } else {
10686
- try {
10687
- spanData.span.log({
10688
- output: eventResult
10689
- });
10690
- } catch (error) {
10691
- console.error(
10692
- "Error extracting output for Claude Agent SDK:",
10693
- error
10694
- );
10695
- } finally {
10696
- spanData.span.end();
10697
- spans.delete(event);
10698
- }
11620
+ return;
11621
+ }
11622
+ try {
11623
+ state.span.log({ output: eventResult });
11624
+ } catch (error) {
11625
+ console.error("Error extracting output for Claude Agent SDK:", error);
11626
+ } finally {
11627
+ state.span.end();
11628
+ spans.delete(event);
10699
11629
  }
10700
11630
  },
10701
11631
  error: (event) => {
10702
- const spanData = spans.get(event);
10703
- if (!spanData || !event.error) {
11632
+ const state = spans.get(event);
11633
+ if (!state || !event.error) {
10704
11634
  return;
10705
11635
  }
10706
- const { span } = spanData;
10707
- span.log({
11636
+ state.span.log({
10708
11637
  error: event.error.message
10709
11638
  });
10710
- span.end();
11639
+ state.span.end();
10711
11640
  spans.delete(event);
10712
11641
  }
10713
11642
  };
@@ -10731,6 +11660,18 @@ var googleGenAIChannels = defineChannels("@google/genai", {
10731
11660
  });
10732
11661
 
10733
11662
  // src/instrumentation/plugins/google-genai-plugin.ts
11663
+ var GOOGLE_GENAI_INTERNAL_CONTEXT = {
11664
+ caller_filename: "<node-internal>",
11665
+ caller_functionname: "<node-internal>",
11666
+ caller_lineno: 0
11667
+ };
11668
+ function createWrapperParityEvent(args) {
11669
+ return {
11670
+ context: GOOGLE_GENAI_INTERNAL_CONTEXT,
11671
+ input: args.input,
11672
+ metadata: args.metadata
11673
+ };
11674
+ }
10734
11675
  var GoogleGenAIPlugin = class extends BasePlugin {
10735
11676
  onEnable() {
10736
11677
  this.subscribeToGoogleGenAIChannels();
@@ -10739,70 +11680,304 @@ var GoogleGenAIPlugin = class extends BasePlugin {
10739
11680
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
10740
11681
  }
10741
11682
  subscribeToGoogleGenAIChannels() {
10742
- this.unsubscribers.push(
10743
- traceAsyncChannel(googleGenAIChannels.generateContent, {
10744
- name: "google-genai.generateContent",
10745
- type: "llm" /* LLM */,
10746
- extractInput: ([params]) => {
10747
- const input = serializeInput(params);
10748
- const metadata = extractMetadata(params);
10749
- return {
10750
- input,
10751
- metadata: { ...metadata, provider: "google-genai" }
10752
- };
10753
- },
10754
- extractOutput: (result) => {
10755
- return result;
10756
- },
10757
- extractMetrics: (result, startTime) => {
10758
- return extractGenerateContentMetrics(result, startTime);
10759
- }
10760
- })
11683
+ this.subscribeToGenerateContentChannel();
11684
+ this.subscribeToGenerateContentStreamChannel();
11685
+ }
11686
+ subscribeToGenerateContentChannel() {
11687
+ const tracingChannel = googleGenAIChannels.generateContent.tracingChannel();
11688
+ const states = /* @__PURE__ */ new WeakMap();
11689
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart2(
11690
+ tracingChannel,
11691
+ states,
11692
+ (event) => {
11693
+ const params = event.arguments[0];
11694
+ const input = serializeInput(params);
11695
+ const metadata = extractMetadata(params);
11696
+ const span = startSpan({
11697
+ name: "generate_content",
11698
+ spanAttributes: {
11699
+ type: "llm" /* LLM */
11700
+ },
11701
+ event: createWrapperParityEvent({ input, metadata })
11702
+ });
11703
+ return {
11704
+ span,
11705
+ startTime: getCurrentUnixTimestamp()
11706
+ };
11707
+ }
10761
11708
  );
10762
- this.unsubscribers.push(
10763
- traceStreamingChannel(googleGenAIChannels.generateContentStream, {
10764
- name: "google-genai.generateContentStream",
10765
- type: "llm" /* LLM */,
10766
- extractInput: ([params]) => {
11709
+ const handlers = {
11710
+ start: (event) => {
11711
+ ensureSpanState(states, event, () => {
11712
+ const params = event.arguments[0];
10767
11713
  const input = serializeInput(params);
10768
11714
  const metadata = extractMetadata(params);
11715
+ const span = startSpan({
11716
+ name: "generate_content",
11717
+ spanAttributes: {
11718
+ type: "llm" /* LLM */
11719
+ },
11720
+ event: createWrapperParityEvent({ input, metadata })
11721
+ });
10769
11722
  return {
10770
- input,
10771
- metadata: { ...metadata, provider: "google-genai" }
11723
+ span,
11724
+ startTime: getCurrentUnixTimestamp()
10772
11725
  };
10773
- },
10774
- extractOutput: (result) => {
10775
- return result;
10776
- },
10777
- extractMetrics: () => {
10778
- return {};
10779
- },
10780
- aggregateChunks: (chunks, _result, _endEvent, startTime) => {
10781
- return aggregateGenerateContentChunks(chunks, startTime);
11726
+ });
11727
+ },
11728
+ asyncEnd: (event) => {
11729
+ const spanState = states.get(event);
11730
+ if (!spanState) {
11731
+ return;
10782
11732
  }
10783
- })
10784
- );
10785
- }
10786
- };
10787
- function serializeInput(params) {
10788
- const input = {
10789
- model: params.model,
10790
- contents: serializeContents(params.contents)
10791
- };
10792
- if (params.config) {
10793
- const config = tryToDict(params.config);
10794
- if (config) {
10795
- const tools = serializeTools(params);
10796
- if (tools) {
10797
- config.tools = tools;
11733
+ try {
11734
+ spanState.span.log({
11735
+ metrics: cleanMetrics(
11736
+ extractGenerateContentMetrics(
11737
+ event.result,
11738
+ spanState.startTime
11739
+ )
11740
+ ),
11741
+ output: event.result
11742
+ });
11743
+ } finally {
11744
+ spanState.span.end();
11745
+ states.delete(event);
11746
+ }
11747
+ },
11748
+ error: (event) => {
11749
+ logErrorAndEndSpan(states, event);
10798
11750
  }
10799
- input.config = config;
10800
- }
11751
+ };
11752
+ tracingChannel.subscribe(handlers);
11753
+ this.unsubscribers.push(() => {
11754
+ unbindCurrentSpanStore?.();
11755
+ tracingChannel.unsubscribe(handlers);
11756
+ });
10801
11757
  }
10802
- return input;
10803
- }
10804
- function serializeContents(contents) {
10805
- if (contents === null || contents === void 0) {
11758
+ subscribeToGenerateContentStreamChannel() {
11759
+ const tracingChannel = googleGenAIChannels.generateContentStream.tracingChannel();
11760
+ const handlers = {
11761
+ start: (event) => {
11762
+ const streamEvent = event;
11763
+ const params = event.arguments[0];
11764
+ streamEvent.googleGenAIInput = serializeInput(params);
11765
+ streamEvent.googleGenAIMetadata = extractMetadata(params);
11766
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
11767
+ },
11768
+ asyncEnd: (event) => {
11769
+ const streamEvent = event;
11770
+ patchGoogleGenAIStreamingResult({
11771
+ input: streamEvent.googleGenAIInput,
11772
+ metadata: streamEvent.googleGenAIMetadata,
11773
+ startTime: streamEvent.googleGenAIStartTime,
11774
+ result: streamEvent.result
11775
+ });
11776
+ },
11777
+ error: () => {
11778
+ }
11779
+ };
11780
+ tracingChannel.subscribe(handlers);
11781
+ this.unsubscribers.push(() => {
11782
+ tracingChannel.unsubscribe(handlers);
11783
+ });
11784
+ }
11785
+ };
11786
+ function ensureSpanState(states, event, create) {
11787
+ const existing = states.get(event);
11788
+ if (existing) {
11789
+ return existing;
11790
+ }
11791
+ const created = create();
11792
+ states.set(event, created);
11793
+ return created;
11794
+ }
11795
+ function bindCurrentSpanStoreToStart2(tracingChannel, states, create) {
11796
+ const state = _internalGetGlobalState();
11797
+ const startChannel = tracingChannel.start;
11798
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
11799
+ if (!startChannel?.bindStore || !currentSpanStore) {
11800
+ return void 0;
11801
+ }
11802
+ startChannel.bindStore(
11803
+ currentSpanStore,
11804
+ (event) => ensureSpanState(
11805
+ states,
11806
+ event,
11807
+ () => create(event)
11808
+ ).span
11809
+ );
11810
+ return () => {
11811
+ startChannel.unbindStore?.(currentSpanStore);
11812
+ };
11813
+ }
11814
+ function logErrorAndEndSpan(states, event) {
11815
+ const spanState = states.get(event);
11816
+ if (!spanState) {
11817
+ return;
11818
+ }
11819
+ spanState.span.log({
11820
+ error: event.error.message
11821
+ });
11822
+ spanState.span.end();
11823
+ states.delete(event);
11824
+ }
11825
+ function patchGoogleGenAIStreamingResult(args) {
11826
+ const { input, metadata, result, startTime } = args;
11827
+ if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
11828
+ return false;
11829
+ }
11830
+ const chunks = [];
11831
+ let firstTokenTime = null;
11832
+ let finalized = false;
11833
+ let span = null;
11834
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
11835
+ const ensureSpan = () => {
11836
+ if (!span) {
11837
+ span = startSpan({
11838
+ name: "generate_content_stream",
11839
+ spanAttributes: {
11840
+ type: "llm" /* LLM */
11841
+ },
11842
+ event: {
11843
+ input,
11844
+ metadata
11845
+ }
11846
+ });
11847
+ }
11848
+ return span;
11849
+ };
11850
+ const finalize = (options) => {
11851
+ if (finalized || !span) {
11852
+ return;
11853
+ }
11854
+ finalized = true;
11855
+ if (options.result) {
11856
+ const { end, ...metricsWithoutEnd } = options.result.metrics;
11857
+ span.log({
11858
+ metrics: cleanMetrics(metricsWithoutEnd),
11859
+ output: options.result.aggregated
11860
+ });
11861
+ span.end(typeof end === "number" ? { endTime: end } : void 0);
11862
+ return;
11863
+ }
11864
+ if (options.error !== void 0) {
11865
+ span.log({
11866
+ error: options.error instanceof Error ? options.error.message : String(options.error)
11867
+ });
11868
+ }
11869
+ span.end();
11870
+ };
11871
+ const patchIterator = (iterator) => {
11872
+ if (typeof iterator !== "object" || iterator === null || "__braintrustGoogleGenAIPatched" in iterator) {
11873
+ return iterator;
11874
+ }
11875
+ const iteratorRecord = iterator;
11876
+ const originalNext = typeof iteratorRecord.next === "function" ? iteratorRecord.next.bind(iterator) : void 0;
11877
+ const originalReturn = typeof iteratorRecord.return === "function" ? iteratorRecord.return.bind(iterator) : void 0;
11878
+ const originalThrow = typeof iteratorRecord.throw === "function" ? iteratorRecord.throw.bind(iterator) : void 0;
11879
+ const asyncIteratorMethod = iteratorRecord[Symbol.asyncIterator];
11880
+ const originalAsyncIterator = typeof asyncIteratorMethod === "function" ? asyncIteratorMethod.bind(iterator) : void 0;
11881
+ Object.defineProperty(iteratorRecord, "__braintrustGoogleGenAIPatched", {
11882
+ configurable: true,
11883
+ enumerable: false,
11884
+ value: true,
11885
+ writable: false
11886
+ });
11887
+ if (originalNext) {
11888
+ iteratorRecord.next = async (...nextArgs) => {
11889
+ ensureSpan();
11890
+ try {
11891
+ const nextResult = await originalNext(
11892
+ ...nextArgs
11893
+ );
11894
+ if (!nextResult.done && nextResult.value) {
11895
+ if (firstTokenTime === null) {
11896
+ firstTokenTime = getCurrentUnixTimestamp();
11897
+ }
11898
+ chunks.push(nextResult.value);
11899
+ }
11900
+ if (nextResult.done) {
11901
+ finalize({
11902
+ result: aggregateGenerateContentChunks(
11903
+ chunks,
11904
+ requestStartTime,
11905
+ firstTokenTime
11906
+ )
11907
+ });
11908
+ }
11909
+ return nextResult;
11910
+ } catch (error) {
11911
+ finalize({ error });
11912
+ throw error;
11913
+ }
11914
+ };
11915
+ }
11916
+ if (originalReturn) {
11917
+ iteratorRecord.return = async (...returnArgs) => {
11918
+ ensureSpan();
11919
+ try {
11920
+ return await originalReturn(
11921
+ ...returnArgs
11922
+ );
11923
+ } finally {
11924
+ if (chunks.length > 0) {
11925
+ finalize({
11926
+ result: aggregateGenerateContentChunks(
11927
+ chunks,
11928
+ requestStartTime,
11929
+ firstTokenTime
11930
+ )
11931
+ });
11932
+ } else {
11933
+ finalize({});
11934
+ }
11935
+ }
11936
+ };
11937
+ }
11938
+ if (originalThrow) {
11939
+ iteratorRecord.throw = async (...throwArgs) => {
11940
+ ensureSpan();
11941
+ try {
11942
+ return await originalThrow(
11943
+ ...throwArgs
11944
+ );
11945
+ } catch (error) {
11946
+ finalize({ error });
11947
+ throw error;
11948
+ }
11949
+ };
11950
+ }
11951
+ iteratorRecord[Symbol.asyncIterator] = () => {
11952
+ const asyncIterator = originalAsyncIterator ? originalAsyncIterator() : iterator;
11953
+ return patchIterator(asyncIterator);
11954
+ };
11955
+ return iterator;
11956
+ };
11957
+ patchIterator(result);
11958
+ return true;
11959
+ }
11960
+ function serializeInput(params) {
11961
+ const input = {
11962
+ model: params.model,
11963
+ contents: serializeContents(params.contents)
11964
+ };
11965
+ if (params.config) {
11966
+ const config = tryToDict(params.config);
11967
+ if (config) {
11968
+ const filteredConfig = {};
11969
+ Object.keys(config).forEach((key) => {
11970
+ if (key !== "tools") {
11971
+ filteredConfig[key] = config[key];
11972
+ }
11973
+ });
11974
+ input.config = filteredConfig;
11975
+ }
11976
+ }
11977
+ return input;
11978
+ }
11979
+ function serializeContents(contents) {
11980
+ if (contents === null || contents === void 0) {
10806
11981
  return null;
10807
11982
  }
10808
11983
  if (Array.isArray(contents)) {
@@ -10883,12 +12058,18 @@ function extractMetadata(params) {
10883
12058
  });
10884
12059
  }
10885
12060
  }
12061
+ const tools = serializeTools(params);
12062
+ if (tools) {
12063
+ metadata.tools = tools;
12064
+ }
10886
12065
  return metadata;
10887
12066
  }
10888
12067
  function extractGenerateContentMetrics(response, startTime) {
10889
12068
  const metrics = {};
10890
- if (startTime) {
12069
+ if (startTime !== void 0) {
10891
12070
  const end = getCurrentUnixTimestamp();
12071
+ metrics.start = startTime;
12072
+ metrics.end = end;
10892
12073
  metrics.duration = end - startTime;
10893
12074
  }
10894
12075
  if (response?.usageMetadata) {
@@ -10913,19 +12094,18 @@ function populateUsageMetrics(metrics, usage) {
10913
12094
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
10914
12095
  }
10915
12096
  }
10916
- function aggregateGenerateContentChunks(chunks, startTime) {
10917
- const metrics = {};
10918
- if (startTime !== void 0) {
10919
- const end = getCurrentUnixTimestamp();
10920
- metrics.duration = end - startTime;
10921
- }
10922
- let firstTokenTime = null;
10923
- if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
10924
- firstTokenTime = getCurrentUnixTimestamp();
12097
+ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
12098
+ const end = getCurrentUnixTimestamp();
12099
+ const metrics = {
12100
+ start: startTime,
12101
+ end,
12102
+ duration: end - startTime
12103
+ };
12104
+ if (firstTokenTime !== null) {
10925
12105
  metrics.time_to_first_token = firstTokenTime - startTime;
10926
12106
  }
10927
12107
  if (chunks.length === 0) {
10928
- return { output: {}, metrics };
12108
+ return { aggregated: {}, metrics };
10929
12109
  }
10930
12110
  let text = "";
10931
12111
  let thoughtText = "";
@@ -10961,7 +12141,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
10961
12141
  }
10962
12142
  }
10963
12143
  }
10964
- const output = {};
12144
+ const aggregated = {};
10965
12145
  const parts = [];
10966
12146
  if (thoughtText) {
10967
12147
  parts.push({ text: thoughtText, thought: true });
@@ -10987,16 +12167,25 @@ function aggregateGenerateContentChunks(chunks, startTime) {
10987
12167
  }
10988
12168
  candidates.push(candidateDict);
10989
12169
  }
10990
- output.candidates = candidates;
12170
+ aggregated.candidates = candidates;
10991
12171
  }
10992
12172
  if (usageMetadata) {
10993
- output.usageMetadata = usageMetadata;
12173
+ aggregated.usageMetadata = usageMetadata;
10994
12174
  populateUsageMetrics(metrics, usageMetadata);
10995
12175
  }
10996
12176
  if (text) {
10997
- output.text = text;
12177
+ aggregated.text = text;
12178
+ }
12179
+ return { aggregated, metrics };
12180
+ }
12181
+ function cleanMetrics(metrics) {
12182
+ const cleaned = {};
12183
+ for (const [key, value] of Object.entries(metrics)) {
12184
+ if (value !== null && value !== void 0) {
12185
+ cleaned[key] = value;
12186
+ }
10998
12187
  }
10999
- return { output, metrics };
12188
+ return cleaned;
11000
12189
  }
11001
12190
  function tryToDict(obj) {
11002
12191
  if (obj === null || obj === void 0) {
@@ -11030,242 +12219,442 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
11030
12219
  channelName: "callModel",
11031
12220
  kind: "sync-stream"
11032
12221
  }),
12222
+ callModelTurn: channel({
12223
+ channelName: "callModel.turn",
12224
+ kind: "async"
12225
+ }),
11033
12226
  toolExecute: channel({
11034
12227
  channelName: "tool.execute",
11035
12228
  kind: "async"
11036
12229
  })
11037
12230
  });
11038
12231
 
11039
- // src/openrouter-utils.ts
11040
- var TOKEN_NAME_MAP2 = {
11041
- promptTokens: "prompt_tokens",
11042
- inputTokens: "prompt_tokens",
11043
- completionTokens: "completion_tokens",
11044
- outputTokens: "completion_tokens",
11045
- totalTokens: "tokens",
11046
- prompt_tokens: "prompt_tokens",
11047
- input_tokens: "prompt_tokens",
11048
- completion_tokens: "completion_tokens",
11049
- output_tokens: "completion_tokens",
11050
- total_tokens: "tokens"
11051
- };
11052
- var TOKEN_DETAIL_PREFIX_MAP = {
11053
- promptTokensDetails: "prompt",
11054
- inputTokensDetails: "prompt",
11055
- completionTokensDetails: "completion",
11056
- outputTokensDetails: "completion",
11057
- costDetails: "cost",
11058
- prompt_tokens_details: "prompt",
11059
- input_tokens_details: "prompt",
11060
- completion_tokens_details: "completion",
11061
- output_tokens_details: "completion",
11062
- cost_details: "cost"
11063
- };
11064
- function camelToSnake(value) {
11065
- return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
11066
- }
11067
- function parseOpenRouterMetricsFromUsage(usage) {
11068
- if (!isObject(usage)) {
11069
- return {};
11070
- }
11071
- const metrics = {};
11072
- for (const [name, value] of Object.entries(usage)) {
11073
- if (typeof value === "number") {
11074
- metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
11075
- continue;
11076
- }
11077
- if (!isObject(value)) {
11078
- continue;
11079
- }
11080
- const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
11081
- if (!prefix) {
11082
- continue;
11083
- }
11084
- for (const [nestedName, nestedValue] of Object.entries(value)) {
11085
- if (typeof nestedValue !== "number") {
11086
- continue;
11087
- }
11088
- metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
11089
- }
11090
- }
11091
- return metrics;
11092
- }
11093
- function extractOpenRouterUsageMetadata(usage) {
11094
- if (!isObject(usage)) {
11095
- return void 0;
11096
- }
11097
- const metadata = {};
11098
- if (typeof usage.isByok === "boolean") {
11099
- metadata.is_byok = usage.isByok;
11100
- } else if (typeof usage.is_byok === "boolean") {
11101
- metadata.is_byok = usage.is_byok;
11102
- }
11103
- return Object.keys(metadata).length > 0 ? metadata : void 0;
11104
- }
11105
-
11106
- // src/openrouter-logging.ts
11107
- var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
11108
- "execute",
11109
- "render",
11110
- "nextTurnParams",
11111
- "requireApproval"
11112
- ]);
11113
- function parseOpenRouterModelString(model) {
11114
- if (typeof model !== "string") {
11115
- return { model };
11116
- }
11117
- const slashIndex = model.indexOf("/");
11118
- if (slashIndex > 0 && slashIndex < model.length - 1) {
11119
- return {
11120
- provider: model.substring(0, slashIndex),
11121
- model: model.substring(slashIndex + 1)
11122
- };
11123
- }
11124
- return { model };
11125
- }
11126
- function isZodSchema2(value) {
11127
- return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11128
- }
11129
- function serializeZodSchema2(schema) {
11130
- try {
11131
- return zodToJsonSchema(schema);
11132
- } catch {
11133
- return {
11134
- type: "object",
11135
- description: "Zod schema (conversion failed)"
11136
- };
11137
- }
11138
- }
11139
- function serializeOpenRouterTool(tool) {
11140
- if (!isObject(tool)) {
11141
- return tool;
11142
- }
11143
- const serialized = {};
11144
- for (const [key, value] of Object.entries(tool)) {
11145
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11146
- continue;
11147
- }
11148
- if (key === "function" && isObject(value)) {
11149
- serialized.function = sanitizeOpenRouterLoggedValue(value);
11150
- continue;
11151
- }
11152
- serialized[key] = sanitizeOpenRouterLoggedValue(value);
11153
- }
11154
- return serialized;
11155
- }
11156
- function serializeOpenRouterToolsForLogging(tools) {
11157
- if (!Array.isArray(tools)) {
11158
- return void 0;
11159
- }
11160
- return tools.map((tool) => serializeOpenRouterTool(tool));
11161
- }
11162
- function sanitizeOpenRouterLoggedValue(value) {
11163
- if (isZodSchema2(value)) {
11164
- return serializeZodSchema2(value);
12232
+ // src/instrumentation/plugins/openrouter-plugin.ts
12233
+ var OpenRouterPlugin = class extends BasePlugin {
12234
+ onEnable() {
12235
+ this.subscribeToOpenRouterChannels();
11165
12236
  }
11166
- if (typeof value === "function") {
11167
- return "[Function]";
12237
+ onDisable() {
12238
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
11168
12239
  }
11169
- if (Array.isArray(value)) {
11170
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
12240
+ subscribeToOpenRouterChannels() {
12241
+ this.unsubscribers.push(
12242
+ traceStreamingChannel(openRouterChannels.chatSend, {
12243
+ name: "openrouter.chat.send",
12244
+ type: "llm" /* LLM */,
12245
+ extractInput: (args) => {
12246
+ const request = getOpenRouterRequestArg(args);
12247
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
12248
+ const httpReferer = request?.httpReferer;
12249
+ const xTitle = request?.xTitle;
12250
+ const { messages, ...metadata } = chatGenerationParams;
12251
+ return {
12252
+ input: messages,
12253
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12254
+ };
12255
+ },
12256
+ extractOutput: (result) => {
12257
+ return isObject(result) ? result.choices : void 0;
12258
+ },
12259
+ extractMetrics: (result, startTime) => {
12260
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12261
+ if (startTime) {
12262
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12263
+ }
12264
+ return metrics;
12265
+ },
12266
+ aggregateChunks: aggregateOpenRouterChatChunks
12267
+ })
12268
+ );
12269
+ this.unsubscribers.push(
12270
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
12271
+ name: "openrouter.embeddings.generate",
12272
+ type: "llm" /* LLM */,
12273
+ extractInput: (args) => {
12274
+ const request = getOpenRouterRequestArg(args);
12275
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
12276
+ const httpReferer = request?.httpReferer;
12277
+ const xTitle = request?.xTitle;
12278
+ const { input, ...metadata } = requestBody;
12279
+ return {
12280
+ input,
12281
+ metadata: buildOpenRouterEmbeddingMetadata(
12282
+ metadata,
12283
+ httpReferer,
12284
+ xTitle
12285
+ )
12286
+ };
12287
+ },
12288
+ extractOutput: (result) => {
12289
+ if (!isObject(result)) {
12290
+ return void 0;
12291
+ }
12292
+ const embedding = result.data?.[0]?.embedding;
12293
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
12294
+ },
12295
+ extractMetadata: (result) => {
12296
+ if (!isObject(result)) {
12297
+ return void 0;
12298
+ }
12299
+ return extractOpenRouterResponseMetadata(result);
12300
+ },
12301
+ extractMetrics: (result) => {
12302
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
12303
+ }
12304
+ })
12305
+ );
12306
+ this.unsubscribers.push(
12307
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
12308
+ name: "openrouter.beta.responses.send",
12309
+ type: "llm" /* LLM */,
12310
+ extractInput: (args) => {
12311
+ const request = getOpenRouterRequestArg(args);
12312
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
12313
+ const httpReferer = request?.httpReferer;
12314
+ const xTitle = request?.xTitle;
12315
+ const { input, ...metadata } = openResponsesRequest;
12316
+ return {
12317
+ input,
12318
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12319
+ };
12320
+ },
12321
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
12322
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
12323
+ extractMetrics: (result, startTime) => {
12324
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12325
+ if (startTime) {
12326
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12327
+ }
12328
+ return metrics;
12329
+ },
12330
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
12331
+ })
12332
+ );
12333
+ this.unsubscribers.push(
12334
+ traceSyncStreamChannel(openRouterChannels.callModel, {
12335
+ name: "openrouter.callModel",
12336
+ type: "llm" /* LLM */,
12337
+ extractInput: (args) => {
12338
+ const request = getOpenRouterCallModelRequestArg(args);
12339
+ return {
12340
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12341
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
12342
+ };
12343
+ },
12344
+ patchResult: ({ endEvent, result, span }) => {
12345
+ return patchOpenRouterCallModelResult({
12346
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
12347
+ result,
12348
+ span
12349
+ });
12350
+ }
12351
+ })
12352
+ );
12353
+ this.unsubscribers.push(
12354
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
12355
+ name: "openrouter.beta.responses.send",
12356
+ type: "llm" /* LLM */,
12357
+ extractInput: (args, event) => {
12358
+ const request = getOpenRouterCallModelRequestArg(args);
12359
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
12360
+ if (isObject(metadata) && "tools" in metadata) {
12361
+ delete metadata.tools;
12362
+ }
12363
+ return {
12364
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12365
+ metadata: {
12366
+ ...metadata,
12367
+ step: event.step,
12368
+ step_type: event.stepType
12369
+ }
12370
+ };
12371
+ },
12372
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
12373
+ extractMetadata: (result, event) => {
12374
+ if (!isObject(result)) {
12375
+ return {
12376
+ step: event?.step,
12377
+ step_type: event?.stepType
12378
+ };
12379
+ }
12380
+ return {
12381
+ ...extractOpenRouterResponseMetadata(result) || {},
12382
+ ...event?.step !== void 0 ? { step: event.step } : {},
12383
+ ...event?.stepType ? { step_type: event.stepType } : {}
12384
+ };
12385
+ },
12386
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
12387
+ })
12388
+ );
12389
+ this.unsubscribers.push(
12390
+ traceStreamingChannel(openRouterChannels.toolExecute, {
12391
+ name: "openrouter.tool",
12392
+ type: "tool" /* TOOL */,
12393
+ extractInput: (args, event) => ({
12394
+ input: args[0],
12395
+ metadata: {
12396
+ provider: "openrouter",
12397
+ tool_name: event.toolName,
12398
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
12399
+ }
12400
+ }),
12401
+ extractOutput: (result) => result,
12402
+ extractMetrics: () => ({}),
12403
+ aggregateChunks: (chunks) => ({
12404
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
12405
+ metrics: {}
12406
+ })
12407
+ })
12408
+ );
12409
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
12410
+ const callModelHandlers = {
12411
+ start: (event) => {
12412
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
12413
+ if (!request) {
12414
+ return;
12415
+ }
12416
+ patchOpenRouterCallModelRequestTools(request);
12417
+ }
12418
+ };
12419
+ callModelChannel.subscribe(callModelHandlers);
12420
+ this.unsubscribers.push(() => {
12421
+ callModelChannel.unsubscribe(callModelHandlers);
12422
+ });
11171
12423
  }
11172
- if (!isObject(value)) {
11173
- return value;
12424
+ };
12425
+ function normalizeArgs(args) {
12426
+ if (Array.isArray(args)) {
12427
+ return args;
11174
12428
  }
11175
- const sanitized = {};
11176
- for (const [key, entry] of Object.entries(value)) {
11177
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11178
- continue;
11179
- }
11180
- if (key === "tools" && Array.isArray(entry)) {
11181
- sanitized.tools = serializeOpenRouterToolsForLogging(entry);
11182
- continue;
11183
- }
11184
- sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12429
+ if (isArrayLike(args)) {
12430
+ return Array.from(args);
11185
12431
  }
11186
- return sanitized;
11187
- }
11188
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
11189
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11190
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11191
- const { model, provider: providerRouting, ...rest } = metadataRecord;
11192
- const normalizedModel = parseOpenRouterModelString(model);
11193
- return {
11194
- ...rest,
11195
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11196
- ...providerRouting !== void 0 ? { providerRouting } : {},
11197
- ...httpReferer !== void 0 ? { httpReferer } : {},
11198
- ...xTitle !== void 0 ? { xTitle } : {},
11199
- provider: normalizedModel.provider || "openrouter"
11200
- };
11201
- }
11202
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
11203
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
11204
- return typeof normalized.model === "string" ? {
11205
- ...normalized,
11206
- embedding_model: normalized.model
11207
- } : normalized;
11208
- }
11209
- function extractOpenRouterCallModelInput(request) {
11210
- return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12432
+ return [args];
11211
12433
  }
11212
- function extractOpenRouterCallModelMetadata(request) {
11213
- if (!isObject(request)) {
11214
- return { provider: "openrouter" };
11215
- }
11216
- const { input: _input, ...metadata } = request;
11217
- return buildOpenRouterMetadata(metadata, void 0, void 0);
12434
+ function isArrayLike(value) {
12435
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
11218
12436
  }
11219
- function extractOpenRouterResponseMetadata(result) {
11220
- if (!isObject(result)) {
11221
- return void 0;
12437
+ function getOpenRouterRequestArg(args) {
12438
+ const normalizedArgs = normalizeArgs(args);
12439
+ const keyedCandidate = normalizedArgs.find(
12440
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
12441
+ );
12442
+ if (isObject(keyedCandidate)) {
12443
+ return keyedCandidate;
11222
12444
  }
11223
- const { output: _output, data: _data, usage, ...metadata } = result;
11224
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11225
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11226
- const { model, provider, ...rest } = metadataRecord;
11227
- const normalizedModel = parseOpenRouterModelString(model);
11228
- const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
11229
- const usageMetadata = extractOpenRouterUsageMetadata(usage);
11230
- const combined = {
11231
- ...rest,
11232
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11233
- ...usageMetadata || {},
11234
- ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
11235
- };
11236
- return Object.keys(combined).length > 0 ? combined : void 0;
12445
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12446
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11237
12447
  }
11238
- function extractOpenRouterResponseOutput(response, fallbackOutput) {
11239
- if (isObject(response) && "output" in response && response.output !== void 0) {
11240
- return sanitizeOpenRouterLoggedValue(response.output);
11241
- }
11242
- if (fallbackOutput !== void 0) {
11243
- return sanitizeOpenRouterLoggedValue(fallbackOutput);
11244
- }
11245
- return void 0;
12448
+ function getOpenRouterCallModelRequestArg(args) {
12449
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
12450
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11246
12451
  }
11247
-
11248
- // src/openrouter-tool-wrapping.ts
11249
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11250
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11251
- "braintrust.openrouter.wrappedCallModelResult"
11252
- );
11253
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
11254
- "getFullResponsesStream",
11255
- "getItemsStream",
11256
- "getNewMessagesStream",
11257
- "getReasoningStream",
11258
- "getTextStream",
11259
- "getToolCallsStream",
11260
- "getToolStream"
11261
- ];
11262
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11263
- "cancel",
11264
- "getPendingToolCalls",
11265
- "getState",
11266
- "getToolCalls",
11267
- "requiresApproval"
11268
- ];
12452
+ var TOKEN_NAME_MAP2 = {
12453
+ promptTokens: "prompt_tokens",
12454
+ inputTokens: "prompt_tokens",
12455
+ completionTokens: "completion_tokens",
12456
+ outputTokens: "completion_tokens",
12457
+ totalTokens: "tokens",
12458
+ prompt_tokens: "prompt_tokens",
12459
+ input_tokens: "prompt_tokens",
12460
+ completion_tokens: "completion_tokens",
12461
+ output_tokens: "completion_tokens",
12462
+ total_tokens: "tokens"
12463
+ };
12464
+ var TOKEN_DETAIL_PREFIX_MAP = {
12465
+ promptTokensDetails: "prompt",
12466
+ inputTokensDetails: "prompt",
12467
+ completionTokensDetails: "completion",
12468
+ outputTokensDetails: "completion",
12469
+ costDetails: "cost",
12470
+ prompt_tokens_details: "prompt",
12471
+ input_tokens_details: "prompt",
12472
+ completion_tokens_details: "completion",
12473
+ output_tokens_details: "completion",
12474
+ cost_details: "cost"
12475
+ };
12476
+ function camelToSnake(value) {
12477
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
12478
+ }
12479
+ function parseOpenRouterMetricsFromUsage(usage) {
12480
+ if (!isObject(usage)) {
12481
+ return {};
12482
+ }
12483
+ const metrics = {};
12484
+ for (const [name, value] of Object.entries(usage)) {
12485
+ if (typeof value === "number") {
12486
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
12487
+ continue;
12488
+ }
12489
+ if (!isObject(value)) {
12490
+ continue;
12491
+ }
12492
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
12493
+ if (!prefix) {
12494
+ continue;
12495
+ }
12496
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
12497
+ if (typeof nestedValue !== "number") {
12498
+ continue;
12499
+ }
12500
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
12501
+ }
12502
+ }
12503
+ return metrics;
12504
+ }
12505
+ function extractOpenRouterUsageMetadata(usage) {
12506
+ if (!isObject(usage)) {
12507
+ return void 0;
12508
+ }
12509
+ const metadata = {};
12510
+ if (typeof usage.isByok === "boolean") {
12511
+ metadata.is_byok = usage.isByok;
12512
+ } else if (typeof usage.is_byok === "boolean") {
12513
+ metadata.is_byok = usage.is_byok;
12514
+ }
12515
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12516
+ }
12517
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
12518
+ "execute",
12519
+ "render",
12520
+ "nextTurnParams",
12521
+ "requireApproval"
12522
+ ]);
12523
+ function parseOpenRouterModelString(model) {
12524
+ if (typeof model !== "string") {
12525
+ return { model };
12526
+ }
12527
+ const slashIndex = model.indexOf("/");
12528
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
12529
+ return {
12530
+ provider: model.substring(0, slashIndex),
12531
+ model: model.substring(slashIndex + 1)
12532
+ };
12533
+ }
12534
+ return { model };
12535
+ }
12536
+ function isZodSchema3(value) {
12537
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
12538
+ }
12539
+ function serializeZodSchema3(schema) {
12540
+ try {
12541
+ return zodToJsonSchema(schema);
12542
+ } catch {
12543
+ return {
12544
+ type: "object",
12545
+ description: "Zod schema (conversion failed)"
12546
+ };
12547
+ }
12548
+ }
12549
+ function serializeOpenRouterTool(tool) {
12550
+ if (!isObject(tool)) {
12551
+ return tool;
12552
+ }
12553
+ const serialized = {};
12554
+ for (const [key, value] of Object.entries(tool)) {
12555
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12556
+ continue;
12557
+ }
12558
+ if (key === "function" && isObject(value)) {
12559
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
12560
+ continue;
12561
+ }
12562
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
12563
+ }
12564
+ return serialized;
12565
+ }
12566
+ function serializeOpenRouterToolsForLogging(tools) {
12567
+ if (!Array.isArray(tools)) {
12568
+ return void 0;
12569
+ }
12570
+ return tools.map((tool) => serializeOpenRouterTool(tool));
12571
+ }
12572
+ function sanitizeOpenRouterLoggedValue(value) {
12573
+ if (isZodSchema3(value)) {
12574
+ return serializeZodSchema3(value);
12575
+ }
12576
+ if (typeof value === "function") {
12577
+ return "[Function]";
12578
+ }
12579
+ if (Array.isArray(value)) {
12580
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
12581
+ }
12582
+ if (!isObject(value)) {
12583
+ return value;
12584
+ }
12585
+ const sanitized = {};
12586
+ for (const [key, entry] of Object.entries(value)) {
12587
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12588
+ continue;
12589
+ }
12590
+ if (key === "tools" && Array.isArray(entry)) {
12591
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
12592
+ continue;
12593
+ }
12594
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12595
+ }
12596
+ return sanitized;
12597
+ }
12598
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
12599
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12600
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12601
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
12602
+ const normalizedModel = parseOpenRouterModelString(model);
12603
+ return {
12604
+ ...rest,
12605
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12606
+ ...providerRouting !== void 0 ? { providerRouting } : {},
12607
+ ...httpReferer !== void 0 ? { httpReferer } : {},
12608
+ ...xTitle !== void 0 ? { xTitle } : {},
12609
+ provider: normalizedModel.provider || "openrouter"
12610
+ };
12611
+ }
12612
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
12613
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
12614
+ return typeof normalized.model === "string" ? {
12615
+ ...normalized,
12616
+ embedding_model: normalized.model
12617
+ } : normalized;
12618
+ }
12619
+ function extractOpenRouterCallModelInput(request) {
12620
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12621
+ }
12622
+ function extractOpenRouterCallModelMetadata(request) {
12623
+ if (!isObject(request)) {
12624
+ return { provider: "openrouter" };
12625
+ }
12626
+ const { input: _input, ...metadata } = request;
12627
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
12628
+ }
12629
+ function extractOpenRouterResponseMetadata(result) {
12630
+ if (!isObject(result)) {
12631
+ return void 0;
12632
+ }
12633
+ const { output: _output, data: _data, usage, ...metadata } = result;
12634
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12635
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12636
+ const { model, provider, ...rest } = metadataRecord;
12637
+ const normalizedModel = parseOpenRouterModelString(model);
12638
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
12639
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
12640
+ const combined = {
12641
+ ...rest,
12642
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12643
+ ...usageMetadata || {},
12644
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
12645
+ };
12646
+ return Object.keys(combined).length > 0 ? combined : void 0;
12647
+ }
12648
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
12649
+ if (isObject(response) && "output" in response && response.output !== void 0) {
12650
+ return sanitizeOpenRouterLoggedValue(response.output);
12651
+ }
12652
+ if (fallbackOutput !== void 0) {
12653
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
12654
+ }
12655
+ return void 0;
12656
+ }
12657
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11269
12658
  function patchOpenRouterCallModelRequestTools(request) {
11270
12659
  if (!Array.isArray(request.tools) || request.tools.length === 0) {
11271
12660
  return void 0;
@@ -11283,29 +12672,234 @@ function patchOpenRouterCallModelRequestTools(request) {
11283
12672
  request.tools = originalTools;
11284
12673
  };
11285
12674
  }
11286
- function patchOpenRouterCallModelResult(span, result, request) {
11287
- if (!isObject(result) || isWrappedCallModelResult(result)) {
11288
- return false;
11289
- }
11290
- const resultLike = result;
11291
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
11292
- (methodName) => typeof resultLike[methodName] === "function"
11293
- );
11294
- if (!hasInstrumentableMethod) {
11295
- return false;
12675
+ function wrapOpenRouterTool(tool) {
12676
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12677
+ return tool;
11296
12678
  }
11297
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
12679
+ const toolName = tool.function.name || "tool";
12680
+ const originalExecute = tool.function.execute;
12681
+ const wrappedTool = {
12682
+ ...tool,
12683
+ function: {
12684
+ ...tool.function,
12685
+ execute(...args) {
12686
+ return traceToolExecution({
12687
+ args,
12688
+ execute: () => Reflect.apply(originalExecute, this, args),
12689
+ toolCallId: getToolCallId(args[1]),
12690
+ toolName
12691
+ });
12692
+ }
12693
+ }
12694
+ };
12695
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
11298
12696
  value: true,
11299
12697
  enumerable: false,
11300
12698
  configurable: false
11301
12699
  });
11302
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
11303
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
11304
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
11305
- let ended = false;
11306
- let tracedTurnCount = 0;
11307
- const endSpanWithResult = async (response, fallbackOutput) => {
11308
- if (ended) {
12700
+ return wrappedTool;
12701
+ }
12702
+ function isWrappedTool(tool) {
12703
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12704
+ }
12705
+ function traceToolExecution(args) {
12706
+ const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
12707
+ const input = args.args.length > 0 ? args.args[0] : void 0;
12708
+ const event = {
12709
+ arguments: [input],
12710
+ span_info: {
12711
+ name: args.toolName
12712
+ },
12713
+ toolCallId: args.toolCallId,
12714
+ toolName: args.toolName
12715
+ };
12716
+ tracingChannel.start.publish(event);
12717
+ try {
12718
+ const result = args.execute();
12719
+ return publishToolResult(tracingChannel, event, result);
12720
+ } catch (error) {
12721
+ event.error = normalizeError(error);
12722
+ tracingChannel.error.publish(event);
12723
+ throw error;
12724
+ }
12725
+ }
12726
+ function publishToolResult(tracingChannel, event, result) {
12727
+ if (isPromiseLike2(result)) {
12728
+ return result.then(
12729
+ (resolved) => {
12730
+ event.result = resolved;
12731
+ tracingChannel.asyncEnd.publish(event);
12732
+ return resolved;
12733
+ },
12734
+ (error) => {
12735
+ event.error = normalizeError(error);
12736
+ tracingChannel.error.publish(event);
12737
+ throw error;
12738
+ }
12739
+ );
12740
+ }
12741
+ event.result = result;
12742
+ tracingChannel.asyncEnd.publish(event);
12743
+ return result;
12744
+ }
12745
+ function getToolCallId(context) {
12746
+ const toolContext = context;
12747
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
12748
+ }
12749
+ function isPromiseLike2(value) {
12750
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12751
+ }
12752
+ function aggregateOpenRouterChatChunks(chunks) {
12753
+ let role;
12754
+ let content = "";
12755
+ let toolCalls;
12756
+ let finishReason;
12757
+ let metrics = {};
12758
+ for (const chunk of chunks) {
12759
+ metrics = {
12760
+ ...metrics,
12761
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
12762
+ };
12763
+ const choice = chunk?.choices?.[0];
12764
+ const delta = choice?.delta;
12765
+ if (!delta) {
12766
+ if (choice?.finish_reason !== void 0) {
12767
+ finishReason = choice.finish_reason;
12768
+ }
12769
+ continue;
12770
+ }
12771
+ if (!role && delta.role) {
12772
+ role = delta.role;
12773
+ }
12774
+ if (typeof delta.content === "string") {
12775
+ content += delta.content;
12776
+ }
12777
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
12778
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
12779
+ if (choiceFinishReason !== void 0) {
12780
+ finishReason = choiceFinishReason;
12781
+ } else if (deltaFinishReason !== void 0) {
12782
+ finishReason = deltaFinishReason;
12783
+ }
12784
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
12785
+ if (!toolCallDeltas) {
12786
+ continue;
12787
+ }
12788
+ for (const toolDelta of toolCallDeltas) {
12789
+ if (!toolDelta?.function) {
12790
+ continue;
12791
+ }
12792
+ const toolIndex = toolDelta.index ?? 0;
12793
+ const existingToolCall = toolCalls?.[toolIndex];
12794
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
12795
+ const nextToolCalls = [...toolCalls || []];
12796
+ nextToolCalls[toolIndex] = {
12797
+ index: toolIndex,
12798
+ id: toolDelta.id,
12799
+ type: toolDelta.type,
12800
+ function: {
12801
+ name: toolDelta.function.name,
12802
+ arguments: toolDelta.function.arguments || ""
12803
+ }
12804
+ };
12805
+ toolCalls = nextToolCalls;
12806
+ continue;
12807
+ }
12808
+ const current = existingToolCall;
12809
+ if (toolDelta.id && !current.id) {
12810
+ current.id = toolDelta.id;
12811
+ }
12812
+ if (toolDelta.type && !current.type) {
12813
+ current.type = toolDelta.type;
12814
+ }
12815
+ if (toolDelta.function.name && !current.function.name) {
12816
+ current.function.name = toolDelta.function.name;
12817
+ }
12818
+ current.function.arguments += toolDelta.function.arguments || "";
12819
+ }
12820
+ }
12821
+ return {
12822
+ output: [
12823
+ {
12824
+ index: 0,
12825
+ message: {
12826
+ role,
12827
+ content: content || void 0,
12828
+ ...toolCalls ? { tool_calls: toolCalls } : {}
12829
+ },
12830
+ logprobs: null,
12831
+ finish_reason: finishReason
12832
+ }
12833
+ ],
12834
+ metrics
12835
+ };
12836
+ }
12837
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
12838
+ let finalResponse;
12839
+ for (const chunk of chunks) {
12840
+ const response = chunk?.response;
12841
+ if (!response) {
12842
+ continue;
12843
+ }
12844
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12845
+ finalResponse = response;
12846
+ }
12847
+ }
12848
+ if (!finalResponse) {
12849
+ return {
12850
+ output: void 0,
12851
+ metrics: {}
12852
+ };
12853
+ }
12854
+ return {
12855
+ output: extractOpenRouterResponseOutput(finalResponse),
12856
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12857
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
12858
+ };
12859
+ }
12860
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
12861
+ "braintrust.openrouter.wrappedCallModelResult"
12862
+ );
12863
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
12864
+ "getFullResponsesStream",
12865
+ "getItemsStream",
12866
+ "getNewMessagesStream",
12867
+ "getReasoningStream",
12868
+ "getTextStream",
12869
+ "getToolCallsStream",
12870
+ "getToolStream"
12871
+ ];
12872
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
12873
+ "cancel",
12874
+ "getPendingToolCalls",
12875
+ "getState",
12876
+ "getToolCalls",
12877
+ "requiresApproval"
12878
+ ];
12879
+ function patchOpenRouterCallModelResult(args) {
12880
+ const { request, result, span } = args;
12881
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
12882
+ return false;
12883
+ }
12884
+ const resultLike = result;
12885
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
12886
+ (methodName) => typeof resultLike[methodName] === "function"
12887
+ );
12888
+ if (!hasInstrumentableMethod) {
12889
+ return false;
12890
+ }
12891
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
12892
+ value: true,
12893
+ enumerable: false,
12894
+ configurable: false
12895
+ });
12896
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
12897
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
12898
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
12899
+ let ended = false;
12900
+ let tracedTurnCount = 0;
12901
+ const endSpanWithResult = async (response, fallbackOutput) => {
12902
+ if (ended) {
11309
12903
  return;
11310
12904
  }
11311
12905
  ended = true;
@@ -11356,10 +12950,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
11356
12950
  }
11357
12951
  };
11358
12952
  if (originalGetResponse) {
11359
- resultLike.getResponse = async (...args) => {
12953
+ resultLike.getResponse = async (...args2) => {
11360
12954
  return await withCurrent(span, async () => {
11361
12955
  try {
11362
- const response = await originalGetResponse(...args);
12956
+ const response = await originalGetResponse(...args2);
11363
12957
  await endSpanWithResult(response);
11364
12958
  return response;
11365
12959
  } catch (error) {
@@ -11371,10 +12965,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
11371
12965
  }
11372
12966
  if (typeof resultLike.getText === "function") {
11373
12967
  const originalGetText = resultLike.getText.bind(resultLike);
11374
- resultLike.getText = async (...args) => {
12968
+ resultLike.getText = async (...args2) => {
11375
12969
  return await withCurrent(span, async () => {
11376
12970
  try {
11377
- const text = await originalGetText(...args);
12971
+ const text = await originalGetText(...args2);
11378
12972
  await finalizeFromResponse(text);
11379
12973
  return text;
11380
12974
  } catch (error) {
@@ -11389,9 +12983,9 @@ function patchOpenRouterCallModelResult(span, result, request) {
11389
12983
  continue;
11390
12984
  }
11391
12985
  const originalMethod = resultLike[methodName];
11392
- resultLike[methodName] = async (...args) => {
12986
+ resultLike[methodName] = async (...args2) => {
11393
12987
  return await withCurrent(span, async () => {
11394
- return await originalMethod.apply(resultLike, args);
12988
+ return await originalMethod.apply(resultLike, args2);
11395
12989
  });
11396
12990
  };
11397
12991
  }
@@ -11400,10 +12994,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
11400
12994
  continue;
11401
12995
  }
11402
12996
  const originalMethod = resultLike[methodName];
11403
- resultLike[methodName] = (...args) => {
12997
+ resultLike[methodName] = (...args2) => {
11404
12998
  const stream = withCurrent(
11405
12999
  span,
11406
- () => originalMethod.apply(resultLike, args)
13000
+ () => originalMethod.apply(resultLike, args2)
11407
13001
  );
11408
13002
  if (!isAsyncIterable2(stream)) {
11409
13003
  return stream;
@@ -11418,616 +13012,194 @@ function patchOpenRouterCallModelResult(span, result, request) {
11418
13012
  }
11419
13013
  if (originalGetInitialResponse) {
11420
13014
  let initialTurnTraced = false;
11421
- resultLike.getInitialResponse = async (...args) => {
13015
+ resultLike.getInitialResponse = async (...args2) => {
11422
13016
  if (initialTurnTraced) {
11423
13017
  return await withCurrent(span, async () => {
11424
- return await originalGetInitialResponse(...args);
11425
- });
11426
- }
11427
- initialTurnTraced = true;
11428
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
11429
- const childSpan = startOpenRouterCallModelTurnSpan({
11430
- request: resolvedRequest,
11431
- step: tracedTurnCount + 1,
11432
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
11433
- });
11434
- return await withCurrent(childSpan, async () => {
11435
- try {
11436
- const response = await originalGetInitialResponse(...args);
11437
- tracedTurnCount++;
11438
- finishOpenRouterCallModelTurnSpan({
11439
- response,
11440
- step: tracedTurnCount,
11441
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
11442
- span: childSpan
11443
- });
11444
- return response;
11445
- } catch (error) {
11446
- childSpan.log({
11447
- error: normalizeError(error).message
11448
- });
11449
- childSpan.end();
11450
- throw error;
11451
- }
11452
- });
11453
- };
11454
- }
11455
- if (originalMakeFollowupRequest) {
11456
- resultLike.makeFollowupRequest = async (...args) => {
11457
- const currentResponse = args[0];
11458
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
11459
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
11460
- const followupRequest = buildOpenRouterFollowupRequest(
11461
- resolvedRequest,
11462
- currentResponse,
11463
- toolResults
11464
- );
11465
- const childSpan = startOpenRouterCallModelTurnSpan({
11466
- request: followupRequest,
11467
- step: tracedTurnCount + 1,
11468
- stepType: "continue"
11469
- });
11470
- return await withCurrent(childSpan, async () => {
11471
- try {
11472
- const response = await originalMakeFollowupRequest(...args);
11473
- tracedTurnCount++;
11474
- finishOpenRouterCallModelTurnSpan({
11475
- response,
11476
- step: tracedTurnCount,
11477
- stepType: "continue",
11478
- span: childSpan
11479
- });
11480
- return response;
11481
- } catch (error) {
11482
- childSpan.log({
11483
- error: normalizeError(error).message
11484
- });
11485
- childSpan.end();
11486
- throw error;
11487
- }
11488
- });
11489
- };
11490
- }
11491
- return true;
11492
- }
11493
- function wrapOpenRouterTool(tool) {
11494
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
11495
- return tool;
11496
- }
11497
- const toolName = tool.function.name || "tool";
11498
- const originalExecute = tool.function.execute;
11499
- const wrappedTool = {
11500
- ...tool,
11501
- function: {
11502
- ...tool.function,
11503
- execute(...args) {
11504
- return traceToolExecution({
11505
- args,
11506
- execute: () => Reflect.apply(originalExecute, this, args),
11507
- toolCallId: getToolCallId(args[1]),
11508
- toolName
11509
- });
11510
- }
11511
- }
11512
- };
11513
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
11514
- value: true,
11515
- enumerable: false,
11516
- configurable: false
11517
- });
11518
- return wrappedTool;
11519
- }
11520
- function isWrappedTool(tool) {
11521
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
11522
- }
11523
- function isWrappedCallModelResult(value) {
11524
- return Boolean(
11525
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
11526
- );
11527
- }
11528
- function traceToolExecution(args) {
11529
- const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
11530
- const input = args.args.length > 0 ? args.args[0] : void 0;
11531
- const event = {
11532
- arguments: [input],
11533
- span_info: {
11534
- name: args.toolName
11535
- },
11536
- toolCallId: args.toolCallId,
11537
- toolName: args.toolName
11538
- };
11539
- tracingChannel.start.publish(event);
11540
- try {
11541
- const result = args.execute();
11542
- return publishToolResult(tracingChannel, event, result);
11543
- } catch (error) {
11544
- event.error = normalizeError(error);
11545
- tracingChannel.error.publish(event);
11546
- throw error;
11547
- }
11548
- }
11549
- function publishToolResult(tracingChannel, event, result) {
11550
- if (isPromiseLike(result)) {
11551
- return result.then(
11552
- (resolved) => {
11553
- event.result = resolved;
11554
- tracingChannel.asyncEnd.publish(event);
11555
- return resolved;
11556
- },
11557
- (error) => {
11558
- event.error = normalizeError(error);
11559
- tracingChannel.error.publish(event);
11560
- throw error;
11561
- }
11562
- );
11563
- }
11564
- event.result = result;
11565
- tracingChannel.asyncEnd.publish(event);
11566
- return result;
11567
- }
11568
- function getToolCallId(context) {
11569
- const toolContext = context;
11570
- return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
11571
- }
11572
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
11573
- const combined = {
11574
- ...extractOpenRouterResponseMetadata(response) || {},
11575
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
11576
- };
11577
- return Object.keys(combined).length > 0 ? combined : void 0;
11578
- }
11579
- function getFinalOpenRouterCallModelResponse(result, response) {
11580
- if (isObject(response)) {
11581
- return response;
11582
- }
11583
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
11584
- }
11585
- function getOpenRouterCallModelRounds(result) {
11586
- if (!Array.isArray(result.allToolExecutionRounds)) {
11587
- return [];
11588
- }
11589
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
11590
- response: isObject(round.response) ? round.response : void 0,
11591
- round: typeof round.round === "number" ? round.round : void 0,
11592
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
11593
- })).filter((round) => round.response !== void 0);
11594
- }
11595
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
11596
- const metrics = {};
11597
- const responses = [
11598
- ...rounds.map((round) => round.response).filter(isObject),
11599
- finalResponse
11600
- ];
11601
- for (const response of responses) {
11602
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
11603
- for (const [name, value] of Object.entries(responseMetrics)) {
11604
- metrics[name] = (metrics[name] || 0) + value;
11605
- }
11606
- }
11607
- return metrics;
11608
- }
11609
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
11610
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
11611
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
11612
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
11613
- (entry) => sanitizeOpenRouterLoggedValue(entry)
11614
- );
11615
- }
11616
- function startOpenRouterCallModelTurnSpan(args) {
11617
- const requestRecord = isObject(args.request) ? args.request : void 0;
11618
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
11619
- if (isObject(metadata) && "tools" in metadata) {
11620
- delete metadata.tools;
11621
- }
11622
- return startSpan({
11623
- name: "openrouter.beta.responses.send",
11624
- spanAttributes: {
11625
- type: "llm" /* LLM */
11626
- },
11627
- event: {
11628
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
11629
- metadata: {
11630
- ...metadata,
11631
- step: args.step,
11632
- step_type: args.stepType
11633
- }
11634
- }
11635
- });
11636
- }
11637
- function finishOpenRouterCallModelTurnSpan(args) {
11638
- if (!isObject(args.response)) {
11639
- args.span.end();
11640
- return;
11641
- }
11642
- args.span.log({
11643
- output: extractOpenRouterResponseOutput(args.response),
11644
- ...extractOpenRouterResponseMetadata(args.response) ? {
11645
- metadata: {
11646
- ...extractOpenRouterResponseMetadata(args.response),
11647
- ...args.step !== void 0 ? { step: args.step } : {},
11648
- ...args.stepType ? { step_type: args.stepType } : {}
11649
- }
11650
- } : {},
11651
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
11652
- });
11653
- args.span.end();
11654
- }
11655
- function getOpenRouterResolvedRequest(result, request) {
11656
- if (isObject(result.resolvedRequest)) {
11657
- return result.resolvedRequest;
11658
- }
11659
- return request;
11660
- }
11661
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
11662
- if (!request) {
11663
- return void 0;
11664
- }
11665
- return {
11666
- ...request,
11667
- input: buildNextOpenRouterCallModelInput(
11668
- extractOpenRouterCallModelInput(request),
11669
- isObject(currentResponse) ? currentResponse : {},
11670
- toolResults
11671
- ),
11672
- stream: false
11673
- };
11674
- }
11675
- function wrapAsyncIterableWithSpan(args) {
11676
- return {
11677
- [Symbol.asyncIterator]() {
11678
- const iterator = args.iteratorFactory();
11679
- return {
11680
- next(value) {
11681
- return withCurrent(
11682
- args.span,
11683
- () => value === void 0 ? iterator.next() : iterator.next(value)
11684
- ).then(
11685
- async (result) => {
11686
- if (result.done) {
11687
- await args.finalize();
11688
- }
11689
- return result;
11690
- },
11691
- (error) => {
11692
- args.onError(error);
11693
- throw error;
11694
- }
11695
- );
11696
- },
11697
- return(value) {
11698
- if (typeof iterator.return !== "function") {
11699
- return args.finalize().then(() => ({
11700
- done: true,
11701
- value
11702
- }));
11703
- }
11704
- return withCurrent(args.span, () => iterator.return(value)).then(
11705
- async (result) => {
11706
- await args.finalize();
11707
- return result;
11708
- },
11709
- (error) => {
11710
- args.onError(error);
11711
- throw error;
11712
- }
11713
- );
11714
- },
11715
- throw(error) {
11716
- args.onError(error);
11717
- if (typeof iterator.throw !== "function") {
11718
- return Promise.reject(error);
11719
- }
11720
- return withCurrent(args.span, () => iterator.throw(error));
11721
- },
11722
- [Symbol.asyncIterator]() {
11723
- return this;
11724
- }
11725
- };
11726
- }
11727
- };
11728
- }
11729
- function isAsyncIterable2(value) {
11730
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
11731
- }
11732
- function isPromiseLike(value) {
11733
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
11734
- }
11735
- function normalizeError(error) {
11736
- return error instanceof Error ? error : new Error(String(error));
11737
- }
11738
-
11739
- // src/instrumentation/plugins/openrouter-plugin.ts
11740
- var OpenRouterPlugin = class extends BasePlugin {
11741
- onEnable() {
11742
- this.subscribeToOpenRouterChannels();
11743
- }
11744
- onDisable() {
11745
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
11746
- }
11747
- subscribeToOpenRouterChannels() {
11748
- this.unsubscribers.push(
11749
- traceStreamingChannel(openRouterChannels.chatSend, {
11750
- name: "openrouter.chat.send",
11751
- type: "llm" /* LLM */,
11752
- extractInput: (args) => {
11753
- const request = getOpenRouterRequestArg(args);
11754
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
11755
- const httpReferer = request?.httpReferer;
11756
- const xTitle = request?.xTitle;
11757
- const { messages, ...metadata } = chatGenerationParams;
11758
- return {
11759
- input: messages,
11760
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
11761
- };
11762
- },
11763
- extractOutput: (result) => {
11764
- return isObject(result) ? result.choices : void 0;
11765
- },
11766
- extractMetrics: (result, startTime) => {
11767
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
11768
- if (startTime) {
11769
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11770
- }
11771
- return metrics;
11772
- },
11773
- aggregateChunks: aggregateOpenRouterChatChunks
11774
- })
11775
- );
11776
- this.unsubscribers.push(
11777
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
11778
- name: "openrouter.embeddings.generate",
11779
- type: "llm" /* LLM */,
11780
- extractInput: (args) => {
11781
- const request = getOpenRouterRequestArg(args);
11782
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
11783
- const httpReferer = request?.httpReferer;
11784
- const xTitle = request?.xTitle;
11785
- const { input, ...metadata } = requestBody;
11786
- return {
11787
- input,
11788
- metadata: buildOpenRouterEmbeddingMetadata(
11789
- metadata,
11790
- httpReferer,
11791
- xTitle
11792
- )
11793
- };
11794
- },
11795
- extractOutput: (result) => {
11796
- if (!isObject(result)) {
11797
- return void 0;
11798
- }
11799
- const embedding = result.data?.[0]?.embedding;
11800
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
11801
- },
11802
- extractMetadata: (result) => {
11803
- if (!isObject(result)) {
11804
- return void 0;
11805
- }
11806
- return extractOpenRouterResponseMetadata(result);
11807
- },
11808
- extractMetrics: (result) => {
11809
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
11810
- }
11811
- })
11812
- );
11813
- this.unsubscribers.push(
11814
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
11815
- name: "openrouter.beta.responses.send",
11816
- type: "llm" /* LLM */,
11817
- extractInput: (args) => {
11818
- const request = getOpenRouterRequestArg(args);
11819
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
11820
- const httpReferer = request?.httpReferer;
11821
- const xTitle = request?.xTitle;
11822
- const { input, ...metadata } = openResponsesRequest;
11823
- return {
11824
- input,
11825
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
11826
- };
11827
- },
11828
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
11829
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
11830
- extractMetrics: (result, startTime) => {
11831
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
11832
- if (startTime) {
11833
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11834
- }
11835
- return metrics;
11836
- },
11837
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
11838
- })
11839
- );
11840
- this.unsubscribers.push(
11841
- traceSyncStreamChannel(openRouterChannels.callModel, {
11842
- name: "openrouter.callModel",
11843
- type: "llm" /* LLM */,
11844
- extractInput: (args) => {
11845
- const request = getOpenRouterCallModelRequestArg(args);
11846
- return {
11847
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
11848
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
11849
- };
11850
- },
11851
- patchResult: ({ endEvent, result, span }) => {
11852
- return patchOpenRouterCallModelResult(
11853
- span,
11854
- result,
11855
- getOpenRouterCallModelRequestArg(endEvent.arguments)
11856
- );
11857
- }
11858
- })
11859
- );
11860
- this.unsubscribers.push(
11861
- traceStreamingChannel(openRouterChannels.toolExecute, {
11862
- name: "openrouter.tool",
11863
- type: "tool" /* TOOL */,
11864
- extractInput: (args, event) => ({
11865
- input: args[0],
11866
- metadata: {
11867
- provider: "openrouter",
11868
- tool_name: event.toolName,
11869
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
11870
- }
11871
- }),
11872
- extractOutput: (result) => result,
11873
- extractMetrics: () => ({}),
11874
- aggregateChunks: (chunks) => ({
11875
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
11876
- metrics: {}
11877
- })
11878
- })
11879
- );
11880
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
11881
- const callModelHandlers = {
11882
- start: (event) => {
11883
- const request = getOpenRouterCallModelRequestArg(event.arguments);
11884
- if (!request) {
11885
- return;
11886
- }
11887
- patchOpenRouterCallModelRequestTools(request);
13018
+ return await originalGetInitialResponse(...args2);
13019
+ });
11888
13020
  }
13021
+ initialTurnTraced = true;
13022
+ const step = tracedTurnCount + 1;
13023
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
13024
+ const response = await traceOpenRouterCallModelTurn({
13025
+ fn: async () => {
13026
+ const nextResponse = await originalGetInitialResponse(...args2);
13027
+ tracedTurnCount++;
13028
+ return nextResponse;
13029
+ },
13030
+ parentSpan: span,
13031
+ request: getOpenRouterResolvedRequest(resultLike, request),
13032
+ step,
13033
+ stepType
13034
+ });
13035
+ return response;
11889
13036
  };
11890
- callModelChannel.subscribe(callModelHandlers);
11891
- this.unsubscribers.push(() => {
11892
- callModelChannel.unsubscribe(callModelHandlers);
11893
- });
11894
- }
11895
- };
11896
- function normalizeArgs(args) {
11897
- if (Array.isArray(args)) {
11898
- return args;
11899
13037
  }
11900
- if (isArrayLike(args)) {
11901
- return Array.from(args);
13038
+ if (originalMakeFollowupRequest) {
13039
+ resultLike.makeFollowupRequest = async (...args2) => {
13040
+ const currentResponse = args2[0];
13041
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
13042
+ const step = tracedTurnCount + 1;
13043
+ const response = await traceOpenRouterCallModelTurn({
13044
+ fn: async () => {
13045
+ const nextResponse = await originalMakeFollowupRequest(...args2);
13046
+ tracedTurnCount++;
13047
+ return nextResponse;
13048
+ },
13049
+ parentSpan: span,
13050
+ request: buildOpenRouterFollowupRequest(
13051
+ getOpenRouterResolvedRequest(resultLike, request),
13052
+ currentResponse,
13053
+ toolResults
13054
+ ),
13055
+ step,
13056
+ stepType: "continue"
13057
+ });
13058
+ return response;
13059
+ };
11902
13060
  }
11903
- return [args];
13061
+ return true;
11904
13062
  }
11905
- function isArrayLike(value) {
11906
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13063
+ async function traceOpenRouterCallModelTurn(args) {
13064
+ const context = {
13065
+ arguments: [args.request],
13066
+ step: args.step,
13067
+ stepType: args.stepType
13068
+ };
13069
+ return await withCurrent(
13070
+ args.parentSpan,
13071
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
13072
+ );
11907
13073
  }
11908
- function getOpenRouterRequestArg(args) {
11909
- const normalizedArgs = normalizeArgs(args);
11910
- const keyedCandidate = normalizedArgs.find(
11911
- (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13074
+ function isWrappedCallModelResult(value) {
13075
+ return Boolean(
13076
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
11912
13077
  );
11913
- if (isObject(keyedCandidate)) {
11914
- return keyedCandidate;
13078
+ }
13079
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
13080
+ const combined = {
13081
+ ...extractOpenRouterResponseMetadata(response) || {},
13082
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
13083
+ };
13084
+ return Object.keys(combined).length > 0 ? combined : void 0;
13085
+ }
13086
+ function getFinalOpenRouterCallModelResponse(result, response) {
13087
+ if (isObject(response)) {
13088
+ return response;
11915
13089
  }
11916
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
11917
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13090
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
11918
13091
  }
11919
- function getOpenRouterCallModelRequestArg(args) {
11920
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
11921
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13092
+ function getOpenRouterCallModelRounds(result) {
13093
+ if (!Array.isArray(result.allToolExecutionRounds)) {
13094
+ return [];
13095
+ }
13096
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
13097
+ response: isObject(round.response) ? round.response : void 0,
13098
+ round: typeof round.round === "number" ? round.round : void 0,
13099
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
13100
+ })).filter((round) => round.response !== void 0);
11922
13101
  }
11923
- function aggregateOpenRouterChatChunks(chunks) {
11924
- let role;
11925
- let content = "";
11926
- let toolCalls;
11927
- let finishReason;
11928
- let metrics = {};
11929
- for (const chunk of chunks) {
11930
- metrics = {
11931
- ...metrics,
11932
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
11933
- };
11934
- const choice = chunk?.choices?.[0];
11935
- const delta = choice?.delta;
11936
- if (!delta) {
11937
- if (choice?.finish_reason !== void 0) {
11938
- finishReason = choice.finish_reason;
11939
- }
11940
- continue;
11941
- }
11942
- if (!role && delta.role) {
11943
- role = delta.role;
11944
- }
11945
- if (typeof delta.content === "string") {
11946
- content += delta.content;
11947
- }
11948
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
11949
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
11950
- if (choiceFinishReason !== void 0) {
11951
- finishReason = choiceFinishReason;
11952
- } else if (deltaFinishReason !== void 0) {
11953
- finishReason = deltaFinishReason;
11954
- }
11955
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
11956
- if (!toolCallDeltas) {
11957
- continue;
11958
- }
11959
- for (const toolDelta of toolCallDeltas) {
11960
- if (!toolDelta?.function) {
11961
- continue;
11962
- }
11963
- const toolIndex = toolDelta.index ?? 0;
11964
- const existingToolCall = toolCalls?.[toolIndex];
11965
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
11966
- const nextToolCalls = [...toolCalls || []];
11967
- nextToolCalls[toolIndex] = {
11968
- index: toolIndex,
11969
- id: toolDelta.id,
11970
- type: toolDelta.type,
11971
- function: {
11972
- name: toolDelta.function.name,
11973
- arguments: toolDelta.function.arguments || ""
11974
- }
11975
- };
11976
- toolCalls = nextToolCalls;
11977
- continue;
11978
- }
11979
- const current = existingToolCall;
11980
- if (toolDelta.id && !current.id) {
11981
- current.id = toolDelta.id;
11982
- }
11983
- if (toolDelta.type && !current.type) {
11984
- current.type = toolDelta.type;
11985
- }
11986
- if (toolDelta.function.name && !current.function.name) {
11987
- current.function.name = toolDelta.function.name;
11988
- }
11989
- current.function.arguments += toolDelta.function.arguments || "";
13102
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
13103
+ const metrics = {};
13104
+ const responses = [
13105
+ ...rounds.map((round) => round.response).filter(isObject),
13106
+ finalResponse
13107
+ ];
13108
+ for (const response of responses) {
13109
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
13110
+ for (const [name, value] of Object.entries(responseMetrics)) {
13111
+ metrics[name] = (metrics[name] || 0) + value;
11990
13112
  }
11991
13113
  }
11992
- return {
11993
- output: [
11994
- {
11995
- index: 0,
11996
- message: {
11997
- role,
11998
- content: content || void 0,
11999
- ...toolCalls ? { tool_calls: toolCalls } : {}
12000
- },
12001
- logprobs: null,
12002
- finish_reason: finishReason
12003
- }
12004
- ],
12005
- metrics
12006
- };
13114
+ return metrics;
12007
13115
  }
12008
- function aggregateOpenRouterResponseStreamEvents(chunks) {
12009
- let finalResponse;
12010
- for (const chunk of chunks) {
12011
- const response = chunk?.response;
12012
- if (!response) {
12013
- continue;
12014
- }
12015
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12016
- finalResponse = response;
12017
- }
13116
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
13117
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
13118
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
13119
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
13120
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
13121
+ );
13122
+ }
13123
+ function getOpenRouterResolvedRequest(result, request) {
13124
+ if (isObject(result.resolvedRequest)) {
13125
+ return result.resolvedRequest;
12018
13126
  }
12019
- if (!finalResponse) {
12020
- return {
12021
- output: void 0,
12022
- metrics: {}
12023
- };
13127
+ return request;
13128
+ }
13129
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
13130
+ if (!request) {
13131
+ return void 0;
12024
13132
  }
12025
13133
  return {
12026
- output: extractOpenRouterResponseOutput(finalResponse),
12027
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12028
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
13134
+ ...request,
13135
+ input: buildNextOpenRouterCallModelInput(
13136
+ extractOpenRouterCallModelInput(request),
13137
+ isObject(currentResponse) ? currentResponse : {},
13138
+ toolResults
13139
+ ),
13140
+ stream: false
13141
+ };
13142
+ }
13143
+ function wrapAsyncIterableWithSpan(args) {
13144
+ return {
13145
+ [Symbol.asyncIterator]() {
13146
+ const iterator = args.iteratorFactory();
13147
+ return {
13148
+ next(value) {
13149
+ return withCurrent(
13150
+ args.span,
13151
+ () => value === void 0 ? iterator.next() : iterator.next(value)
13152
+ ).then(
13153
+ async (result) => {
13154
+ if (result.done) {
13155
+ await args.finalize();
13156
+ }
13157
+ return result;
13158
+ },
13159
+ (error) => {
13160
+ args.onError(error);
13161
+ throw error;
13162
+ }
13163
+ );
13164
+ },
13165
+ return(value) {
13166
+ if (typeof iterator.return !== "function") {
13167
+ return args.finalize().then(() => ({
13168
+ done: true,
13169
+ value
13170
+ }));
13171
+ }
13172
+ return withCurrent(args.span, () => iterator.return(value)).then(
13173
+ async (result) => {
13174
+ await args.finalize();
13175
+ return result;
13176
+ },
13177
+ (error) => {
13178
+ args.onError(error);
13179
+ throw error;
13180
+ }
13181
+ );
13182
+ },
13183
+ throw(error) {
13184
+ args.onError(error);
13185
+ if (typeof iterator.throw !== "function") {
13186
+ return Promise.reject(error);
13187
+ }
13188
+ return withCurrent(args.span, () => iterator.throw(error));
13189
+ },
13190
+ [Symbol.asyncIterator]() {
13191
+ return this;
13192
+ }
13193
+ };
13194
+ }
12029
13195
  };
12030
13196
  }
13197
+ function isAsyncIterable2(value) {
13198
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
13199
+ }
13200
+ function normalizeError(error) {
13201
+ return error instanceof Error ? error : new Error(String(error));
13202
+ }
12031
13203
 
12032
13204
  // src/instrumentation/braintrust-plugin.ts
12033
13205
  var BraintrustPlugin = class extends BasePlugin {