@mastra/observability 1.16.5 → 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/CHANGELOG.md +27 -0
- package/dist/default.d.ts.map +1 -1
- package/dist/index.cjs +94 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +94 -21
- package/dist/index.js.map +1 -1
- package/dist/model-tracing.d.ts.map +1 -1
- package/dist/spans/base.d.ts +17 -7
- package/dist/spans/base.d.ts.map +1 -1
- package/package.json +4 -4
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
|
|
2435
|
-
* the
|
|
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
|
-
|
|
2461
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
2575
|
-
if (
|
|
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
|
|
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);
|
|
@@ -8696,6 +8736,23 @@ function isInstance(obj) {
|
|
|
8696
8736
|
return obj instanceof BaseObservabilityInstance;
|
|
8697
8737
|
}
|
|
8698
8738
|
/**
|
|
8739
|
+
* Delays (ms) between attempts to rehydrate a trace from storage when an
|
|
8740
|
+
* annotation (score/feedback) targets a span that has not been flushed by
|
|
8741
|
+
* the configured exporters yet. Exporters buffer and flush asynchronously:
|
|
8742
|
+
* by default they flush at 1000 spans or after a 5000ms wait
|
|
8743
|
+
* (`maxBatchWaitMs`), so an annotation emitted right after a span ends can
|
|
8744
|
+
* race the flush and be silently dropped. The schedule below sums to
|
|
8745
|
+
* ~5.85s, covering the default flush interval with margin.
|
|
8746
|
+
*/
|
|
8747
|
+
const RECORDED_TRACE_LOOKUP_RETRY_DELAYS_MS = [
|
|
8748
|
+
100,
|
|
8749
|
+
250,
|
|
8750
|
+
500,
|
|
8751
|
+
1e3,
|
|
8752
|
+
2e3,
|
|
8753
|
+
2e3
|
|
8754
|
+
];
|
|
8755
|
+
/**
|
|
8699
8756
|
* Top-level observability entrypoint. Manages a registry of ObservabilityInstance
|
|
8700
8757
|
* configurations and provides instance selection via config selectors.
|
|
8701
8758
|
*/
|
|
@@ -8852,14 +8909,15 @@ var Observability = class extends MastraBase {
|
|
|
8852
8909
|
return;
|
|
8853
8910
|
}
|
|
8854
8911
|
if (!args.traceId) return;
|
|
8855
|
-
const
|
|
8856
|
-
if (!trace) return;
|
|
8857
|
-
const event = buildRecordedScoreEventFromTrace({
|
|
8912
|
+
const event = await this.#buildRecordedEventWithRetry(args.traceId, (trace) => buildRecordedScoreEventFromTrace({
|
|
8858
8913
|
trace,
|
|
8859
8914
|
spanId: args.spanId,
|
|
8860
8915
|
score: args.score
|
|
8861
|
-
});
|
|
8862
|
-
if (!event)
|
|
8916
|
+
}));
|
|
8917
|
+
if (!event) {
|
|
8918
|
+
this.logger?.warn(`Score event was dropped because the target trace/span was not found in observability storage (traceId: ${args.traceId}, spanId: ${args.spanId})`);
|
|
8919
|
+
return;
|
|
8920
|
+
}
|
|
8863
8921
|
await this.#emitRecordedEvent(event);
|
|
8864
8922
|
}
|
|
8865
8923
|
async addFeedback(args) {
|
|
@@ -8875,14 +8933,15 @@ var Observability = class extends MastraBase {
|
|
|
8875
8933
|
return;
|
|
8876
8934
|
}
|
|
8877
8935
|
if (!args.traceId) return;
|
|
8878
|
-
const
|
|
8879
|
-
if (!trace) return;
|
|
8880
|
-
const event = buildRecordedFeedbackEventFromTrace({
|
|
8936
|
+
const event = await this.#buildRecordedEventWithRetry(args.traceId, (trace) => buildRecordedFeedbackEventFromTrace({
|
|
8881
8937
|
trace,
|
|
8882
8938
|
spanId: args.spanId,
|
|
8883
8939
|
feedback: args.feedback
|
|
8884
|
-
});
|
|
8885
|
-
if (!event)
|
|
8940
|
+
}));
|
|
8941
|
+
if (!event) {
|
|
8942
|
+
this.logger?.warn(`Feedback event was dropped because the target trace/span was not found in observability storage (traceId: ${args.traceId}, spanId: ${args.spanId})`);
|
|
8943
|
+
return;
|
|
8944
|
+
}
|
|
8886
8945
|
await this.#emitRecordedEvent(event);
|
|
8887
8946
|
}
|
|
8888
8947
|
/** Register a named observability instance, optionally marking it as default. */
|
|
@@ -8947,6 +9006,20 @@ var Observability = class extends MastraBase {
|
|
|
8947
9006
|
if (!storage) return null;
|
|
8948
9007
|
return await storage.getStore("observability") ?? null;
|
|
8949
9008
|
}
|
|
9009
|
+
/**
|
|
9010
|
+
* Build a recorded score/feedback event from storage, retrying briefly to
|
|
9011
|
+
* ride out the async exporter flush: annotations emitted right after a
|
|
9012
|
+
* span ends can otherwise race the flush and be silently dropped.
|
|
9013
|
+
*/
|
|
9014
|
+
async #buildRecordedEventWithRetry(traceId, build) {
|
|
9015
|
+
for (const delayMs of [0, ...RECORDED_TRACE_LOOKUP_RETRY_DELAYS_MS]) {
|
|
9016
|
+
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
9017
|
+
const trace = await this.#getStoredTrace(traceId);
|
|
9018
|
+
const event = trace ? build(trace) : null;
|
|
9019
|
+
if (event) return event;
|
|
9020
|
+
}
|
|
9021
|
+
return null;
|
|
9022
|
+
}
|
|
8950
9023
|
async #getStoredTrace(traceId) {
|
|
8951
9024
|
const observabilityStorage = await this.#getObservabilityStorage();
|
|
8952
9025
|
if (!observabilityStorage) return null;
|