@mastra/observability 1.12.0-alpha.0 → 1.12.0-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 +28 -0
- package/dist/bus/observability-bus.d.ts +21 -4
- package/dist/bus/observability-bus.d.ts.map +1 -1
- package/dist/bus/route-event.d.ts +8 -1
- package/dist/bus/route-event.d.ts.map +1 -1
- package/dist/default.d.ts.map +1 -1
- package/dist/exporters/default.d.ts +2 -0
- package/dist/exporters/default.d.ts.map +1 -1
- package/dist/exporters/event-buffer.d.ts +4 -4
- package/dist/exporters/event-buffer.d.ts.map +1 -1
- package/dist/features.d.ts +39 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/index.cjs +301 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +301 -45
- package/dist/index.js.map +1 -1
- package/dist/metrics/pricing-data.jsonl +706 -411
- package/dist/model-tracing.d.ts +34 -4
- package/dist/model-tracing.d.ts.map +1 -1
- package/dist/recorded.d.ts.map +1 -1
- package/dist/spans/base.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var fs = require('fs');
|
|
|
10
10
|
var module$1 = require('module');
|
|
11
11
|
var path = require('path');
|
|
12
12
|
var web = require('stream/web');
|
|
13
|
+
var features = require('@mastra/core/features');
|
|
13
14
|
|
|
14
15
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
16
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -45,6 +46,23 @@ function routeToHandler(handler, event, logger) {
|
|
|
45
46
|
if (handler.onScoreEvent) {
|
|
46
47
|
return catchAsyncResult(handler.onScoreEvent(event), handler.name, "score", logger);
|
|
47
48
|
}
|
|
49
|
+
if (handler.addScoreToTrace) {
|
|
50
|
+
const score = event.score;
|
|
51
|
+
if (!score.traceId) break;
|
|
52
|
+
return catchAsyncResult(
|
|
53
|
+
handler.addScoreToTrace({
|
|
54
|
+
traceId: score.traceId,
|
|
55
|
+
...score.spanId ? { spanId: score.spanId } : {},
|
|
56
|
+
score: score.score,
|
|
57
|
+
...score.reason ? { reason: score.reason } : {},
|
|
58
|
+
scorerName: score.scorerName ?? score.scorerId,
|
|
59
|
+
...score.metadata ? { metadata: score.metadata } : {}
|
|
60
|
+
}),
|
|
61
|
+
handler.name,
|
|
62
|
+
"score",
|
|
63
|
+
logger
|
|
64
|
+
);
|
|
65
|
+
}
|
|
48
66
|
break;
|
|
49
67
|
case "feedback":
|
|
50
68
|
if (handler.onFeedbackEvent) {
|
|
@@ -56,6 +74,15 @@ function routeToHandler(handler, event, logger) {
|
|
|
56
74
|
logger.error(`[Observability] Handler error [handler=${handler.name}]:`, err);
|
|
57
75
|
}
|
|
58
76
|
}
|
|
77
|
+
function routeDropToHandler(handler, event, logger) {
|
|
78
|
+
try {
|
|
79
|
+
if (handler.onDroppedEvent) {
|
|
80
|
+
return catchAsyncResult(handler.onDroppedEvent(event), handler.name, "drop", logger);
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
logger.error(`[Observability] Handler error [handler=${handler.name}]:`, err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
59
86
|
function catchAsyncResult(result, handlerName, signal, logger) {
|
|
60
87
|
if (result && typeof result.then === "function") {
|
|
61
88
|
return result.catch((err) => {
|
|
@@ -15686,31 +15713,39 @@ var EventBuffer = class {
|
|
|
15686
15713
|
break;
|
|
15687
15714
|
}
|
|
15688
15715
|
}
|
|
15689
|
-
/** Re-add failed create events to the buffer,
|
|
15716
|
+
/** Re-add failed create events to the buffer, returning events that exceed max retries. */
|
|
15690
15717
|
reAddCreates(events) {
|
|
15691
15718
|
const retryable = [];
|
|
15719
|
+
const dropped = [];
|
|
15692
15720
|
for (const e of events) {
|
|
15693
15721
|
if (++e.retryCount <= this.#maxRetries) {
|
|
15694
15722
|
retryable.push(e);
|
|
15723
|
+
} else {
|
|
15724
|
+
dropped.push(e);
|
|
15695
15725
|
}
|
|
15696
15726
|
}
|
|
15697
15727
|
if (retryable.length > 0) {
|
|
15698
15728
|
this.setFirstEventTime();
|
|
15699
15729
|
this.#creates.push(...retryable);
|
|
15700
15730
|
}
|
|
15731
|
+
return dropped;
|
|
15701
15732
|
}
|
|
15702
|
-
/** Re-add failed update events to the buffer,
|
|
15733
|
+
/** Re-add failed update events to the buffer, returning events that exceed max retries. */
|
|
15703
15734
|
reAddUpdates(events) {
|
|
15704
15735
|
const retryable = [];
|
|
15736
|
+
const dropped = [];
|
|
15705
15737
|
for (const e of events) {
|
|
15706
15738
|
if (++e.retryCount <= this.#maxRetries) {
|
|
15707
15739
|
retryable.push(e);
|
|
15740
|
+
} else {
|
|
15741
|
+
dropped.push(e);
|
|
15708
15742
|
}
|
|
15709
15743
|
}
|
|
15710
15744
|
if (retryable.length > 0) {
|
|
15711
15745
|
this.setFirstEventTime();
|
|
15712
15746
|
this.#updates.push(...retryable);
|
|
15713
15747
|
}
|
|
15748
|
+
return dropped;
|
|
15714
15749
|
}
|
|
15715
15750
|
/** Snapshot of buffered create events. */
|
|
15716
15751
|
get creates() {
|
|
@@ -15789,6 +15824,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
15789
15824
|
#observabilityStorage;
|
|
15790
15825
|
#resolvedStrategy;
|
|
15791
15826
|
#flushTimer;
|
|
15827
|
+
#emitDropEvent;
|
|
15792
15828
|
// Signals whose storage methods threw "not implemented" — skip on future flushes
|
|
15793
15829
|
#unsupportedSignals = /* @__PURE__ */ new Set();
|
|
15794
15830
|
constructor(config2 = {}) {
|
|
@@ -15810,6 +15846,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
15810
15846
|
async init(options) {
|
|
15811
15847
|
try {
|
|
15812
15848
|
this.#isInitializing = true;
|
|
15849
|
+
this.#emitDropEvent = options.emitDropEvent;
|
|
15813
15850
|
this.#storage = options.mastra?.getStorage();
|
|
15814
15851
|
if (!this.#storage) {
|
|
15815
15852
|
this.logger.warn("DefaultExporter disabled: Storage not available. Traces will not be persisted.");
|
|
@@ -15895,21 +15932,54 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
15895
15932
|
this.scheduleFlush();
|
|
15896
15933
|
}
|
|
15897
15934
|
}
|
|
15935
|
+
sanitizeDropError(error48) {
|
|
15936
|
+
if (error48 instanceof error$1.MastraError) {
|
|
15937
|
+
return {
|
|
15938
|
+
id: error48.id,
|
|
15939
|
+
domain: String(error48.domain),
|
|
15940
|
+
message: error48.message
|
|
15941
|
+
};
|
|
15942
|
+
}
|
|
15943
|
+
if (error48 instanceof Error) {
|
|
15944
|
+
return { message: error48.message };
|
|
15945
|
+
}
|
|
15946
|
+
return { message: String(error48) };
|
|
15947
|
+
}
|
|
15948
|
+
emitDrop(signal, reason, count, error48) {
|
|
15949
|
+
if (count === 0) return;
|
|
15950
|
+
const dropEvent = {
|
|
15951
|
+
type: "drop",
|
|
15952
|
+
signal,
|
|
15953
|
+
reason,
|
|
15954
|
+
count,
|
|
15955
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
15956
|
+
exporterName: this.name,
|
|
15957
|
+
...this.#observabilityStorage ? { storageName: this.#observabilityStorage.constructor.name } : {},
|
|
15958
|
+
...error48 === void 0 ? {} : { error: this.sanitizeDropError(error48) }
|
|
15959
|
+
};
|
|
15960
|
+
this.#emitDropEvent?.(dropEvent);
|
|
15961
|
+
}
|
|
15898
15962
|
/**
|
|
15899
15963
|
* Flush a batch of create events for a single signal type.
|
|
15900
15964
|
* On "not implemented" errors, disables the signal for future flushes.
|
|
15901
15965
|
* On other errors, re-adds events to the buffer for retry.
|
|
15902
15966
|
*/
|
|
15903
15967
|
async flushCreates(signal, events, storageCall) {
|
|
15904
|
-
if (
|
|
15968
|
+
if (events.length === 0) return;
|
|
15969
|
+
if (this.#unsupportedSignals.has(signal)) {
|
|
15970
|
+
this.emitDrop(signal, "unsupported-storage", events.length);
|
|
15971
|
+
return;
|
|
15972
|
+
}
|
|
15905
15973
|
try {
|
|
15906
15974
|
await storageCall(events);
|
|
15907
15975
|
} catch (error48) {
|
|
15908
15976
|
if (error48 instanceof error$1.MastraError && error48.domain === error$1.ErrorDomain.MASTRA_OBSERVABILITY && error48.id.endsWith("_NOT_IMPLEMENTED")) {
|
|
15909
15977
|
this.logger.warn(error48.message);
|
|
15910
15978
|
this.#unsupportedSignals.add(signal);
|
|
15979
|
+
this.emitDrop(signal, "unsupported-storage", events.length, error48);
|
|
15911
15980
|
} else {
|
|
15912
|
-
this.#eventBuffer.reAddCreates(events);
|
|
15981
|
+
const dropped = this.#eventBuffer.reAddCreates(events);
|
|
15982
|
+
this.emitDrop(signal, "retry-exhausted", dropped.length, error48);
|
|
15913
15983
|
}
|
|
15914
15984
|
}
|
|
15915
15985
|
}
|
|
@@ -15918,7 +15988,12 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
15918
15988
|
* When `isEnd` is true, successfully flushed spans are removed from tracking.
|
|
15919
15989
|
*/
|
|
15920
15990
|
async flushSpanUpdates(events, deferredUpdates, isEnd) {
|
|
15921
|
-
|
|
15991
|
+
const deferredCountAtEntry = deferredUpdates.length;
|
|
15992
|
+
if (events.length === 0) return;
|
|
15993
|
+
if (this.#unsupportedSignals.has("tracing")) {
|
|
15994
|
+
this.emitDrop("tracing", "unsupported-storage", events.length);
|
|
15995
|
+
return;
|
|
15996
|
+
}
|
|
15922
15997
|
const partials = [];
|
|
15923
15998
|
for (const event of events) {
|
|
15924
15999
|
const span = event.exportedSpan;
|
|
@@ -15942,9 +16017,12 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
15942
16017
|
if (error48 instanceof error$1.MastraError && error48.domain === error$1.ErrorDomain.MASTRA_OBSERVABILITY && error48.id.endsWith("_NOT_IMPLEMENTED")) {
|
|
15943
16018
|
this.logger.warn(error48.message);
|
|
15944
16019
|
this.#unsupportedSignals.add("tracing");
|
|
16020
|
+
deferredUpdates.length = 0;
|
|
16021
|
+
this.emitDrop("tracing", "unsupported-storage", events.length + deferredCountAtEntry, error48);
|
|
15945
16022
|
} else {
|
|
15946
16023
|
deferredUpdates.length = 0;
|
|
15947
|
-
this.#eventBuffer.reAddUpdates(events);
|
|
16024
|
+
const dropped = this.#eventBuffer.reAddUpdates(events);
|
|
16025
|
+
this.emitDrop("tracing", "retry-exhausted", dropped.length, error48);
|
|
15948
16026
|
}
|
|
15949
16027
|
}
|
|
15950
16028
|
}
|
|
@@ -16020,17 +16098,17 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
16020
16098
|
(events) => this.#observabilityStorage.batchCreateFeedback({ feedbacks: events.map((f) => storage.buildFeedbackRecord(f)) })
|
|
16021
16099
|
),
|
|
16022
16100
|
this.flushCreates(
|
|
16023
|
-
"
|
|
16101
|
+
"log",
|
|
16024
16102
|
createLogEvents,
|
|
16025
16103
|
(events) => this.#observabilityStorage.batchCreateLogs({ logs: events.map((l) => storage.buildLogRecord(l)) })
|
|
16026
16104
|
),
|
|
16027
16105
|
this.flushCreates(
|
|
16028
|
-
"
|
|
16106
|
+
"metric",
|
|
16029
16107
|
createMetricEvents,
|
|
16030
16108
|
(events) => this.#observabilityStorage.batchCreateMetrics({ metrics: events.map((m) => storage.buildMetricRecord(m)) })
|
|
16031
16109
|
),
|
|
16032
16110
|
this.flushCreates(
|
|
16033
|
-
"
|
|
16111
|
+
"score",
|
|
16034
16112
|
createScoreEvents,
|
|
16035
16113
|
(events) => this.#observabilityStorage.batchCreateScores({ scores: events.map((s) => storage.buildScoreRecord(s)) })
|
|
16036
16114
|
),
|
|
@@ -16044,7 +16122,13 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
16044
16122
|
await this.flushSpanUpdates(updateSpanEvents, deferredUpdates, false);
|
|
16045
16123
|
await this.flushSpanUpdates(endSpanEvents, deferredUpdates, true);
|
|
16046
16124
|
if (deferredUpdates.length > 0) {
|
|
16047
|
-
this.#
|
|
16125
|
+
if (this.#unsupportedSignals.has("tracing")) {
|
|
16126
|
+
this.emitDrop("tracing", "unsupported-storage", deferredUpdates.length);
|
|
16127
|
+
deferredUpdates.length = 0;
|
|
16128
|
+
} else {
|
|
16129
|
+
const dropped = this.#eventBuffer.reAddUpdates(deferredUpdates);
|
|
16130
|
+
this.emitDrop("tracing", "retry-exhausted", dropped.length);
|
|
16131
|
+
}
|
|
16048
16132
|
}
|
|
16049
16133
|
const elapsed = Date.now() - startTime;
|
|
16050
16134
|
this.logger.debug("Batch flushed", {
|
|
@@ -17673,6 +17757,8 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
|
|
|
17673
17757
|
bridge;
|
|
17674
17758
|
/** In-flight handler promises from routeToHandler. Self-cleaning via .finally(). */
|
|
17675
17759
|
pendingHandlers = /* @__PURE__ */ new Set();
|
|
17760
|
+
handlerBufferFlushDepth = 0;
|
|
17761
|
+
dropEventsEmittedDuringHandlerFlush = 0;
|
|
17676
17762
|
/** Resolved deepClean options applied to non-tracing events before fan-out. */
|
|
17677
17763
|
deepCleanOptions;
|
|
17678
17764
|
constructor(opts) {
|
|
@@ -17759,6 +17845,23 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
|
|
|
17759
17845
|
}
|
|
17760
17846
|
super.emit(cleaned);
|
|
17761
17847
|
}
|
|
17848
|
+
/**
|
|
17849
|
+
* Emit exporter pipeline drop events to exporters and the bridge.
|
|
17850
|
+
*
|
|
17851
|
+
* Drop events describe exporter health, not user observability data, so they
|
|
17852
|
+
* are intentionally not delivered to generic event-bus subscribers.
|
|
17853
|
+
*/
|
|
17854
|
+
emitDropEvent(event) {
|
|
17855
|
+
if (this.handlerBufferFlushDepth > 0) {
|
|
17856
|
+
this.dropEventsEmittedDuringHandlerFlush++;
|
|
17857
|
+
}
|
|
17858
|
+
for (const exporter of this.exporters) {
|
|
17859
|
+
this.trackPromise(routeDropToHandler(exporter, event, this.logger));
|
|
17860
|
+
}
|
|
17861
|
+
if (this.bridge) {
|
|
17862
|
+
this.trackPromise(routeDropToHandler(this.bridge, event, this.logger));
|
|
17863
|
+
}
|
|
17864
|
+
}
|
|
17762
17865
|
/**
|
|
17763
17866
|
* Track an async handler promise so flush() can await it.
|
|
17764
17867
|
* No-ops for sync (void) results.
|
|
@@ -17770,19 +17873,8 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
|
|
|
17770
17873
|
void promise2.finally(() => this.pendingHandlers.delete(promise2));
|
|
17771
17874
|
}
|
|
17772
17875
|
}
|
|
17773
|
-
/**
|
|
17774
|
-
|
|
17775
|
-
*
|
|
17776
|
-
* **Phase 1 — Delivery:** Await all in-flight handler promises (exporters,
|
|
17777
|
-
* bridge, and base-class subscribers). After this resolves, all event data
|
|
17778
|
-
* has been delivered to handler methods.
|
|
17779
|
-
*
|
|
17780
|
-
* **Phase 2 — Buffer drain:** Call flush() on each exporter and bridge to
|
|
17781
|
-
* drain their SDK-internal buffers (e.g., OTEL BatchSpanProcessor, Langfuse
|
|
17782
|
-
* client queue). Phases are sequential — Phase 2 must not start until
|
|
17783
|
-
* Phase 1 completes, otherwise exporters would flush empty buffers.
|
|
17784
|
-
*/
|
|
17785
|
-
async flush() {
|
|
17876
|
+
/** Await in-flight routed handler promises, draining until empty. */
|
|
17877
|
+
async drainPendingHandlers() {
|
|
17786
17878
|
let iterations = 0;
|
|
17787
17879
|
while (this.pendingHandlers.size > 0) {
|
|
17788
17880
|
await Promise.allSettled([...this.pendingHandlers]);
|
|
@@ -17797,14 +17889,54 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
|
|
|
17797
17889
|
break;
|
|
17798
17890
|
}
|
|
17799
17891
|
}
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17892
|
+
}
|
|
17893
|
+
/** Drain exporter and bridge SDK-internal buffers. */
|
|
17894
|
+
async flushHandlerBuffers() {
|
|
17895
|
+
const initialDropCount = this.dropEventsEmittedDuringHandlerFlush;
|
|
17896
|
+
this.handlerBufferFlushDepth++;
|
|
17897
|
+
try {
|
|
17898
|
+
const bufferFlushPromises = this.exporters.map((e) => e.flush());
|
|
17899
|
+
if (this.bridge) {
|
|
17900
|
+
bufferFlushPromises.push(this.bridge.flush());
|
|
17901
|
+
}
|
|
17902
|
+
if (bufferFlushPromises.length > 0) {
|
|
17903
|
+
await Promise.allSettled(bufferFlushPromises);
|
|
17904
|
+
}
|
|
17905
|
+
return this.dropEventsEmittedDuringHandlerFlush > initialDropCount;
|
|
17906
|
+
} finally {
|
|
17907
|
+
this.handlerBufferFlushDepth--;
|
|
17804
17908
|
}
|
|
17805
|
-
|
|
17806
|
-
|
|
17909
|
+
}
|
|
17910
|
+
/**
|
|
17911
|
+
* Multi-phase flush to ensure all observability data is fully exported.
|
|
17912
|
+
*
|
|
17913
|
+
* **Phase 1 — Delivery:** Await all in-flight handler promises (exporters,
|
|
17914
|
+
* bridge, and base-class subscribers). After this resolves, all event data
|
|
17915
|
+
* has been delivered to handler methods.
|
|
17916
|
+
*
|
|
17917
|
+
* **Phase 2 — Buffer drain:** Call flush() on each exporter and bridge to
|
|
17918
|
+
* drain their SDK-internal buffers (e.g., OTEL BatchSpanProcessor, Langfuse
|
|
17919
|
+
* client queue). Phases are sequential — buffer drains must not start until
|
|
17920
|
+
* delivery completes, otherwise exporters would flush empty buffers.
|
|
17921
|
+
*
|
|
17922
|
+
* Exporter flushes can emit drop events. When that happens, flush loops
|
|
17923
|
+
* through delivery and buffer drain again so alerting integrations that buffer
|
|
17924
|
+
* drop notifications are drained before returning.
|
|
17925
|
+
*/
|
|
17926
|
+
async flush() {
|
|
17927
|
+
await this.drainPendingHandlers();
|
|
17928
|
+
await super.flush();
|
|
17929
|
+
for (let iterations = 0; iterations < MAX_FLUSH_ITERATIONS; iterations++) {
|
|
17930
|
+
const emittedDropEvents = await this.flushHandlerBuffers();
|
|
17931
|
+
if (!emittedDropEvents && this.pendingHandlers.size === 0) {
|
|
17932
|
+
return;
|
|
17933
|
+
}
|
|
17934
|
+
await this.drainPendingHandlers();
|
|
17935
|
+
await super.flush();
|
|
17807
17936
|
}
|
|
17937
|
+
this.logger.error(
|
|
17938
|
+
`[ObservabilityBus] flush() exceeded ${MAX_FLUSH_ITERATIONS} buffer drain iterations. Handlers may be emitting drop events during every flush.`
|
|
17939
|
+
);
|
|
17808
17940
|
}
|
|
17809
17941
|
/** Flush all pending events and exporter buffers, then clear subscribers. */
|
|
17810
17942
|
async shutdown() {
|
|
@@ -18163,8 +18295,19 @@ function normalizeProvider(provider) {
|
|
|
18163
18295
|
return dotIndex !== -1 ? normalized.substring(0, dotIndex) : normalized;
|
|
18164
18296
|
}
|
|
18165
18297
|
function getModelVariants(model) {
|
|
18166
|
-
const
|
|
18167
|
-
|
|
18298
|
+
const variants = /* @__PURE__ */ new Set();
|
|
18299
|
+
const add = (v) => {
|
|
18300
|
+
variants.add(v);
|
|
18301
|
+
variants.add(stripDateSuffix(v));
|
|
18302
|
+
};
|
|
18303
|
+
add(model);
|
|
18304
|
+
add(model.replace(/\./g, "-"));
|
|
18305
|
+
add(model.replace(/[./]/g, "-"));
|
|
18306
|
+
const slashIndex = model.indexOf("/");
|
|
18307
|
+
if (slashIndex !== -1) {
|
|
18308
|
+
add(model.substring(slashIndex + 1));
|
|
18309
|
+
}
|
|
18310
|
+
return [...variants];
|
|
18168
18311
|
}
|
|
18169
18312
|
function stripDateSuffix(model) {
|
|
18170
18313
|
let stripped = model.replace(/-20\d{2}-\d{2}-\d{2}$/, "");
|
|
@@ -18607,6 +18750,9 @@ function sumDefinedValues(obj, keys) {
|
|
|
18607
18750
|
}
|
|
18608
18751
|
|
|
18609
18752
|
// src/model-tracing.ts
|
|
18753
|
+
function supportsModelInference() {
|
|
18754
|
+
return features.coreFeatures.has("model-inference-span");
|
|
18755
|
+
}
|
|
18610
18756
|
function formatPreviewLabel(label, fallback) {
|
|
18611
18757
|
return typeof label === "string" && label.length > 0 ? label : fallback;
|
|
18612
18758
|
}
|
|
@@ -18765,6 +18911,7 @@ function extractStepInput(payload) {
|
|
|
18765
18911
|
var ModelSpanTracker = class {
|
|
18766
18912
|
#modelSpan;
|
|
18767
18913
|
#currentStepSpan;
|
|
18914
|
+
#currentInferenceSpan;
|
|
18768
18915
|
#currentChunkSpan;
|
|
18769
18916
|
#currentChunkType;
|
|
18770
18917
|
#accumulator = {};
|
|
@@ -18776,9 +18923,18 @@ var ModelSpanTracker = class {
|
|
|
18776
18923
|
#deferStepClose = false;
|
|
18777
18924
|
/** Stored step-finish payload when defer mode is enabled */
|
|
18778
18925
|
#pendingStepFinishPayload;
|
|
18926
|
+
/** Static request-side context applied to every MODEL_INFERENCE span */
|
|
18927
|
+
#inferenceContext;
|
|
18779
18928
|
constructor(modelSpan) {
|
|
18780
18929
|
this.#modelSpan = modelSpan;
|
|
18781
18930
|
}
|
|
18931
|
+
/**
|
|
18932
|
+
* Set request-side context applied to subsequent MODEL_INFERENCE spans.
|
|
18933
|
+
* No-op when paired with an older @mastra/core that lacks the feature flag.
|
|
18934
|
+
*/
|
|
18935
|
+
setInferenceContext(context) {
|
|
18936
|
+
this.#inferenceContext = context;
|
|
18937
|
+
}
|
|
18782
18938
|
/**
|
|
18783
18939
|
* Capture the completion start time (time to first token) when the first content chunk arrives.
|
|
18784
18940
|
*/
|
|
@@ -18860,6 +19016,11 @@ var ModelSpanTracker = class {
|
|
|
18860
19016
|
* Start a new Model execution step.
|
|
18861
19017
|
* This should be called at the beginning of LLM execution to capture accurate startTime.
|
|
18862
19018
|
* The step-start chunk payload can be passed later via updateStep() if needed.
|
|
19019
|
+
*
|
|
19020
|
+
* Note: this only opens MODEL_STEP. The MODEL_INFERENCE child span is opened
|
|
19021
|
+
* separately via startInference() so its duration excludes input processor work.
|
|
19022
|
+
* Callers that don't call startInference() explicitly will get one auto-created
|
|
19023
|
+
* when the first model chunk arrives.
|
|
18863
19024
|
*/
|
|
18864
19025
|
startStep(payload) {
|
|
18865
19026
|
if (this.#currentStepSpan) {
|
|
@@ -18879,6 +19040,71 @@ var ModelSpanTracker = class {
|
|
|
18879
19040
|
this.#currentStepInputIsFinal = Array.isArray(payload?.inputMessages);
|
|
18880
19041
|
this.#chunkSequence = 0;
|
|
18881
19042
|
}
|
|
19043
|
+
/**
|
|
19044
|
+
* End the current MODEL_INFERENCE span when the provider stream finishes.
|
|
19045
|
+
* Fields are duplicated onto MODEL_STEP (in #endStepSpan) so existing
|
|
19046
|
+
* integrations that read usage/finishReason from the step span continue
|
|
19047
|
+
* to work unchanged.
|
|
19048
|
+
*
|
|
19049
|
+
* Safe to call multiple times - no-ops if the span is already closed.
|
|
19050
|
+
*/
|
|
19051
|
+
#endInferenceSpan(payload) {
|
|
19052
|
+
if (!this.#currentInferenceSpan) return;
|
|
19053
|
+
const { usage: rawUsage, ...otherOutput } = payload.output;
|
|
19054
|
+
const usage = extractUsageMetrics(rawUsage, payload.metadata?.providerMetadata);
|
|
19055
|
+
this.#currentInferenceSpan.end({
|
|
19056
|
+
output: otherOutput,
|
|
19057
|
+
attributes: {
|
|
19058
|
+
usage,
|
|
19059
|
+
finishReason: payload.stepResult.reason,
|
|
19060
|
+
warnings: payload.stepResult.warnings,
|
|
19061
|
+
completionStartTime: this.#completionStartTime
|
|
19062
|
+
}
|
|
19063
|
+
});
|
|
19064
|
+
this.#currentInferenceSpan = void 0;
|
|
19065
|
+
}
|
|
19066
|
+
/**
|
|
19067
|
+
* Open the MODEL_INFERENCE span for the current step. Chunks (including tool-call
|
|
19068
|
+
* chunks emitted by the model) parent under this span so its duration reflects
|
|
19069
|
+
* pure model latency.
|
|
19070
|
+
*
|
|
19071
|
+
* Should be called immediately before invoking the model — after any input
|
|
19072
|
+
* processors / `prepareStep` work has completed — so the span's startTime
|
|
19073
|
+
* does not include processor time. The latest `#inferenceContext` (set via
|
|
19074
|
+
* setInferenceContext) is snapshotted onto the span at creation.
|
|
19075
|
+
*
|
|
19076
|
+
* No-ops when the installed @mastra/core lacks the `model-inference-span`
|
|
19077
|
+
* feature flag, or when called without an active step span. Auto-invoked from
|
|
19078
|
+
* chunk handlers as a safety net; explicit callers get the most accurate
|
|
19079
|
+
* start time.
|
|
19080
|
+
*/
|
|
19081
|
+
startInference(payload) {
|
|
19082
|
+
if (!supportsModelInference()) {
|
|
19083
|
+
return;
|
|
19084
|
+
}
|
|
19085
|
+
if (!this.#currentStepSpan || this.#currentInferenceSpan) {
|
|
19086
|
+
return;
|
|
19087
|
+
}
|
|
19088
|
+
const input = extractStepInput(payload);
|
|
19089
|
+
const generationAttrs = this.#modelSpan?.attributes;
|
|
19090
|
+
const ctx = this.#inferenceContext;
|
|
19091
|
+
this.#currentInferenceSpan = this.#currentStepSpan.createChildSpan({
|
|
19092
|
+
name: `inference: ${this.#stepIndex}`,
|
|
19093
|
+
type: observability.SpanType.MODEL_INFERENCE,
|
|
19094
|
+
attributes: {
|
|
19095
|
+
stepIndex: this.#stepIndex,
|
|
19096
|
+
model: generationAttrs?.model,
|
|
19097
|
+
provider: generationAttrs?.provider,
|
|
19098
|
+
streaming: generationAttrs?.streaming,
|
|
19099
|
+
...ctx?.parameters !== void 0 ? { parameters: ctx.parameters } : {},
|
|
19100
|
+
...ctx?.providerOptions !== void 0 ? { providerOptions: ctx.providerOptions } : {},
|
|
19101
|
+
...ctx?.availableTools !== void 0 ? { availableTools: ctx.availableTools } : {},
|
|
19102
|
+
...ctx?.toolChoice !== void 0 ? { toolChoice: ctx.toolChoice } : {},
|
|
19103
|
+
...ctx?.responseFormat !== void 0 ? { responseFormat: ctx.responseFormat } : {}
|
|
19104
|
+
},
|
|
19105
|
+
input
|
|
19106
|
+
});
|
|
19107
|
+
}
|
|
18882
19108
|
/**
|
|
18883
19109
|
* Update the current step span with additional payload data.
|
|
18884
19110
|
* Called when step-start chunk arrives with request/warnings info.
|
|
@@ -18917,6 +19143,7 @@ var ModelSpanTracker = class {
|
|
|
18917
19143
|
delete cleanMetadata[key];
|
|
18918
19144
|
}
|
|
18919
19145
|
}
|
|
19146
|
+
this.#endInferenceSpan(payload);
|
|
18920
19147
|
this.#currentStepSpan.end({
|
|
18921
19148
|
output: otherOutput,
|
|
18922
19149
|
attributes: {
|
|
@@ -18934,14 +19161,35 @@ var ModelSpanTracker = class {
|
|
|
18934
19161
|
this.#stepIndex++;
|
|
18935
19162
|
}
|
|
18936
19163
|
/**
|
|
18937
|
-
*
|
|
19164
|
+
* Returns the parent span for chunks. Chunks parent under MODEL_INFERENCE
|
|
19165
|
+
* (the provider call) when available, falling back to MODEL_STEP only if
|
|
19166
|
+
* startStep() was bypassed.
|
|
18938
19167
|
*/
|
|
18939
|
-
#
|
|
18940
|
-
this.#
|
|
19168
|
+
#chunkParent() {
|
|
19169
|
+
return this.#currentInferenceSpan ?? this.#currentStepSpan;
|
|
19170
|
+
}
|
|
19171
|
+
/**
|
|
19172
|
+
* Safety-net invoked from chunk handlers: auto-create MODEL_STEP and
|
|
19173
|
+
* MODEL_INFERENCE if a chunk arrives before the loop has explicitly opened
|
|
19174
|
+
* them, so chunks parent under MODEL_INFERENCE rather than falling through
|
|
19175
|
+
* to MODEL_STEP. Idempotent — each public start* method is itself a no-op
|
|
19176
|
+
* when its span is already live.
|
|
19177
|
+
*/
|
|
19178
|
+
#ensureStepAndInference() {
|
|
18941
19179
|
if (!this.#currentStepSpan) {
|
|
18942
19180
|
this.startStep();
|
|
18943
19181
|
}
|
|
18944
|
-
this.#
|
|
19182
|
+
if (!this.#currentInferenceSpan) {
|
|
19183
|
+
this.startInference();
|
|
19184
|
+
}
|
|
19185
|
+
}
|
|
19186
|
+
/**
|
|
19187
|
+
* Create a new chunk span (for multi-part chunks like text-start/delta/end)
|
|
19188
|
+
*/
|
|
19189
|
+
#startChunkSpan(chunkType, initialData) {
|
|
19190
|
+
this.#endChunkSpan();
|
|
19191
|
+
this.#ensureStepAndInference();
|
|
19192
|
+
this.#currentChunkSpan = this.#chunkParent()?.createChildSpan({
|
|
18945
19193
|
name: `chunk: '${chunkType}'`,
|
|
18946
19194
|
type: observability.SpanType.MODEL_CHUNK,
|
|
18947
19195
|
attributes: {
|
|
@@ -18980,10 +19228,8 @@ var ModelSpanTracker = class {
|
|
|
18980
19228
|
* Create an event span (for single chunks like tool-call)
|
|
18981
19229
|
*/
|
|
18982
19230
|
#createEventSpan(chunkType, output, options) {
|
|
18983
|
-
|
|
18984
|
-
|
|
18985
|
-
}
|
|
18986
|
-
const span = this.#currentStepSpan?.createEventSpan({
|
|
19231
|
+
this.#ensureStepAndInference();
|
|
19232
|
+
const span = this.#chunkParent()?.createEventSpan({
|
|
18987
19233
|
name: `chunk: '${chunkType}'`,
|
|
18988
19234
|
type: observability.SpanType.MODEL_CHUNK,
|
|
18989
19235
|
attributes: {
|
|
@@ -19104,10 +19350,8 @@ var ModelSpanTracker = class {
|
|
|
19104
19350
|
#handleToolApprovalChunk(chunk) {
|
|
19105
19351
|
if (chunk.type !== "tool-call-approval") return;
|
|
19106
19352
|
const payload = chunk.payload;
|
|
19107
|
-
|
|
19108
|
-
|
|
19109
|
-
}
|
|
19110
|
-
const span = this.#currentStepSpan?.createEventSpan({
|
|
19353
|
+
this.#ensureStepAndInference();
|
|
19354
|
+
const span = this.#chunkParent()?.createEventSpan({
|
|
19111
19355
|
name: `chunk: 'tool-call-approval'`,
|
|
19112
19356
|
type: observability.SpanType.MODEL_CHUNK,
|
|
19113
19357
|
attributes: {
|
|
@@ -19165,10 +19409,15 @@ var ModelSpanTracker = class {
|
|
|
19165
19409
|
} else {
|
|
19166
19410
|
this.startStep(chunk.payload);
|
|
19167
19411
|
}
|
|
19412
|
+
if (!this.#currentInferenceSpan) {
|
|
19413
|
+
this.startInference(chunk.payload);
|
|
19414
|
+
}
|
|
19168
19415
|
break;
|
|
19169
19416
|
case "step-finish":
|
|
19170
19417
|
if (this.#deferStepClose) {
|
|
19171
19418
|
this.#pendingStepFinishPayload = chunk.payload;
|
|
19419
|
+
this.#endChunkSpan();
|
|
19420
|
+
this.#endInferenceSpan(chunk.payload);
|
|
19172
19421
|
} else {
|
|
19173
19422
|
this.#endStepSpan(chunk.payload);
|
|
19174
19423
|
}
|
|
@@ -19264,6 +19513,7 @@ function isSpanInternal(spanType, flags) {
|
|
|
19264
19513
|
// Model-related spans
|
|
19265
19514
|
case observability.SpanType.MODEL_GENERATION:
|
|
19266
19515
|
case observability.SpanType.MODEL_STEP:
|
|
19516
|
+
case observability.SpanType.MODEL_INFERENCE:
|
|
19267
19517
|
case observability.SpanType.MODEL_CHUNK:
|
|
19268
19518
|
return (flags & observability.InternalSpans.MODEL) !== 0;
|
|
19269
19519
|
// Default: never internal
|
|
@@ -20356,6 +20606,7 @@ function buildScoreEvent(args) {
|
|
|
20356
20606
|
traceId,
|
|
20357
20607
|
spanId,
|
|
20358
20608
|
scorerId: score.scorerId,
|
|
20609
|
+
scorerName: score.scorerName,
|
|
20359
20610
|
scorerVersion: score.scorerVersion,
|
|
20360
20611
|
source: score.source,
|
|
20361
20612
|
scoreSource: score.scoreSource,
|
|
@@ -20363,6 +20614,7 @@ function buildScoreEvent(args) {
|
|
|
20363
20614
|
reason: score.reason,
|
|
20364
20615
|
experimentId: score.experimentId,
|
|
20365
20616
|
scoreTraceId: score.scoreTraceId,
|
|
20617
|
+
targetEntityType: score.targetEntityType,
|
|
20366
20618
|
correlationContext,
|
|
20367
20619
|
metadata: mergeMetadata(inheritedMetadata, score.metadata)
|
|
20368
20620
|
}
|
|
@@ -20961,10 +21213,11 @@ var Observability = class extends base.MastraBase {
|
|
|
20961
21213
|
instance.__setMastraEnvironment?.(mastraEnvironment);
|
|
20962
21214
|
const config2 = instance.getConfig();
|
|
20963
21215
|
const exporters = instance.getExporters();
|
|
21216
|
+
const emitDropEvent = instance instanceof BaseObservabilityInstance ? (event) => instance.getObservabilityBus().emitDropEvent(event) : void 0;
|
|
20964
21217
|
exporters.forEach((exporter) => {
|
|
20965
21218
|
if ("init" in exporter && typeof exporter.init === "function") {
|
|
20966
21219
|
try {
|
|
20967
|
-
exporter.init({ mastra, config: config2 });
|
|
21220
|
+
exporter.init({ mastra, config: config2, emitDropEvent });
|
|
20968
21221
|
} catch (error48) {
|
|
20969
21222
|
this.logger?.warn("Failed to initialize observability exporter", {
|
|
20970
21223
|
exporterName: exporter.name,
|
|
@@ -21152,6 +21405,9 @@ var Observability = class extends base.MastraBase {
|
|
|
21152
21405
|
}
|
|
21153
21406
|
};
|
|
21154
21407
|
|
|
21408
|
+
// src/features.ts
|
|
21409
|
+
var observabilityFeatures = /* @__PURE__ */ new Set(["model-inference-span"]);
|
|
21410
|
+
|
|
21155
21411
|
// src/tracing-options.ts
|
|
21156
21412
|
function buildTracingOptions(...updaters) {
|
|
21157
21413
|
return updaters.reduce((opts, updater) => updater(opts), {});
|
|
@@ -21188,6 +21444,7 @@ exports.getExternalParentId = getExternalParentId;
|
|
|
21188
21444
|
exports.isSerializedMap = isSerializedMap;
|
|
21189
21445
|
exports.mergeSerializationOptions = mergeSerializationOptions;
|
|
21190
21446
|
exports.observabilityConfigValueSchema = observabilityConfigValueSchema;
|
|
21447
|
+
exports.observabilityFeatures = observabilityFeatures;
|
|
21191
21448
|
exports.observabilityInstanceConfigSchema = observabilityInstanceConfigSchema;
|
|
21192
21449
|
exports.observabilityRegistryConfigSchema = observabilityRegistryConfigSchema;
|
|
21193
21450
|
exports.reconstructSerializedMap = reconstructSerializedMap;
|