@raindrop-ai/ai-sdk 0.2.0 → 0.2.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.
@@ -1,4 +1,4 @@
1
- // ../core/dist/chunk-QTUH3BHZ.js
1
+ // ../core/dist/chunk-QNWZIOOY.js
2
2
  function normalizeFeatureFlags(input) {
3
3
  if (Array.isArray(input)) {
4
4
  return Object.fromEntries(input.map((name) => [name, "true"]));
@@ -824,6 +824,193 @@ var EventShipper = class {
824
824
  }
825
825
  }
826
826
  };
827
+ var MODEL_USAGE_ATTRIBUTES = {
828
+ providerName: "gen_ai.provider.name",
829
+ requestModel: "gen_ai.request.model",
830
+ responseModel: "gen_ai.response.model",
831
+ inputTokens: "gen_ai.usage.input_tokens",
832
+ outputTokens: "gen_ai.usage.output_tokens",
833
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
834
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
835
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
836
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
837
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
838
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
839
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
840
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
841
+ };
842
+ var MODEL_PROVIDER_NAMES = [
843
+ ["google.vertex", "google-vertex"],
844
+ ["vertex.anthropic", "google-vertex"],
845
+ ["amazon-bedrock", "amazon-bedrock"],
846
+ ["bedrock", "amazon-bedrock"],
847
+ ["anthropic", "anthropic"],
848
+ ["openai", "openai"],
849
+ ["google", "google"],
850
+ ["azure", "azure"],
851
+ ["openrouter", "openrouter"]
852
+ ];
853
+ function canonicalModelProvider(provider) {
854
+ var _a;
855
+ const normalizedProvider = provider.trim().toLowerCase();
856
+ if (normalizedProvider === "gateway") return "vercel";
857
+ const match = MODEL_PROVIDER_NAMES.find(
858
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
859
+ );
860
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
861
+ }
862
+ function tokenCount(value) {
863
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
864
+ }
865
+ function exclusiveTokens({
866
+ explicit,
867
+ total,
868
+ parts,
869
+ semantics
870
+ }) {
871
+ if (explicit !== void 0) return explicit;
872
+ if (total === void 0) return void 0;
873
+ if (semantics === "exclusive") return total;
874
+ const observedParts = parts.filter((part) => part !== void 0 && part > 0);
875
+ if (semantics === void 0 && observedParts.length > 0) return void 0;
876
+ const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
877
+ return exclusive >= 0 ? exclusive : void 0;
878
+ }
879
+ function buildModelUsageAttributes(usage) {
880
+ const inputTokens = tokenCount(usage.inputTokens);
881
+ const outputTokens = tokenCount(usage.outputTokens);
882
+ const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
883
+ const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
884
+ const reasoningTokens = tokenCount(usage.reasoningTokens);
885
+ const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
886
+ const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
887
+ const nonCachedInputTokens = exclusiveTokens({
888
+ explicit: explicitNonCachedInputTokens,
889
+ total: inputTokens,
890
+ parts: [cacheReadInputTokens, cacheWriteInputTokens],
891
+ semantics: usage.inputTokenSemantics
892
+ });
893
+ const nonReasoningOutputTokens = exclusiveTokens({
894
+ explicit: explicitNonReasoningOutputTokens,
895
+ total: outputTokens,
896
+ parts: [reasoningTokens],
897
+ semantics: usage.outputTokenSemantics
898
+ });
899
+ const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
900
+ // backend consumes this complete raindrop.* set as one atomic billing-pool contract.
901
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
902
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
903
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
904
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
905
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
906
+ ] : [];
907
+ return [
908
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
909
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
910
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
911
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
912
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
913
+ ...exclusivePoolAttributes
914
+ ].filter((attribute) => attribute !== void 0);
915
+ }
916
+ var STRING_ALIASES = {
917
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
918
+ };
919
+ var NUMBER_ALIASES = {
920
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
921
+ "gen_ai.usage.prompt_tokens",
922
+ "ai.usage.prompt_tokens",
923
+ "ai.usage.promptTokens",
924
+ "ai.usage.input_tokens",
925
+ "ai.usage.inputTokens"
926
+ ],
927
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
928
+ "gen_ai.usage.completion_tokens",
929
+ "ai.usage.completion_tokens",
930
+ "ai.usage.completionTokens",
931
+ "ai.usage.output_tokens",
932
+ "ai.usage.outputTokens"
933
+ ],
934
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
935
+ "gen_ai.usage.reasoning.output_tokens",
936
+ "ai.usage.reasoningTokens",
937
+ "ai.usage.thoughts_tokens"
938
+ ],
939
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
940
+ "gen_ai.usage.cache_read_tokens",
941
+ "gen_ai.usage.cache_read.input_tokens",
942
+ "ai.usage.cachedInputTokens",
943
+ "ai.usage.cached_tokens",
944
+ "ai.usage.cache_read_tokens",
945
+ "ai.usage.cache_read_input_tokens",
946
+ "ai.usage.cacheReadInputTokens"
947
+ ],
948
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
949
+ "gen_ai.usage.cache_creation_input_tokens",
950
+ "gen_ai.usage.cache_creation.input_tokens",
951
+ "ai.usage.cacheWriteInputTokens",
952
+ "ai.usage.cache_creation_input_tokens",
953
+ "ai.usage.cacheCreationInputTokens"
954
+ ]
955
+ };
956
+ function hasAttribute(attributes, key) {
957
+ return attributes.some((attribute) => attribute.key === key);
958
+ }
959
+ function findStringAttribute(attributes, keys) {
960
+ return attributes.find(
961
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
962
+ );
963
+ }
964
+ function findNumberAttribute(attributes, keys) {
965
+ return attributes.find((attribute) => {
966
+ if (!keys.includes(attribute.key)) return false;
967
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
968
+ return true;
969
+ }
970
+ const intValue = attribute.value.intValue;
971
+ return integerValue(intValue) !== void 0;
972
+ });
973
+ }
974
+ function integerValue(value) {
975
+ if (typeof value === "number") {
976
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
977
+ }
978
+ if (typeof value !== "string") return void 0;
979
+ const parsed = Number(value);
980
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
981
+ }
982
+ function appendAlias(attributes, target, source) {
983
+ if (!source || hasAttribute(attributes, target)) return attributes;
984
+ return [...attributes, { key: target, value: { ...source.value } }];
985
+ }
986
+ function appendProviderAlias(attributes) {
987
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
988
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
989
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
990
+ const attribute = attrString(
991
+ MODEL_USAGE_ATTRIBUTES.providerName,
992
+ canonicalModelProvider(source.value.stringValue)
993
+ );
994
+ return attribute ? [...attributes, attribute] : attributes;
995
+ }
996
+ function normalizeModelUsageSpan(span) {
997
+ const original = span.attributes;
998
+ if (!original || original.length === 0) return span;
999
+ const responseModel = findStringAttribute(original, [
1000
+ MODEL_USAGE_ATTRIBUTES.responseModel,
1001
+ "ai.response.model"
1002
+ ]);
1003
+ if (!responseModel) return span;
1004
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
1005
+ attributes = appendProviderAlias(attributes);
1006
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
1007
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
1008
+ }
1009
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
1010
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
1011
+ }
1012
+ return attributes === original ? span : { ...span, attributes };
1013
+ }
827
1014
  var DEFAULT_SECRET_KEY_NAMES = [
828
1015
  "apikey",
829
1016
  "apisecret",
@@ -1024,6 +1211,10 @@ var TraceShipper = class {
1024
1211
  return null;
1025
1212
  }
1026
1213
  }
