@mastra/observability 1.20.0-alpha.0 → 1.20.0
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/default.d.ts.map +1 -1
- package/dist/exporters/default.d.ts +5 -0
- package/dist/exporters/default.d.ts.map +1 -1
- package/dist/exporters/mastra-storage.d.ts +5 -0
- package/dist/exporters/mastra-storage.d.ts.map +1 -1
- package/dist/index.cjs +120 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +120 -17
- package/dist/index.js.map +1 -1
- package/dist/metrics/models-dev.d.ts.map +1 -1
- package/dist/metrics/pricing-data.json +2879 -385
- package/dist/metrics/pricing-registry.d.ts +14 -0
- package/dist/metrics/pricing-registry.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1150,7 +1150,7 @@ const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
|
|
1150
1150
|
* not replace the pricing in use. The full catalog prices roughly 8,000 models.
|
|
1151
1151
|
*/
|
|
1152
1152
|
const MIN_PRICED_MODELS = 1e3;
|
|
1153
|
-
/** models.dev `cost` keys (USD per 1M tokens) mapped to pricing meters. */
|
|
1153
|
+
/** models.dev `cost` keys (USD per 1M tokens) mapped to pricing meters. Keyed by core's `ModelPricingRates` so the two cannot drift. */
|
|
1154
1154
|
const COST_KEY_TO_METER = {
|
|
1155
1155
|
input: "input_tokens",
|
|
1156
1156
|
output: "output_tokens",
|
|
@@ -1290,6 +1290,7 @@ const BEDROCK_GEOGRAPHY_PREFIXES = /* @__PURE__ */ new Set([
|
|
|
1290
1290
|
"au"
|
|
1291
1291
|
]);
|
|
1292
1292
|
const AI_SDK_VERCEL_GATEWAY_PROVIDER_ID = "gateway";
|
|
1293
|
+
const AZURE_OPENAI_GATEWAY_ID = "azure-openai";
|
|
1293
1294
|
const VERCEL_PRICING_PROVIDER_ID = "vercel";
|
|
1294
1295
|
const AI_SDK_PROVIDER_NAMESPACE_ALIASES = /* @__PURE__ */ new Map([
|
|
1295
1296
|
["fireworks", "fireworks-ai"],
|
|
@@ -1300,6 +1301,11 @@ const AI_SDK_PROVIDER_NAMESPACE_ALIASES = /* @__PURE__ */ new Map([
|
|
|
1300
1301
|
let cachedLoadError = null;
|
|
1301
1302
|
let globalRegistry = null;
|
|
1302
1303
|
let bundledRegistry = null;
|
|
1304
|
+
let gatewayRegistry = null;
|
|
1305
|
+
let gatewayCosts = {};
|
|
1306
|
+
let gatewaySource = null;
|
|
1307
|
+
const gatewayLoadAttempts = /* @__PURE__ */ new Map();
|
|
1308
|
+
let gatewayLoadInFlight = false;
|
|
1303
1309
|
let refreshedAt = 0;
|
|
1304
1310
|
let lastRefreshAttemptAt = 0;
|
|
1305
1311
|
let refreshEtag;
|
|
@@ -1328,10 +1334,10 @@ var PricingRegistry = class PricingRegistry {
|
|
|
1328
1334
|
return globalRegistry;
|
|
1329
1335
|
}
|
|
1330
1336
|
get(args) {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1337
|
+
if (this !== globalRegistry) return this.find(args);
|
|
1338
|
+
const match = this.find(args) ?? (this === bundledRegistry ? null : getBundledRegistry()?.find(args)) ?? gatewayRegistry?.find(args);
|
|
1339
|
+
if (match) return match;
|
|
1340
|
+
loadGatewayPricing();
|
|
1335
1341
|
refreshGlobalPricing();
|
|
1336
1342
|
return null;
|
|
1337
1343
|
}
|
|
@@ -1354,12 +1360,70 @@ function getBundledRegistry() {
|
|
|
1354
1360
|
return bundledRegistry;
|
|
1355
1361
|
}
|
|
1356
1362
|
/**
|
|
1363
|
+
* Where to find the model router gateways whose `pricing` fills the gaps in models.dev. Prices
|
|
1364
|
+
* load on the first lookup that has no price, so production does not call gateways at startup;
|
|
1365
|
+
* under the same switches as the models.dev refresh (`mastra dev`, or
|
|
1366
|
+
* `MASTRA_AUTO_REFRESH_PRICING=true`) they also load right away, off the hot path, so the first
|
|
1367
|
+
* model call is priced. Each gateway is read once; a failed read is retried at most once an hour.
|
|
1368
|
+
*/
|
|
1369
|
+
function setPricingGateways(source) {
|
|
1370
|
+
gatewaySource = source;
|
|
1371
|
+
if (isRefreshEnabled()) loadGatewayPricing();
|
|
1372
|
+
}
|
|
1373
|
+
/** Load `pricing` from every custom gateway not loaded yet. Off under `MASTRA_OFFLINE`. */
|
|
1374
|
+
async function loadGatewayPricing() {
|
|
1375
|
+
if (!gatewaySource || gatewayLoadInFlight || isOffline()) return;
|
|
1376
|
+
gatewayLoadInFlight = true;
|
|
1377
|
+
try {
|
|
1378
|
+
const builtInIds = await getBuiltInGatewayIds();
|
|
1379
|
+
let changed = false;
|
|
1380
|
+
for (const gateway of gatewaySource()) {
|
|
1381
|
+
const gatewayId = gateway.getId?.() ?? gateway.id;
|
|
1382
|
+
const lastAttempt = gatewayLoadAttempts.get(gateway) ?? 0;
|
|
1383
|
+
if (builtInIds.has(gatewayId) || Date.now() - lastAttempt < MIN_REFRESH_ATTEMPT_INTERVAL_MS) continue;
|
|
1384
|
+
try {
|
|
1385
|
+
const providers = await gateway.fetchProviders();
|
|
1386
|
+
for (const [providerId, config] of Object.entries(providers ?? {})) for (const [modelId, cost] of Object.entries(config?.pricing ?? {})) (gatewayCosts[providerId] ??= { models: {} }).models[modelId] = { cost };
|
|
1387
|
+
gatewayLoadAttempts.set(gateway, Infinity);
|
|
1388
|
+
changed = true;
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
gatewayLoadAttempts.set(gateway, Date.now());
|
|
1391
|
+
console.warn(`Failed to load model prices from gateway ${gatewayId}:`, error);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
if (changed) gatewayRegistry = new PricingRegistry(modelsDevToPricingModels(gatewayCosts));
|
|
1395
|
+
} finally {
|
|
1396
|
+
gatewayLoadInFlight = false;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* The gateways core ships: the ones Mastra registers on its own, plus Azure OpenAI, which users
|
|
1401
|
+
* construct themselves. Their `fetchProviders()` can hit the network and never carries prices.
|
|
1402
|
+
* Read from core when it exports the list (1.46+), else the known ids.
|
|
1403
|
+
*/
|
|
1404
|
+
async function getBuiltInGatewayIds() {
|
|
1405
|
+
try {
|
|
1406
|
+
const { defaultGateways, getGatewayId } = await import("@mastra/core/llm");
|
|
1407
|
+
return /* @__PURE__ */ new Set([...defaultGateways.map(getGatewayId), AZURE_OPENAI_GATEWAY_ID]);
|
|
1408
|
+
} catch {
|
|
1409
|
+
return /* @__PURE__ */ new Set([
|
|
1410
|
+
"models.dev",
|
|
1411
|
+
"netlify",
|
|
1412
|
+
"mastra",
|
|
1413
|
+
AZURE_OPENAI_GATEWAY_ID
|
|
1414
|
+
]);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
function isOffline() {
|
|
1418
|
+
const offline = process.env.MASTRA_OFFLINE;
|
|
1419
|
+
return offline === "true" || offline === "1";
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1357
1422
|
* Same switches as the model router's auto-refresh in `@mastra/core`: on by default only under
|
|
1358
1423
|
* `mastra dev` (`MASTRA_DEV`), opt-in elsewhere, and always off with `MASTRA_OFFLINE`.
|
|
1359
1424
|
*/
|
|
1360
1425
|
function isRefreshEnabled() {
|
|
1361
|
-
|
|
1362
|
-
if (offline === "true" || offline === "1") return false;
|
|
1426
|
+
if (isOffline()) return false;
|
|
1363
1427
|
const isDev = process.env.MASTRA_DEV === "true" || process.env.MASTRA_DEV === "1";
|
|
1364
1428
|
const setting = process.env.MASTRA_AUTO_REFRESH_PRICING;
|
|
1365
1429
|
return setting === "true" || setting !== "false" && isDev;
|
|
@@ -4000,6 +4064,8 @@ function resolveTracingStorageStrategy$1(config, observabilityStorage, storageNa
|
|
|
4000
4064
|
return observabilityStrategy.preferred;
|
|
4001
4065
|
}
|
|
4002
4066
|
const FLUSH_SUCCEEDED$1 = { failed: false };
|
|
4067
|
+
/** Max join/write rounds a single flush() makes before it returns under continuous traffic */
|
|
4068
|
+
const MAX_FLUSH_ROUNDS$1 = 3;
|
|
4003
4069
|
/**
|
|
4004
4070
|
* @deprecated Use `MastraStorageExporter` from `@mastra/observability` instead.
|
|
4005
4071
|
* This class is preserved unchanged so existing integrations (including code
|
|
@@ -4019,6 +4085,8 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4019
4085
|
#observabilityStorage;
|
|
4020
4086
|
#resolvedStrategy;
|
|
4021
4087
|
#flushTimer;
|
|
4088
|
+
/** Storage writes (timer, size trigger, or flush()) that haven't finished yet */
|
|
4089
|
+
#inFlightFlushes = /* @__PURE__ */ new Set();
|
|
4022
4090
|
#emitDropEvent;
|
|
4023
4091
|
#unsupportedSignals = /* @__PURE__ */ new Set();
|
|
4024
4092
|
constructor(config = {}) {
|
|
@@ -4093,7 +4161,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4093
4161
|
if (this.#flushTimer) clearTimeout(this.#flushTimer);
|
|
4094
4162
|
this.#flushTimer = setTimeout(() => {
|
|
4095
4163
|
this.#flushTimer = void 0;
|
|
4096
|
-
this.
|
|
4164
|
+
this.trackFlushBuffer().catch((error) => {
|
|
4097
4165
|
this.logger.error("Scheduled flush failed", { error: error instanceof Error ? error.message : String(error) });
|
|
4098
4166
|
});
|
|
4099
4167
|
}, delayMs);
|
|
@@ -4123,7 +4191,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4123
4191
|
* Returns the flush promise when flushing so callers can await it.
|
|
4124
4192
|
*/
|
|
4125
4193
|
async handleBatchedFlush() {
|
|
4126
|
-
if (this.shouldFlush()) await this.
|
|
4194
|
+
if (this.shouldFlush()) await this.trackFlushBuffer();
|
|
4127
4195
|
else if (this.#eventBuffer.totalSize === 1) this.scheduleFlush();
|
|
4128
4196
|
}
|
|
4129
4197
|
sanitizeDropError(error) {
|
|
@@ -4227,6 +4295,15 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4227
4295
|
}
|
|
4228
4296
|
}
|
|
4229
4297
|
/**
|
|
4298
|
+
* Runs flushBuffer() and tracks its promise so flush() can wait for writes
|
|
4299
|
+
* that a timer or size trigger started on their own.
|
|
4300
|
+
*/
|
|
4301
|
+
trackFlushBuffer() {
|
|
4302
|
+
const promise = this.flushBuffer().finally(() => this.#inFlightFlushes.delete(promise));
|
|
4303
|
+
this.#inFlightFlushes.add(promise);
|
|
4304
|
+
return promise;
|
|
4305
|
+
}
|
|
4306
|
+
/**
|
|
4230
4307
|
* Flushes the current buffer to storage.
|
|
4231
4308
|
*
|
|
4232
4309
|
* Creates are flushed first, then their span keys are added to allCreatedSpans.
|
|
@@ -4385,10 +4462,16 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4385
4462
|
* are exported before the runtime instance is terminated.
|
|
4386
4463
|
*/
|
|
4387
4464
|
async flush() {
|
|
4388
|
-
|
|
4389
|
-
this.
|
|
4390
|
-
|
|
4465
|
+
for (let round = 0; round < MAX_FLUSH_ROUNDS$1; round++) {
|
|
4466
|
+
if (this.#inFlightFlushes.size > 0) await Promise.allSettled([...this.#inFlightFlushes]);
|
|
4467
|
+
if (this.#eventBuffer.totalSize > 0) {
|
|
4468
|
+
this.logger.debug("Flushing buffered events", { bufferedEvents: this.#eventBuffer.totalSize });
|
|
4469
|
+
await this.trackFlushBuffer();
|
|
4470
|
+
}
|
|
4471
|
+
if (this.#inFlightFlushes.size === 0) return;
|
|
4391
4472
|
}
|
|
4473
|
+
await Promise.allSettled([...this.#inFlightFlushes]);
|
|
4474
|
+
this.logger.warn(`flush() stopped after ${MAX_FLUSH_ROUNDS$1} rounds; new writes kept starting`, { bufferedEventsLeftBehind: this.#eventBuffer.totalSize });
|
|
4392
4475
|
}
|
|
4393
4476
|
async shutdown() {
|
|
4394
4477
|
if (this.#flushTimer) {
|
|
@@ -4422,6 +4505,8 @@ function resolveTracingStorageStrategy(config, observabilityStorage, storageName
|
|
|
4422
4505
|
return observabilityStrategy.preferred;
|
|
4423
4506
|
}
|
|
4424
4507
|
const FLUSH_SUCCEEDED = { failed: false };
|
|
4508
|
+
/** Max join/write rounds a single flush() makes before it returns under continuous traffic */
|
|
4509
|
+
const MAX_FLUSH_ROUNDS = 3;
|
|
4425
4510
|
/**
|
|
4426
4511
|
* Storage-backed exporter. Buffers observability events and flushes them in
|
|
4427
4512
|
* batches to the configured ObservabilityStorage backend with retry support.
|
|
@@ -4436,6 +4521,8 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
4436
4521
|
#observabilityStorage;
|
|
4437
4522
|
#resolvedStrategy;
|
|
4438
4523
|
#flushTimer;
|
|
4524
|
+
/** Storage writes (timer, size trigger, or flush()) that haven't finished yet */
|
|
4525
|
+
#inFlightFlushes = /* @__PURE__ */ new Set();
|
|
4439
4526
|
#emitDropEvent;
|
|
4440
4527
|
#unsupportedSignals = /* @__PURE__ */ new Set();
|
|
4441
4528
|
constructor(config = {}) {
|
|
@@ -4525,7 +4612,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
4525
4612
|
if (this.#flushTimer) clearTimeout(this.#flushTimer);
|
|
4526
4613
|
this.#flushTimer = setTimeout(() => {
|
|
4527
4614
|
this.#flushTimer = void 0;
|
|
4528
|
-
this.
|
|
4615
|
+
this.trackFlushBuffer().catch((error) => {
|
|
4529
4616
|
this.logger.error("Scheduled flush failed", { error: error instanceof Error ? error.message : String(error) });
|
|
4530
4617
|
});
|
|
4531
4618
|
}, delayMs);
|
|
@@ -4555,7 +4642,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
4555
4642
|
* Returns the flush promise when flushing so callers can await it.
|
|
4556
4643
|
*/
|
|
4557
4644
|
async handleBatchedFlush() {
|
|
4558
|
-
if (this.shouldFlush()) await this.
|
|
4645
|
+
if (this.shouldFlush()) await this.trackFlushBuffer();
|
|
4559
4646
|
else if (this.#eventBuffer.totalSize === 1) this.scheduleFlush();
|
|
4560
4647
|
}
|
|
4561
4648
|
sanitizeDropError(error) {
|
|
@@ -4659,6 +4746,15 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
4659
4746
|
}
|
|
4660
4747
|
}
|
|
4661
4748
|
/**
|
|
4749
|
+
* Runs flushBuffer() and tracks its promise so flush() can wait for writes
|
|
4750
|
+
* that a timer or size trigger started on their own.
|
|
4751
|
+
*/
|
|
4752
|
+
trackFlushBuffer() {
|
|
4753
|
+
const promise = this.flushBuffer().finally(() => this.#inFlightFlushes.delete(promise));
|
|
4754
|
+
this.#inFlightFlushes.add(promise);
|
|
4755
|
+
return promise;
|
|
4756
|
+
}
|
|
4757
|
+
/**
|
|
4662
4758
|
* Flushes the current buffer to storage.
|
|
4663
4759
|
*
|
|
4664
4760
|
* Creates are flushed first, then their span keys are added to allCreatedSpans.
|
|
@@ -4806,10 +4902,16 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
4806
4902
|
* are exported before the runtime instance is terminated.
|
|
4807
4903
|
*/
|
|
4808
4904
|
async flush() {
|
|
4809
|
-
|
|
4810
|
-
this.
|
|
4811
|
-
|
|
4905
|
+
for (let round = 0; round < MAX_FLUSH_ROUNDS; round++) {
|
|
4906
|
+
if (this.#inFlightFlushes.size > 0) await Promise.allSettled([...this.#inFlightFlushes]);
|
|
4907
|
+
if (this.#eventBuffer.totalSize > 0) {
|
|
4908
|
+
this.logger.debug("Flushing buffered events", { bufferedEvents: this.#eventBuffer.totalSize });
|
|
4909
|
+
await this.trackFlushBuffer();
|
|
4910
|
+
}
|
|
4911
|
+
if (this.#inFlightFlushes.size === 0) return;
|
|
4812
4912
|
}
|
|
4913
|
+
await Promise.allSettled([...this.#inFlightFlushes]);
|
|
4914
|
+
this.logger.warn(`flush() stopped after ${MAX_FLUSH_ROUNDS} rounds; new writes kept starting`, { bufferedEventsLeftBehind: this.#eventBuffer.totalSize });
|
|
4813
4915
|
}
|
|
4814
4916
|
async shutdown() {
|
|
4815
4917
|
if (this.#flushTimer) {
|
|
@@ -9931,6 +10033,7 @@ var Observability = class extends MastraBase {
|
|
|
9931
10033
|
const instances = this.listInstances();
|
|
9932
10034
|
const { mastra } = options;
|
|
9933
10035
|
this.#mastra = mastra;
|
|
10036
|
+
setPricingGateways(() => Object.values(mastra.listGateways?.() ?? {}));
|
|
9934
10037
|
const mastraEnvironment = mastra.getEnvironment?.();
|
|
9935
10038
|
instances.forEach((instance) => {
|
|
9936
10039
|
instance.__setMastraEnvironment?.(mastraEnvironment);
|