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

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
  }
@@ -2027,13 +2056,15 @@ var ModelSpanTracker = class {
2027
2056
  if (!this.#currentInferenceSpan) return;
2028
2057
  const { usage: rawUsage, ...otherOutput } = payload.output;
2029
2058
  const usage = extractUsageMetrics(rawUsage, payload.metadata?.providerMetadata);
2059
+ const responseModel = typeof payload.metadata?.modelId === "string" ? payload.metadata.modelId : void 0;
2030
2060
  this.#currentInferenceSpan.end({
2031
2061
  output: otherOutput,
2032
2062
  attributes: {
2033
2063
  usage,
2034
2064
  finishReason: payload.stepResult.reason,
2035
2065
  warnings: payload.stepResult.warnings,
2036
- completionStartTime: this.#completionStartTime
2066
+ completionStartTime: this.#completionStartTime,
2067
+ ...responseModel?.trim() ? { responseModel } : {}
2037
2068
  }
2038
2069
  });
2039
2070
  this.#currentInferenceSpan = void 0;
@@ -2431,12 +2462,12 @@ function isSpanInternal(spanType, flags) {
2431
2462
  /**
2432
2463
  * Get the external parent span ID from CreateSpanOptions.
2433
2464
  *
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.
2465
+ * If the parent is internal or excluded from export (`excludeSpanTypes`),
2466
+ * walks up the parent chain to find the closest exported ancestor.
2467
+ * If the parent is already exportable, returns its ID directly.
2437
2468
  *
2438
2469
  * This is useful when exporting spans to external observability systems
2439
- * that shouldn't include internal framework spans.
2470
+ * that shouldn't include internal framework spans or excluded types.
2440
2471
  *
2441
2472
  * @param options - Span creation options
2442
2473
  * @returns The external parent span ID, or undefined if no external parent exists
@@ -2457,8 +2488,9 @@ function isSpanInternal(spanType, flags) {
2457
2488
  */
2458
2489
  function getExternalParentId(options) {
2459
2490
  if (!options.parent) return;
2460
- if (options.parent.isInternal) return options.parent.getParentSpanId(false);
2461
- else return options.parent.id;
2491
+ const parent = options.parent;
2492
+ if (parent.isInternal || parent.isExcluded) return parent.getParentSpanId(false);
2493
+ return parent.id;
2462
2494
  }
2463
2495
  var BaseSpan = class {
2464
2496
  name;
@@ -2497,6 +2529,10 @@ var BaseSpan = class {
2497
2529
  * when the span is internal and includeInternalSpans is false, or when
2498
2530
  * the subclass is always excluded (e.g., NoOpSpan).
2499
2531
  *
2532
+ * Public so parent-chain walks (`getParentSpan`) can skip ancestors that
2533
+ * exporters will never receive — otherwise descendants export a
2534
+ * `parentSpanId` pointing at a dropped span and become orphans.
2535
+ *
2500
2536
  * Note: metadata is still attached and deepCleaned because it is read in
2501
2537
  * process by getCorrelationContext() and by getLoggerContext() /
2502
2538
  * getMetricsContext() (which structuredClone it).
@@ -2568,14 +2604,20 @@ var BaseSpan = class {
2568
2604
  get isRootSpan() {
2569
2605
  return !this.parent;
2570
2606
  }
2571
- /** Get the closest parent span, optionally skipping internal spans */
2607
+ /**
2608
+ * Get the closest parent span.
2609
+ * Always skips ancestors dropped by `excludeSpanTypes` (`isExcluded`), so
2610
+ * exported `parentSpanId` values never point at spans exporters omit.
2611
+ * When `includeInternalSpans` is false/undefined, also skips `isInternal`
2612
+ * ancestors.
2613
+ */
2572
2614
  getParentSpan(includeInternalSpans) {
2573
2615
  if (!this.parent) return;
2574
- if (includeInternalSpans) return this.parent;
2575
- if (this.parent.isInternal) return this.parent.getParentSpan(includeInternalSpans);
2616
+ const parent = this.parent;
2617
+ if (parent.isExcluded || !includeInternalSpans && parent.isInternal) return parent.getParentSpan(includeInternalSpans);
2576
2618
  return this.parent;
2577
2619
  }
2578
- /** Get the closest parent spanId that isn't an internal span */
2620
+ /** Get the closest parent spanId that will reach exporters (unless includeInternalSpans) */
2579
2621
  getParentSpanId(includeInternalSpans) {
2580
2622
  if (!this.parent) return this.parentSpanId;
2581
2623
  const parentSpan = this.getParentSpan(includeInternalSpans);