1214
+ try {
1215
+ current = normalizeModelUsageSpan(current);
1216
+ } catch (e) {
1217
+ }
1027
1218
  if (!this.disableDefaultRedaction) {
1028
1219
  current = defaultTransformSpan(current);
1029
1220
  }
@@ -1357,7 +1548,7 @@ async function* asyncGeneratorWithCurrent(span, gen) {
1357
1548
  // package.json
1358
1549
  var package_default = {
1359
1550
  name: "@raindrop-ai/ai-sdk",
1360
- version: "0.2.0"};
1551
+ version: "0.2.1"};
1361
1552
 
1362
1553
  // src/internal/version.ts
1363
1554
  var libraryName = package_default.name;
@@ -2054,6 +2245,15 @@ function attrsFromHeaders(headers) {
2054
2245
  if (!isRecord(headers)) return [];
2055
2246
  return Object.entries(headers).filter(([, v]) => typeof v === "string").map(([k, v]) => attrString(`ai.request.headers.${k}`, v));
2056
2247
  }
2248
+ function attrsFromModelProvider(provider) {
2249
+ if (typeof provider !== "string" || provider.length === 0) return [];
2250
+ const providerName = canonicalModelProvider(provider);
2251
+ return [
2252
+ attrString("ai.model.provider", provider),
2253
+ attrString(MODEL_USAGE_ATTRIBUTES.providerName, providerName),
2254
+ attrString("gen_ai.system", provider)
2255
+ ];
2256
+ }
2057
2257
  function attrsFromSettings(args) {
2058
2258
  if (!isRecord(args)) return [];
2059
2259
  const result = [];
@@ -2236,6 +2436,126 @@ function runWithParentToolContext(ctx, fn) {
2236
2436
  return getStorage2().run(ctx, fn);
2237
2437
  }
2238
2438
 
2439
+ // src/internal/usage.ts
2440
+ function firstTokenCount(...values) {
2441
+ for (const value of values) {
2442
+ if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
2443
+ return value;
2444
+ }
2445
+ }
2446
+ return void 0;
2447
+ }
2448
+ function flatUsageSemantics(provider) {
2449
+ const normalizedProvider = typeof provider === "string" ? provider.trim().toLowerCase() : void 0;
2450
+ if (normalizedProvider === "anthropic" || (normalizedProvider == null ? void 0 : normalizedProvider.startsWith("anthropic.")) || normalizedProvider === "vertex.anthropic" || (normalizedProvider == null ? void 0 : normalizedProvider.startsWith("vertex.anthropic."))) {
2451
+ return {
2452
+ inputTokenSemantics: "exclusive",
2453
+ outputTokenSemantics: "total"
2454
+ };
2455
+ }
2456
+ if (normalizedProvider === "openai" || (normalizedProvider == null ? void 0 : normalizedProvider.startsWith("openai.")) || normalizedProvider === "azure" || (normalizedProvider == null ? void 0 : normalizedProvider.startsWith("azure."))) {
2457
+ return {
2458
+ inputTokenSemantics: "total",
2459
+ outputTokenSemantics: "total"
2460
+ };
2461
+ }
2462
+ if (normalizedProvider === "google" || (normalizedProvider == null ? void 0 : normalizedProvider.startsWith("google."))) {
2463
+ return {
2464
+ inputTokenSemantics: "total",
2465
+ // Google v5 reports candidate output and thoughts separately.
2466
+ outputTokenSemantics: "exclusive"
2467
+ };
2468
+ }
2469
+ return {};
2470
+ }
2471
+ var CACHE_WRITE_METADATA_KEYS = [
2472
+ ["anthropic", "cacheCreationInputTokens"],
2473
+ ["openai", "cacheWriteTokens"],
2474
+ ["bedrock", "cacheWriteInputTokens"]
2475
+ ];
2476
+ function cacheWriteTokensFromProviderMetadata(providerMetadata) {
2477
+ if (!isRecord(providerMetadata)) return void 0;
2478
+ return firstTokenCount(
2479
+ ...CACHE_WRITE_METADATA_KEYS.flatMap(([namespace, key]) => {
2480
+ const scope = providerMetadata[namespace];
2481
+ if (!isRecord(scope)) return [];
2482
+ const usage = scope["usage"];
2483
+ return [scope[key], isRecord(usage) ? usage[key] : void 0];
2484
+ })
2485
+ );
2486
+ }
2487
+ function extractUsageMetricsFromUsage(usage, provider, providerMetadata) {
2488
+ if (!isRecord(usage)) return {};
2489
+ const inputTokenValue = usage["inputTokens"];
2490
+ const outputTokenValue = usage["outputTokens"];
2491
+ const inputTokenDetails = usage["inputTokenDetails"];
2492
+ const outputTokenDetails = usage["outputTokenDetails"];
2493
+ const hasStructuredUsage = isRecord(inputTokenValue) || isRecord(outputTokenValue) || isRecord(inputTokenDetails) || isRecord(outputTokenDetails);
2494
+ const inputTokens = firstTokenCount(
2495
+ isRecord(inputTokenValue) ? inputTokenValue["total"] : void 0,
2496
+ inputTokenValue,
2497
+ usage["promptTokens"],
2498
+ usage["prompt_tokens"]
2499
+ );
2500
+ const outputTokens = firstTokenCount(
2501
+ isRecord(outputTokenValue) ? outputTokenValue["total"] : void 0,
2502
+ outputTokenValue,
2503
+ usage["completionTokens"],
2504
+ usage["completion_tokens"]
2505
+ );
2506
+ const reasoningTokens = firstTokenCount(
2507
+ isRecord(outputTokenValue) ? outputTokenValue["reasoning"] : void 0,
2508
+ isRecord(outputTokenDetails) ? outputTokenDetails["reasoningTokens"] : void 0,
2509
+ usage["reasoningTokens"],
2510
+ usage["completionReasoningTokens"],
2511
+ usage["completion_reasoning_tokens"],
2512
+ usage["reasoning_tokens"],
2513
+ usage["thinkingTokens"],
2514
+ usage["thinking_tokens"]
2515
+ );
2516
+ const cacheReadInputTokens = firstTokenCount(
2517
+ isRecord(inputTokenValue) ? inputTokenValue["cacheRead"] : void 0,
2518
+ isRecord(inputTokenDetails) ? inputTokenDetails["cacheReadTokens"] : void 0,
2519
+ usage["cachedInputTokens"],
2520
+ usage["promptCachedTokens"],
2521
+ usage["prompt_cached_tokens"]
2522
+ );
2523
+ const cacheWriteInputTokens = firstTokenCount(
2524
+ isRecord(inputTokenValue) ? inputTokenValue["cacheWrite"] : void 0,
2525
+ isRecord(inputTokenDetails) ? inputTokenDetails["cacheWriteTokens"] : void 0,
2526
+ usage["cacheWriteInputTokens"],
2527
+ usage["cacheCreationInputTokens"],
2528
+ usage["cache_creation_input_tokens"],
2529
+ cacheWriteTokensFromProviderMetadata(providerMetadata)
2530
+ );
2531
+ const nonCachedInputTokens = firstTokenCount(
2532
+ isRecord(inputTokenValue) ? inputTokenValue["noCache"] : void 0,
2533
+ isRecord(inputTokenDetails) ? inputTokenDetails["noCacheTokens"] : void 0
2534
+ );
2535
+ const nonReasoningOutputTokens = firstTokenCount(
2536
+ isRecord(outputTokenValue) ? outputTokenValue["text"] : void 0,
2537
+ isRecord(outputTokenDetails) ? outputTokenDetails["textTokens"] : void 0
2538
+ );
2539
+ const totalTokens = firstTokenCount(
2540
+ usage["totalTokens"],
2541
+ usage["tokens"],
2542
+ usage["total_tokens"],
2543
+ inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0
2544
+ );
2545
+ return {
2546
+ inputTokens,
2547
+ outputTokens,
2548
+ totalTokens,
2549
+ nonCachedInputTokens,
2550
+ nonReasoningOutputTokens,
2551
+ reasoningTokens,
2552
+ cacheReadInputTokens,
2553
+ cachedInputTokens: cacheReadInputTokens,
2554
+ cacheWriteInputTokens,
2555
+ ...hasStructuredUsage ? {} : flatUsageSemantics(provider)
2556
+ };
2557
+ }
2558
+
2239
2559
  // src/internal/raindrop-telemetry-integration.ts
