@mastra/observability 1.16.6-alpha.0 → 1.16.6-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1759,6 +1759,34 @@ function addUsageStats(a, b) {
1759
1759
  function supportsModelInference() {
1760
1760
  return coreFeatures.has("model-inference-span");
1761
1761
  }
1762
+ function parseGatewayCost(providerMetadata) {
1763
+ const rawCost = providerMetadata?.gateway?.cost;
1764
+ const cost = typeof rawCost === "number" ? rawCost : typeof rawCost === "string" && rawCost.trim().length > 0 ? Number(rawCost) : void 0;
1765
+ return typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : void 0;
1766
+ }
1767
+ function getGatewayCostContext({ stepProviderMetadata }) {
1768
+ if (!stepProviderMetadata) return;
1769
+ if (!stepProviderMetadata.some((metadata) => metadata?.gateway !== void 0)) return;
1770
+ const costs = [];
1771
+ for (const metadata of stepProviderMetadata) {
1772
+ const cost = parseGatewayCost(metadata);
1773
+ if (cost === void 0) return;
1774
+ costs.push(cost);
1775
+ }
1776
+ const estimatedCost = costs.reduce((total, cost) => total + cost, 0);
1777
+ if (!Number.isFinite(estimatedCost)) return;
1778
+ return {
1779
+ estimatedCost,
1780
+ costUnit: "USD",
1781
+ costMetadata: {
1782
+ source: "provider_reported",
1783
+ sdkProvider: "vercel_ai_gateway",
1784
+ sdkCostField: "gateway.cost",
1785
+ scope: "query_total",
1786
+ reportedStepCount: costs.length
1787
+ }
1788
+ };
1789
+ }
1762
1790
  function formatPreviewLabel(label, fallback) {
1763
1791
  return typeof label === "string" && label.length > 0 ? label : fallback;
1764
1792
  }
@@ -1940,10 +1968,11 @@ var ModelSpanTracker = class {
1940
1968
  * If usage is provided, it will be converted to UsageStats with cache token details.
1941
1969
  */
1942
1970
  endGeneration(options) {
1943
- const { usage, providerMetadata, ...spanOptions } = options ?? {};
1971
+ const { usage, providerMetadata, stepProviderMetadata, ...spanOptions } = options ?? {};
1944
1972
  if (spanOptions.attributes) {
1945
1973
  spanOptions.attributes.completionStartTime = this.#completionStartTime;
1946
1974
  spanOptions.attributes.usage = extractUsageMetrics(usage, providerMetadata);
1975
+ if (!spanOptions.attributes.costContext) spanOptions.attributes.costContext = getGatewayCostContext({ stepProviderMetadata });
1947
1976
  }
1948
1977
  this.#modelSpan?.end(spanOptions);
1949
1978
  }
@@ -2431,12 +2460,12 @@ function isSpanInternal(spanType, flags) {
2431
2460
  /**
2432
2461
  * Get the external parent span ID from CreateSpanOptions.
2433
2462
  *
2434
- * If the parent is internal, walks up the parent chain to find
2435
- * the closest external ancestor. If the parent is already external,
2436
- * returns its ID directly.
2463
+ * If the parent is internal or excluded from export (`excludeSpanTypes`),
2464
+ * walks up the parent chain to find the closest exported ancestor.
2465
+ * If the parent is already exportable, returns its ID directly.
2437
2466
  *
2438
2467
  * This is useful when exporting spans to external observability systems
2439
- * that shouldn't include internal framework spans.
2468
+ * that shouldn't include internal framework spans or excluded types.
2440
2469
  *
2441
2470
  * @param options - Span creation options
2442
2471
  * @returns The external parent span ID, or undefined if no external parent exists
@@ -2457,8 +2486,9 @@ function isSpanInternal(spanType, flags) {
2457
2486
  */
2458
2487
  function getExternalParentId(options) {
2459
2488
  if (!options.parent) return;
2460
- if (options.parent.isInternal) return options.parent.getParentSpanId(false);
2461
- else return options.parent.id;
2489
+ const parent = options.parent;
2490
+ if (parent.isInternal || parent.isExcluded) return parent.getParentSpanId(false);
2491
+ return parent.id;
2462
2492
  }
2463
2493
  var BaseSpan = class {
2464
2494
  name;
@@ -2497,6 +2527,10 @@ var BaseSpan = class {
2497
2527
  * when the span is internal and includeInternalSpans is false, or when
2498
2528
  * the subclass is always excluded (e.g., NoOpSpan).
2499
2529
  *
2530
+ * Public so parent-chain walks (`getParentSpan`) can skip ancestors that
2531
+ * exporters will never receive — otherwise descendants export a
2532
+ * `parentSpanId` pointing at a dropped span and become orphans.
2533
+ *
2500
2534
  * Note: metadata is still attached and deepCleaned because it is read in
2501
2535
  * process by getCorrelationContext() and by getLoggerContext() /
2502
2536
  * getMetricsContext() (which structuredClone it).
@@ -2568,14 +2602,20 @@ var BaseSpan = class {
2568
2602
  get isRootSpan() {
2569
2603
  return !this.parent;
2570
2604
  }
2571
- /** Get the closest parent span, optionally skipping internal spans */
2605
+ /**
2606
+ * Get the closest parent span.
2607
+ * Always skips ancestors dropped by `excludeSpanTypes` (`isExcluded`), so
2608
+ * exported `parentSpanId` values never point at spans exporters omit.
2609
+ * When `includeInternalSpans` is false/undefined, also skips `isInternal`
2610
+ * ancestors.
2611
+ */
2572
2612
  getParentSpan(includeInternalSpans) {
2573
2613
  if (!this.parent) return;
2574
- if (includeInternalSpans) return this.parent;
2575
- if (this.parent.isInternal) return this.parent.getParentSpan(includeInternalSpans);
2614
+ const parent = this.parent;
2615
+ if (parent.isExcluded || !includeInternalSpans && parent.isInternal) return parent.getParentSpan(includeInternalSpans);
2576
2616
  return this.parent;
2577
2617
  }
2578
- /** Get the closest parent spanId that isn't an internal span */
2618
+ /** Get the closest parent spanId that will reach exporters (unless includeInternalSpans) */
2579
2619
  getParentSpanId(includeInternalSpans) {
2580
2620
  if (!this.parent) return this.parentSpanId;
2581
2621
  const parentSpan = this.getParentSpan(includeInternalSpans);