@mastra/observability 1.16.6-alpha.2 → 1.16.6-alpha.4
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/exporters/auth-failure-cooldown.d.ts +1 -1
- package/dist/exporters/auth-failure-cooldown.d.ts.map +1 -1
- package/dist/exporters/mastra-platform.d.ts +21 -0
- package/dist/exporters/mastra-platform.d.ts.map +1 -1
- package/dist/index.cjs +128 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +128 -14
- package/dist/index.js.map +1 -1
- package/dist/model-tracing.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2056,13 +2056,15 @@ var ModelSpanTracker = class {
|
|
|
2056
2056
|
if (!this.#currentInferenceSpan) return;
|
|
2057
2057
|
const { usage: rawUsage, ...otherOutput } = payload.output;
|
|
2058
2058
|
const usage = extractUsageMetrics(rawUsage, payload.metadata?.providerMetadata);
|
|
2059
|
+
const responseModel = typeof payload.metadata?.modelId === "string" ? payload.metadata.modelId : void 0;
|
|
2059
2060
|
this.#currentInferenceSpan.end({
|
|
2060
2061
|
output: otherOutput,
|
|
2061
2062
|
attributes: {
|
|
2062
2063
|
usage,
|
|
2063
2064
|
finishReason: payload.stepResult.reason,
|
|
2064
2065
|
warnings: payload.stepResult.warnings,
|
|
2065
|
-
completionStartTime: this.#completionStartTime
|
|
2066
|
+
completionStartTime: this.#completionStartTime,
|
|
2067
|
+
...responseModel?.trim() ? { responseModel } : {}
|
|
2066
2068
|
}
|
|
2067
2069
|
});
|
|
2068
2070
|
this.#currentInferenceSpan = void 0;
|
|
@@ -5176,15 +5178,15 @@ function isAuthFailureError(error) {
|
|
|
5176
5178
|
function isAuthFailureStatus(status) {
|
|
5177
5179
|
return AUTH_FAILURE_STATUSES.has(status);
|
|
5178
5180
|
}
|
|
5179
|
-
async function fetchWithAuthFailureHandling(url, options, maxRetries) {
|
|
5181
|
+
async function fetchWithAuthFailureHandling(url, options, maxRetries, callerShouldRetry) {
|
|
5180
5182
|
let authFailureStatus;
|
|
5181
5183
|
try {
|
|
5182
|
-
await fetchWithRetry(url, options, maxRetries, { shouldRetryResponse: (response) => {
|
|
5184
|
+
return await fetchWithRetry(url, options, maxRetries, { shouldRetryResponse: (response) => {
|
|
5183
5185
|
if (isAuthFailureStatus(response.status)) {
|
|
5184
5186
|
authFailureStatus = response.status;
|
|
5185
5187
|
return false;
|
|
5186
5188
|
}
|
|
5187
|
-
return true;
|
|
5189
|
+
return callerShouldRetry?.(response) ?? true;
|
|
5188
5190
|
} });
|
|
5189
5191
|
} catch (error) {
|
|
5190
5192
|
if (authFailureStatus !== void 0) throw new AuthFailureError(authFailureStatus, error);
|
|
@@ -6314,6 +6316,25 @@ const SIGNAL_PUBLISH_SUFFIXES = {
|
|
|
6314
6316
|
feedback: "/feedback/publish"
|
|
6315
6317
|
};
|
|
6316
6318
|
const DEFAULT_PLATFORM_SPAN_FILTER = (span) => span.type !== SpanType.MODEL_CHUNK;
|
|
6319
|
+
const QUOTA_EXCEEDED_STATUS = 402;
|
|
6320
|
+
const OBSERVABILITY_STATUS_HEADER = "x-mastra-observability";
|
|
6321
|
+
const OBSERVABILITY_DISABLED_VALUE = "disabled";
|
|
6322
|
+
const OBSERVABILITY_RETRY_AFTER_HEADER = "x-mastra-observability-retry-after";
|
|
6323
|
+
const DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS = 300;
|
|
6324
|
+
const MAX_QUOTA_PROBE_INTERVAL_SECONDS = Math.floor(2147483647 / 1e3);
|
|
6325
|
+
function isObservabilityDisabled(response) {
|
|
6326
|
+
return response.headers.get(OBSERVABILITY_STATUS_HEADER) === OBSERVABILITY_DISABLED_VALUE;
|
|
6327
|
+
}
|
|
6328
|
+
function isQuotaExceededResponse(response) {
|
|
6329
|
+
return response.status === QUOTA_EXCEEDED_STATUS || isObservabilityDisabled(response);
|
|
6330
|
+
}
|
|
6331
|
+
function parseQuotaRetryAfterSeconds(response) {
|
|
6332
|
+
const raw = response.headers.get(OBSERVABILITY_RETRY_AFTER_HEADER);
|
|
6333
|
+
if (!raw || !/^\d+$/.test(raw.trim())) return DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6334
|
+
const parsed = Number.parseInt(raw, 10);
|
|
6335
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6336
|
+
return Math.min(parsed, MAX_QUOTA_PROBE_INTERVAL_SECONDS);
|
|
6337
|
+
}
|
|
6317
6338
|
const SIGNAL_PUBLISH_SEGMENTS = {
|
|
6318
6339
|
traces: "spans",
|
|
6319
6340
|
logs: "logs",
|
|
@@ -6403,6 +6424,10 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6403
6424
|
buffer;
|
|
6404
6425
|
flushTimer = null;
|
|
6405
6426
|
inFlightFlushes = /* @__PURE__ */ new Set();
|
|
6427
|
+
quotaPaused = false;
|
|
6428
|
+
quotaProbeTimer = null;
|
|
6429
|
+
quotaProbeIntervalSeconds = DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6430
|
+
shuttingDown = false;
|
|
6406
6431
|
constructor(config = {}) {
|
|
6407
6432
|
super(config);
|
|
6408
6433
|
if (config.projectId !== void 0 && !VALID_PROJECT_ID.test(config.projectId)) throw createInvalidProjectIdError(config.projectId);
|
|
@@ -6451,31 +6476,31 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6451
6476
|
async _exportTracingEvent(event) {
|
|
6452
6477
|
if (event.type !== TracingEventType.SPAN_ENDED) return;
|
|
6453
6478
|
if (!DEFAULT_PLATFORM_SPAN_FILTER(event.exportedSpan)) return;
|
|
6454
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6479
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6455
6480
|
this.addToBuffer(event);
|
|
6456
6481
|
await this.handleBufferedEvent();
|
|
6457
6482
|
}
|
|
6458
6483
|
async onLogEvent(event) {
|
|
6459
6484
|
if (this.isDisabled) return;
|
|
6460
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6485
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6461
6486
|
this.addLogToBuffer(event);
|
|
6462
6487
|
await this.handleBufferedEvent();
|
|
6463
6488
|
}
|
|
6464
6489
|
async onMetricEvent(event) {
|
|
6465
6490
|
if (this.isDisabled) return;
|
|
6466
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6491
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6467
6492
|
this.addMetricToBuffer(event);
|
|
6468
6493
|
await this.handleBufferedEvent();
|
|
6469
6494
|
}
|
|
6470
6495
|
async onScoreEvent(event) {
|
|
6471
6496
|
if (this.isDisabled) return;
|
|
6472
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6497
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6473
6498
|
this.addScoreToBuffer(event);
|
|
6474
6499
|
await this.handleBufferedEvent();
|
|
6475
6500
|
}
|
|
6476
6501
|
async onFeedbackEvent(event) {
|
|
6477
6502
|
if (this.isDisabled) return;
|
|
6478
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6503
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6479
6504
|
this.addFeedbackToBuffer(event);
|
|
6480
6505
|
await this.handleBufferedEvent();
|
|
6481
6506
|
}
|
|
@@ -6565,6 +6590,10 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6565
6590
|
this.flushTimer = null;
|
|
6566
6591
|
}
|
|
6567
6592
|
if (this.buffer.totalSize === 0) return;
|
|
6593
|
+
if (this.quotaPaused) {
|
|
6594
|
+
this.resetBuffer();
|
|
6595
|
+
return;
|
|
6596
|
+
}
|
|
6568
6597
|
if (this.authFailureCooldown.dropEventsIfCoolingDown(this.buffer.totalSize)) {
|
|
6569
6598
|
this.resetBuffer();
|
|
6570
6599
|
return;
|
|
@@ -6614,11 +6643,16 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6614
6643
|
/**
|
|
6615
6644
|
* Uploads a signal batch to the configured Mastra Observability API using fetchWithRetry.
|
|
6616
6645
|
*/
|
|
6617
|
-
|
|
6618
|
-
|
|
6646
|
+
buildPublishHeaders() {
|
|
6647
|
+
return {
|
|
6619
6648
|
Authorization: `Bearer ${this.platformConfig.accessToken}`,
|
|
6620
6649
|
"Content-Type": "application/json"
|
|
6621
6650
|
};
|
|
6651
|
+
}
|
|
6652
|
+
buildPublishBody(signal, records) {
|
|
6653
|
+
return JSON.stringify({ [SIGNAL_PUBLISH_SEGMENTS[signal]]: records });
|
|
6654
|
+
}
|
|
6655
|
+
async batchUpload(signal, records) {
|
|
6622
6656
|
const endpointMap = {
|
|
6623
6657
|
traces: this.platformConfig.tracesEndpoint,
|
|
6624
6658
|
logs: this.platformConfig.logsEndpoint,
|
|
@@ -6628,10 +6662,85 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6628
6662
|
};
|
|
6629
6663
|
const options = {
|
|
6630
6664
|
method: "POST",
|
|
6631
|
-
headers,
|
|
6632
|
-
body:
|
|
6665
|
+
headers: this.buildPublishHeaders(),
|
|
6666
|
+
body: this.buildPublishBody(signal, records)
|
|
6633
6667
|
};
|
|
6634
|
-
|
|
6668
|
+
let quotaResponse;
|
|
6669
|
+
let response;
|
|
6670
|
+
try {
|
|
6671
|
+
response = await fetchWithAuthFailureHandling(endpointMap[signal], options, this.platformConfig.maxRetries, (res) => {
|
|
6672
|
+
if (isQuotaExceededResponse(res)) {
|
|
6673
|
+
quotaResponse = res;
|
|
6674
|
+
return false;
|
|
6675
|
+
}
|
|
6676
|
+
return true;
|
|
6677
|
+
});
|
|
6678
|
+
} catch (error) {
|
|
6679
|
+
if (quotaResponse) {
|
|
6680
|
+
this.enterQuotaPause(parseQuotaRetryAfterSeconds(quotaResponse));
|
|
6681
|
+
return;
|
|
6682
|
+
}
|
|
6683
|
+
throw error;
|
|
6684
|
+
}
|
|
6685
|
+
if (isObservabilityDisabled(response)) this.enterQuotaPause(parseQuotaRetryAfterSeconds(response));
|
|
6686
|
+
}
|
|
6687
|
+
/**
|
|
6688
|
+
* Enter the quota-exhausted paused state: drop buffered events, stop the
|
|
6689
|
+
* flush timer, and start probing until the collector stops rejecting with
|
|
6690
|
+
* 402. The gate is per-org, so a signal on any publish route pauses all
|
|
6691
|
+
* five signal types.
|
|
6692
|
+
*/
|
|
6693
|
+
enterQuotaPause(retryAfterSeconds) {
|
|
6694
|
+
this.quotaProbeIntervalSeconds = retryAfterSeconds;
|
|
6695
|
+
if (this.quotaPaused) return;
|
|
6696
|
+
this.quotaPaused = true;
|
|
6697
|
+
if (this.flushTimer) {
|
|
6698
|
+
clearTimeout(this.flushTimer);
|
|
6699
|
+
this.flushTimer = null;
|
|
6700
|
+
}
|
|
6701
|
+
this.resetBuffer();
|
|
6702
|
+
this.logger.warn(`Mastra observability paused: quota exhausted, dropping telemetry and probing every ${retryAfterSeconds}s`);
|
|
6703
|
+
this.scheduleQuotaProbe();
|
|
6704
|
+
}
|
|
6705
|
+
scheduleQuotaProbe() {
|
|
6706
|
+
if (this.shuttingDown) return;
|
|
6707
|
+
if (this.quotaProbeTimer) clearTimeout(this.quotaProbeTimer);
|
|
6708
|
+
this.quotaProbeTimer = setTimeout(() => {
|
|
6709
|
+
this.quotaProbeTimer = null;
|
|
6710
|
+
this.probeQuotaStatus();
|
|
6711
|
+
}, this.quotaProbeIntervalSeconds * 1e3);
|
|
6712
|
+
this.quotaProbeTimer.unref?.();
|
|
6713
|
+
}
|
|
6714
|
+
/**
|
|
6715
|
+
* Send an empty spans batch as a free probe. The collector treats an empty
|
|
6716
|
+
* batch as a no-op, and an exhausted org still gets the 402 on it. A 402
|
|
6717
|
+
* throws out of fetchWithRetry without the Response, so the retry predicate
|
|
6718
|
+
* captures it to keep the retry-after hint.
|
|
6719
|
+
*/
|
|
6720
|
+
async probeQuotaStatus() {
|
|
6721
|
+
if (this.shuttingDown || !this.quotaPaused) return;
|
|
6722
|
+
let quotaResponse;
|
|
6723
|
+
try {
|
|
6724
|
+
const response = await fetchWithRetry(this.platformConfig.tracesEndpoint, {
|
|
6725
|
+
method: "POST",
|
|
6726
|
+
headers: this.buildPublishHeaders(),
|
|
6727
|
+
body: this.buildPublishBody("traces", [])
|
|
6728
|
+
}, 1, { shouldRetryResponse: (res) => {
|
|
6729
|
+
if (isQuotaExceededResponse(res)) quotaResponse = res;
|
|
6730
|
+
return false;
|
|
6731
|
+
} });
|
|
6732
|
+
if (this.shuttingDown) return;
|
|
6733
|
+
if (!isObservabilityDisabled(response)) {
|
|
6734
|
+
this.quotaPaused = false;
|
|
6735
|
+
this.quotaProbeIntervalSeconds = DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6736
|
+
this.logger.warn("Mastra observability resumed: quota restored, exports re-enabled");
|
|
6737
|
+
return;
|
|
6738
|
+
}
|
|
6739
|
+
this.quotaProbeIntervalSeconds = parseQuotaRetryAfterSeconds(response);
|
|
6740
|
+
} catch {
|
|
6741
|
+
if (quotaResponse) this.quotaProbeIntervalSeconds = parseQuotaRetryAfterSeconds(quotaResponse);
|
|
6742
|
+
}
|
|
6743
|
+
this.scheduleQuotaProbe();
|
|
6635
6744
|
}
|
|
6636
6745
|
async flushSignalBatch(signal, records) {
|
|
6637
6746
|
if (records.length === 0) return {
|
|
@@ -6699,11 +6808,16 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6699
6808
|
}
|
|
6700
6809
|
}
|
|
6701
6810
|
async shutdown() {
|
|
6811
|
+
this.shuttingDown = true;
|
|
6702
6812
|
if (this.isDisabled) return;
|
|
6703
6813
|
if (this.flushTimer) {
|
|
6704
6814
|
clearTimeout(this.flushTimer);
|
|
6705
6815
|
this.flushTimer = null;
|
|
6706
6816
|
}
|
|
6817
|
+
if (this.quotaProbeTimer) {
|
|
6818
|
+
clearTimeout(this.quotaProbeTimer);
|
|
6819
|
+
this.quotaProbeTimer = null;
|
|
6820
|
+
}
|
|
6707
6821
|
try {
|
|
6708
6822
|
await this.flush();
|
|
6709
6823
|
} catch (error) {
|