2240
2560
  function hasUnresolvedToolApproval(event) {
2241
2561
  var _a;
@@ -2405,6 +2725,8 @@ var RaindropTelemetryIntegration = class {
2405
2725
  rootParent: rootSpan ? this.spanParentRef(rootSpan) : rootParentOverride != null ? rootParentOverride : inheritedParent,
2406
2726
  stepSpan: void 0,
2407
2727
  stepParent: void 0,
2728
+ stepProvider: void 0,
2729
+ stepModelId: void 0,
2408
2730
  toolSpans: /* @__PURE__ */ new Map(),
2409
2731
  embedSpans: /* @__PURE__ */ new Map(),
2410
2732
  parentContextToolCallIds: /* @__PURE__ */ new Set(),
@@ -2458,15 +2780,16 @@ var RaindropTelemetryIntegration = class {
2458
2780
  attrString("operation.name", operationName),
2459
2781
  attrString("resource.name", resourceName),
2460
2782
  attrString("ai.telemetry.functionId", state.functionId),
2461
- attrString("ai.model.provider", event.provider),
2783
+ ...attrsFromModelProvider(event.provider),
2462
2784
  attrString("ai.model.id", event.modelId),
2463
- attrString("gen_ai.system", event.provider),
2464
- attrString("gen_ai.request.model", event.modelId),
2785
+ attrString(MODEL_USAGE_ATTRIBUTES.requestModel, event.modelId),
2465
2786
  ...inputAttrs
2466
2787
  ]
2467
2788
  });
2468
2789
  state.stepSpan = stepSpan;
2469
2790
  state.stepParent = this.spanParentRef(stepSpan);
2791
+ state.stepProvider = event.provider;
2792
+ state.stepModelId = event.modelId;
2470
2793
  };
2471
2794
  this.onToolExecutionStart = (event) => this.toolExecutionStart(event);
2472
2795
  this.onToolExecutionEnd = (event) => this.toolExecutionEnd(event);
@@ -2517,23 +2840,24 @@ var RaindropTelemetryIntegration = class {
2517
2840
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2518
2841
  const state = this.getState(event.callId);
2519
2842
  if (!(state == null ? void 0 : state.stepSpan)) return;
2843
+ const responseModelId = (_b = (_a = event.response) == null ? void 0 : _a.modelId) != null ? _b : state.stepModelId;
2520
2844
  const outputAttrs = [];
2521
2845
  if (state.recordOutputs) {
2522
2846
  outputAttrs.push(
2523
2847
  attrString("ai.response.finishReason", event.finishReason),
2524
- attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2525
- attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2526
- attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2848
+ attrString("ai.response.text", capText2((_c = event.text) != null ? _c : void 0)),
2849
+ attrString("ai.response.id", (_d = event.response) == null ? void 0 : _d.id),
2850
+ attrString("ai.response.model", responseModelId),
2527
2851
  attrString(
2528
2852
  "ai.response.timestamp",
2529
- ((_d = event.response) == null ? void 0 : _d.timestamp) instanceof Date ? event.response.timestamp.toISOString() : (_e = event.response) == null ? void 0 : _e.timestamp
2853
+ ((_e = event.response) == null ? void 0 : _e.timestamp) instanceof Date ? event.response.timestamp.toISOString() : (_f = event.response) == null ? void 0 : _f.timestamp
2530
2854
  ),
2531
2855
  attrString(
2532
2856
  "ai.response.providerMetadata",
2533
2857
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
2534
2858
  )
2535
2859
  );
2536
- if (((_f = event.toolCalls) == null ? void 0 : _f.length) > 0) {
2860
+ if (((_g = event.toolCalls) == null ? void 0 : _g.length) > 0) {
2537
2861
  outputAttrs.push(
2538
2862
  attrString(
2539
2863
  "ai.response.toolCalls",
@@ -2547,7 +2871,7 @@ var RaindropTelemetryIntegration = class {
2547
2871
  )
2548
2872
  );
2549
2873
  }
2550
- if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2874
+ if (((_h = event.reasoning) == null ? void 0 : _h.length) > 0) {
2551
2875
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2552
2876
  if (reasoningText) {
2553
2877
  outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
@@ -2556,25 +2880,31 @@ var RaindropTelemetryIntegration = class {
2556
2880
  }
2557
2881
  outputAttrs.push(
2558
2882
  attrStringArray("gen_ai.response.finish_reasons", [event.finishReason]),
2559
- attrString("gen_ai.response.id", (_h = event.response) == null ? void 0 : _h.id),
2560
- attrString("gen_ai.response.model", (_i = event.response) == null ? void 0 : _i.modelId)
2883
+ attrString("gen_ai.response.id", (_i = event.response) == null ? void 0 : _i.id),
2884
+ attrString(MODEL_USAGE_ATTRIBUTES.responseModel, responseModelId)
2561
2885
  );
2562
2886
  const usage = event.usage;
2563
2887
  if (usage) {
2888
+ const usageMetrics = extractUsageMetricsFromUsage(
2889
+ usage,
2890
+ state.stepProvider,
2891
+ event.providerMetadata
2892
+ );
2564
2893
  outputAttrs.push(
2565
- attrInt("ai.usage.inputTokens", usage.inputTokens),
2566
- attrInt("ai.usage.outputTokens", usage.outputTokens),
2567
- attrInt("ai.usage.totalTokens", usage.totalTokens),
2568
- attrInt("ai.usage.reasoningTokens", usage.reasoningTokens),
2569
- attrInt("ai.usage.cachedInputTokens", usage.cachedInputTokens),
2570
- attrInt("gen_ai.usage.input_tokens", usage.inputTokens),
2571
- attrInt("gen_ai.usage.output_tokens", usage.outputTokens)
2894
+ attrInt("ai.usage.inputTokens", usageMetrics.inputTokens),
2895
+ attrInt("ai.usage.outputTokens", usageMetrics.outputTokens),
2896
+ attrInt("ai.usage.totalTokens", usageMetrics.totalTokens),
2897
+ attrInt("ai.usage.reasoningTokens", usageMetrics.reasoningTokens),
2898
+ attrInt("ai.usage.cachedInputTokens", usageMetrics.cachedInputTokens),
2899
+ ...buildModelUsageAttributes(usageMetrics)
2572
2900
  );
2573
2901
  }
2574
2902
  this.emitProviderExecutedToolSpans(event, state);
2575
2903
  this.traceShipper.endSpan(state.stepSpan, { attributes: outputAttrs });
2576
2904
  state.stepSpan = void 0;
2577
2905
  state.stepParent = void 0;
2906
+ state.stepProvider = void 0;
2907
+ state.stepModelId = void 0;
2578
2908
  };
2579
2909
  // ── onEmbedStart ────────────────────────────────────────────────────────
2580
2910
  this.onEmbedStart = (event) => {
@@ -2649,6 +2979,8 @@ var RaindropTelemetryIntegration = class {
2649
2979
  const actualError = (_a = event.error) != null ? _a : error;
2650
2980
  if (state.stepSpan) {
2651
2981
  this.traceShipper.endSpan(state.stepSpan, { error: actualError });
2982
+ state.stepProvider = void 0;
2983
+ state.stepModelId = void 0;
2652
2984
  }
2653
2985
  for (const embedSpan of state.embedSpans.values()) {
2654
2986
  this.traceShipper.endSpan(embedSpan, { error: actualError });
@@ -2692,6 +3024,8 @@ var RaindropTelemetryIntegration = class {
2692
3024
  this.traceShipper.endSpan(state.stepSpan, { attributes: [abortedAttr] });
2693
3025
  state.stepSpan = void 0;
2694
3026
  state.stepParent = void 0;
3027
+ state.stepProvider = void 0;
3028
+ state.stepModelId = void 0;
2695
3029
  }
2696
3030
  for (const embedSpan of state.embedSpans.values()) {
2697
3031
  this.traceShipper.endSpan(embedSpan, { attributes: [abortedAttr] });
@@ -2991,12 +3325,13 @@ var RaindropTelemetryIntegration = class {
2991
3325
  }
2992
3326
  const usage = (_d = event.totalUsage) != null ? _d : event.usage;
2993
3327
  if (usage) {
3328
+ const usageMetrics = extractUsageMetricsFromUsage(usage);
2994
3329
  outputAttrs.push(
2995
- attrInt("ai.usage.inputTokens", usage.inputTokens),
2996
- attrInt("ai.usage.outputTokens", usage.outputTokens),
2997
- attrInt("ai.usage.totalTokens", usage.totalTokens),
2998
- attrInt("ai.usage.reasoningTokens", usage.reasoningTokens),
2999
- attrInt("ai.usage.cachedInputTokens", usage.cachedInputTokens)
3330
+ attrInt("ai.usage.inputTokens", usageMetrics.inputTokens),
3331
+ attrInt("ai.usage.outputTokens", usageMetrics.outputTokens),
3332
+ attrInt("ai.usage.totalTokens", usageMetrics.totalTokens),
3333
+ attrInt("ai.usage.reasoningTokens", usageMetrics.reasoningTokens),
3334
+ attrInt("ai.usage.cachedInputTokens", usageMetrics.cachedInputTokens)
3000
3335
  );
3001
3336
  }
3002
3337
  outputAttrs.push(attrInt("ai.toolCall.count", state.toolCallCount));
@@ -3449,14 +3784,6 @@ function runWithParentSpanContextSync(ctx, fn) {
3449
3784
  function isAsyncIterable(value) {
3450
3785
  return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
3451
3786
  }
3452
- function firstFiniteNumber(...values) {
3453
- for (const value of values) {
3454
- if (typeof value === "number" && Number.isFinite(value)) {
3455
- return value;
3456
- }
3457
- }
3458
- return void 0;
3459
- }
3460
3787
  function resolveUsageRecord(result) {
3461
3788
  if (!isRecord(result)) return void 0;
3462
3789
  let usage;
@@ -3472,50 +3799,7 @@ function resolveUsageRecord(result) {
3472
3799
  return isRecord(usage) ? usage : void 0;
3473
3800
  }
3474
3801
  function extractUsageMetrics(result) {
3475
- const usage = resolveUsageRecord(result);
3476
- if (!usage) return {};
3477
- const inputTokenValue = usage["inputTokens"];
3478
- const outputTokenValue = usage["outputTokens"];
3479
- const inputTokens = firstFiniteNumber(
3480
- isRecord(inputTokenValue) ? inputTokenValue["total"] : void 0,
3481
- inputTokenValue,
3482
- usage["promptTokens"],
3483
- usage["prompt_tokens"]
3484
- );
3485
- const outputTokens = firstFiniteNumber(
3486
- isRecord(outputTokenValue) ? outputTokenValue["total"] : void 0,
3487
- outputTokenValue,
3488
- usage["completionTokens"],
3489
- usage["completion_tokens"]
3490
- );
3491
- const totalTokens = firstFiniteNumber(
3492
- usage["totalTokens"],
3493
- usage["tokens"],
3494
- usage["total_tokens"],
3495
- inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0
3496
- );
3497
- const reasoningTokens = firstFiniteNumber(
3498
- isRecord(outputTokenValue) ? outputTokenValue["reasoning"] : void 0,
3499
- usage["reasoningTokens"],
3500
- usage["completionReasoningTokens"],
3501
- usage["completion_reasoning_tokens"],
3502
- usage["reasoning_tokens"],
3503
- usage["thinkingTokens"],
3504
- usage["thinking_tokens"]
3505
- );
3506
- const cachedInputTokens = firstFiniteNumber(
3507
- isRecord(inputTokenValue) ? inputTokenValue["cacheRead"] : void 0,
3508
- usage["cachedInputTokens"],
3509
- usage["promptCachedTokens"],
3510
- usage["prompt_cached_tokens"]
3511
- );
3512
- return {
3513
- inputTokens,
3514
- outputTokens,
3515
- totalTokens,
3516
- reasoningTokens,
3517
- cachedInputTokens
3518
- };
3802
+ return extractUsageMetricsFromUsage(resolveUsageRecord(result));
3519
3803
  }
3520
3804
  function isObjectOperation(operation) {
3521
3805
  return operation === "generateObject" || operation === "streamObject";
@@ -4906,8 +5190,13 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4906
5190
  ended = true;
4907
5191
  const msToFirstChunk = firstChunkMs !== void 0 ? firstChunkMs - startMs : void 0;
4908
5192
  const msToFinish = finishMs !== void 0 ? finishMs - startMs : void 0;
4909
- const inputTokens = extractNestedTokens(usage, "inputTokens");
4910
- const outputTokens = extractNestedTokens(usage, "outputTokens");
5193
+ const usageMetrics = extractUsageMetricsFromUsage(
5194
+ usage,
5195
+ modelInfo.provider,
5196
+ providerMetadata
5197
+ );
5198
+ const finalResponseModelId = responseModelId != null ? responseModelId : modelInfo.modelId;
5199
+ const { inputTokens, outputTokens } = usageMetrics;
4911
5200
  const avgOutTokensPerSecond = msToFinish && outputTokens !== void 0 && msToFinish > 0 ? 1e3 * outputTokens / msToFinish : void 0;
4912
5201
  ctx.traceShipper.endSpan(span, {
4913
5202
  attributes: [
@@ -4919,7 +5208,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4919
5208
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
4920
5209
  ),
4921
5210
  attrString("ai.response.id", responseId),
4922
- attrString("ai.response.model", responseModelId),
5211
+ attrString("ai.response.model", finalResponseModelId),
4923
5212
  attrString("ai.response.timestamp", responseTimestampIso),
4924
5213
  attrString(
4925
5214
  "ai.response.providerMetadata",
@@ -4930,9 +5219,8 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4930
5219
  attrInt("ai.usage.outputTokens", outputTokens),
4931
5220
  ...finishReason ? [attrStringArray("gen_ai.response.finish_reasons", [finishReason])] : [],
4932
5221
  attrString("gen_ai.response.id", responseId),
4933
- attrString("gen_ai.response.model", responseModelId),
4934
- attrInt("gen_ai.usage.input_tokens", inputTokens),
4935
- attrInt("gen_ai.usage.output_tokens", outputTokens),
5222
+ attrString(MODEL_USAGE_ATTRIBUTES.responseModel, finalResponseModelId),
5223
+ ...buildModelUsageAttributes(usageMetrics),
4936
5224
  ...msToFirstChunk !== void 0 ? [attrInt("ai.stream.msToFirstChunk", msToFirstChunk)] : [],
4937
5225
  ...msToFinish !== void 0 ? [attrInt("ai.stream.msToFinish", msToFinish)] : [],
4938
5226
  ...avgOutTokensPerSecond !== void 0 ? [attrDouble("ai.stream.avgOutputTokensPerSecond", avgOutTokensPerSecond)] : [],
@@ -5038,15 +5326,14 @@ function startDoGenerateSpan(operationId, options, modelInfo, parent, ctx) {
5038
5326
  attrString("operation.name", operationName),
5039
5327
  attrString("resource.name", resourceName),
5040
5328
  attrString("ai.telemetry.functionId", (_b = ctx.telemetry) == null ? void 0 : _b.functionId),
5041
- attrString("ai.model.provider", modelInfo.provider),
5329
+ ...attrsFromModelProvider(modelInfo.provider),
5042
5330
  attrString("ai.model.id", modelInfo.modelId),
5043
5331
  ...((_c = ctx.telemetry) == null ? void 0 : _c.recordInputs) === false ? [] : [
5044
5332
  attrString("ai.prompt.messages", promptJson),
5045
5333
  attrStringArray("ai.prompt.tools", toolsJson),
5046
5334
  attrString("ai.prompt.toolChoice", toolChoiceJson)
5047
5335
  ],
5048
- attrString("gen_ai.system", modelInfo.provider),
5049
- attrString("gen_ai.request.model", modelInfo.modelId),
5336
+ attrString(MODEL_USAGE_ATTRIBUTES.requestModel, modelInfo.modelId),
5050
5337
  ...attrsFromGenAiRequest(options),
5051
5338
  attrProviderOptions(options)
5052
5339
  ]
@@ -5079,8 +5366,12 @@ function endDoGenerateSpan(span, result, modelInfo, ctx) {
5079
5366
  } else {
5080
5367
  responseTimestampIso = (/* @__PURE__ */ new Date()).toISOString();
5081
5368
  }
5082
- const inputTokens = extractNestedTokens(usage, "inputTokens");
5083
- const outputTokens = extractNestedTokens(usage, "outputTokens");
5369
+ const usageMetrics = extractUsageMetricsFromUsage(
5370
+ usage,
5371
+ modelInfo.provider,
5372
+ providerMetadata
5373
+ );
5374
+ const { inputTokens, outputTokens } = usageMetrics;
5084
5375
  ctx.traceShipper.endSpan(span, {
5085
5376
  attributes: [
5086
5377
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
@@ -5099,9 +5390,8 @@ function endDoGenerateSpan(span, result, modelInfo, ctx) {
5099
5390
  attrInt("ai.usage.completionTokens", outputTokens),
5100
5391
  ...finishReason ? [attrStringArray("gen_ai.response.finish_reasons", [finishReason])] : [],
5101
5392
  attrString("gen_ai.response.id", responseId),
5102
- attrString("gen_ai.response.model", responseModelId),
5103
- attrInt("gen_ai.usage.input_tokens", inputTokens),
5104
- attrInt("gen_ai.usage.output_tokens", outputTokens)
5393
+ attrString(MODEL_USAGE_ATTRIBUTES.responseModel, responseModelId),
5394
+ ...buildModelUsageAttributes(usageMetrics)
5105
5395
  ]
5106
5396
  });
5107
5397
  }
@@ -5122,15 +5412,14 @@ function startDoStreamSpan(operationId, options, modelInfo, parent, ctx) {
5122
5412
  attrString("operation.name", operationName),
5123
5413
  attrString("resource.name", resourceName),
5124
5414
  attrString("ai.telemetry.functionId", (_b = ctx.telemetry) == null ? void 0 : _b.functionId),
5125
- attrString("ai.model.provider", modelInfo.provider),
5415
+ ...attrsFromModelProvider(modelInfo.provider),
5126
5416
  attrString("ai.model.id", modelInfo.modelId),
5127
5417
  ...((_c = ctx.telemetry) == null ? void 0 : _c.recordInputs) === false ? [] : [
5128
5418
  attrString("ai.prompt.messages", promptJson),
5129
5419
  attrStringArray("ai.prompt.tools", toolsJson),
5130
5420
  attrString("ai.prompt.toolChoice", toolChoiceJson)
5131
5421
  ],
5132
- attrString("gen_ai.system", modelInfo.provider),
5133
- attrString("gen_ai.request.model", modelInfo.modelId),
5422
+ attrString(MODEL_USAGE_ATTRIBUTES.requestModel, modelInfo.modelId),
5134
5423
  ...attrsFromGenAiRequest(options),
5135
5424
  attrProviderOptions(options)
5136
5425
  ]
@@ -5195,13 +5484,6 @@ function mergeAttachments(...groups) {
5195
5484
  }
5196
5485
  return merged.length ? merged : void 0;
5197
5486
  }
5198
- function extractNestedTokens(usage, key) {
5199
- if (!isRecord(usage)) return void 0;
5200
- const val = usage[key];
5201
- if (typeof val === "number") return val;
5202
- if (isRecord(val) && typeof val["total"] === "number") return val["total"];
5203
- return void 0;
5204
- }
5205
5487
 
5206
5488
  // src/index.ts
5207
5489
  function eventMetadata(options) {