@mastra/observability 1.17.1-alpha.0 → 1.17.1-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 +18 -0
- package/README.md +3 -1
- package/dist/index.cjs +104 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +104 -13
- package/dist/index.js.map +1 -1
- package/dist/metrics/estimator.d.ts.map +1 -1
- package/dist/metrics/pricing-registry.d.ts.map +1 -1
- package/dist/metrics/types.d.ts +4 -0
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/metrics/usage-metrics.d.ts.map +1 -1
- package/dist/spans/serialization.d.ts.map +1 -1
- package/dist/usage.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -192,6 +192,12 @@ const LOGGER_METHODS = [
|
|
|
192
192
|
"warn",
|
|
193
193
|
"error"
|
|
194
194
|
];
|
|
195
|
+
/** Keys whose value decides the outcome in `shouldStripEntry()`. */
|
|
196
|
+
const VALUE_STRIP_KEYS = /* @__PURE__ */ new Set([
|
|
197
|
+
"logger",
|
|
198
|
+
"tracingContext",
|
|
199
|
+
...FUNCTION_KEYS_TO_STRIP
|
|
200
|
+
]);
|
|
195
201
|
const DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
|
|
196
202
|
keysToStrip: [],
|
|
197
203
|
maxDepth: 8,
|
|
@@ -273,6 +279,25 @@ function shouldStripEntry(key, value, stripSet) {
|
|
|
273
279
|
if (key === "logger") return typeof value === "function" || isLoggerLike(value);
|
|
274
280
|
return FUNCTION_KEYS_TO_STRIP.has(key) && typeof value === "function";
|
|
275
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Whether an entry past the object-key limit would have been stripped anyway.
|
|
284
|
+
*
|
|
285
|
+
* The entry is dropped either way, so the value is taken from its property descriptor
|
|
286
|
+
* rather than read: a getter must not run for data that is being discarded. That keeps
|
|
287
|
+
* the truncation count the same wherever a runtime-shaped key sits. An accessor cannot
|
|
288
|
+
* be classified without invoking it, so it is left to the limit and counted.
|
|
289
|
+
*/
|
|
290
|
+
function wouldBeStripped(key, val, stripSet) {
|
|
291
|
+
if (!VALUE_STRIP_KEYS.has(key)) return false;
|
|
292
|
+
let descriptor;
|
|
293
|
+
try {
|
|
294
|
+
descriptor = Object.getOwnPropertyDescriptor(val, key);
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
if (!descriptor || !("value" in descriptor)) return false;
|
|
299
|
+
return shouldStripEntry(key, descriptor.value, stripSet);
|
|
300
|
+
}
|
|
276
301
|
function restoreSerializedMapKey(keyType, key) {
|
|
277
302
|
switch (keyType) {
|
|
278
303
|
case "undefined": return;
|
|
@@ -458,25 +483,22 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
|
|
|
458
483
|
return formatSerializationError(error);
|
|
459
484
|
}
|
|
460
485
|
let keyCount = 0;
|
|
486
|
+
let omittedByLimit = 0;
|
|
461
487
|
for (const key of keys) {
|
|
462
488
|
if (stripSet.has(key)) continue;
|
|
489
|
+
if (keyCount >= maxObjectKeys) {
|
|
490
|
+
if (!wouldBeStripped(key, val, stripSet)) omittedByLimit++;
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
463
493
|
let rawValue;
|
|
464
494
|
try {
|
|
465
495
|
rawValue = val[key];
|
|
466
496
|
} catch (error) {
|
|
467
|
-
if (keyCount >= maxObjectKeys) {
|
|
468
|
-
cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
|
|
469
|
-
break;
|
|
470
|
-
}
|
|
471
497
|
cleaned[key] = formatSerializationError(error);
|
|
472
498
|
keyCount++;
|
|
473
499
|
continue;
|
|
474
500
|
}
|
|
475
501
|
if (shouldStripEntry(key, rawValue, stripSet)) continue;
|
|
476
|
-
if (keyCount >= maxObjectKeys) {
|
|
477
|
-
cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
502
|
try {
|
|
481
503
|
cleaned[key] = helper(rawValue, depth + 1);
|
|
482
504
|
keyCount++;
|
|
@@ -485,6 +507,7 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
|
|
|
485
507
|
keyCount++;
|
|
486
508
|
}
|
|
487
509
|
}
|
|
510
|
+
if (omittedByLimit > 0) cleaned["__truncated"] = `${omittedByLimit} more keys omitted`;
|
|
488
511
|
return cleaned;
|
|
489
512
|
} finally {
|
|
490
513
|
if (typeof val === "object" && val !== null) ancestors.delete(val);
|
|
@@ -1077,6 +1100,8 @@ const MINIFIED_METER_TO_CANONICAL = {
|
|
|
1077
1100
|
ot: "output_tokens",
|
|
1078
1101
|
icrt: "input_cache_read_tokens",
|
|
1079
1102
|
icwt: "input_cache_write_tokens",
|
|
1103
|
+
icwt5m: "input_cache_write_5m_tokens",
|
|
1104
|
+
icwt1h: "input_cache_write_1h_tokens",
|
|
1080
1105
|
iat: "input_audio_tokens",
|
|
1081
1106
|
oat: "output_audio_tokens",
|
|
1082
1107
|
ort: "output_reasoning_tokens"
|
|
@@ -1133,6 +1158,15 @@ function parsePricingModelText(content) {
|
|
|
1133
1158
|
}
|
|
1134
1159
|
return pricingModels;
|
|
1135
1160
|
}
|
|
1161
|
+
function expandRates(row, tier) {
|
|
1162
|
+
const rates = Object.fromEntries(Object.entries(tier.r).map(([meter, value]) => [MINIFIED_METER_TO_CANONICAL[meter], value.c]));
|
|
1163
|
+
const cacheWriteRate = rates.input_cache_write_tokens;
|
|
1164
|
+
if (row.m.includes("claude") && typeof cacheWriteRate === "number") {
|
|
1165
|
+
rates.input_cache_write_5m_tokens ??= cacheWriteRate;
|
|
1166
|
+
rates.input_cache_write_1h_tokens ??= cacheWriteRate * 1.6;
|
|
1167
|
+
}
|
|
1168
|
+
return rates;
|
|
1169
|
+
}
|
|
1136
1170
|
function expandPricingModelRow(row) {
|
|
1137
1171
|
return new PricingModel({
|
|
1138
1172
|
id: row.i,
|
|
@@ -1147,7 +1181,7 @@ function expandPricingModelRow(row) {
|
|
|
1147
1181
|
op: condition.op,
|
|
1148
1182
|
value: condition.value
|
|
1149
1183
|
})),
|
|
1150
|
-
rates:
|
|
1184
|
+
rates: expandRates(row, tier)
|
|
1151
1185
|
}))
|
|
1152
1186
|
});
|
|
1153
1187
|
}
|
|
@@ -1278,6 +1312,8 @@ const PricingMeter = {
|
|
|
1278
1312
|
INPUT_AUDIO_TOKENS: "input_audio_tokens",
|
|
1279
1313
|
INPUT_CACHE_READ_TOKENS: "input_cache_read_tokens",
|
|
1280
1314
|
INPUT_CACHE_WRITE_TOKENS: "input_cache_write_tokens",
|
|
1315
|
+
INPUT_CACHE_WRITE_5M_TOKENS: "input_cache_write_5m_tokens",
|
|
1316
|
+
INPUT_CACHE_WRITE_1H_TOKENS: "input_cache_write_1h_tokens",
|
|
1281
1317
|
INPUT_IMAGE_TOKENS: "input_image_tokens",
|
|
1282
1318
|
OUTPUT_TOKENS: "output_tokens",
|
|
1283
1319
|
OUTPUT_AUDIO_TOKENS: "output_audio_tokens",
|
|
@@ -1290,6 +1326,8 @@ const TokenMetrics = {
|
|
|
1290
1326
|
INPUT_TEXT: "mastra_model_input_text_tokens",
|
|
1291
1327
|
INPUT_CACHE_READ: "mastra_model_input_cache_read_tokens",
|
|
1292
1328
|
INPUT_CACHE_WRITE: "mastra_model_input_cache_write_tokens",
|
|
1329
|
+
INPUT_CACHE_WRITE_5M: "mastra_model_input_cache_write_5m_tokens",
|
|
1330
|
+
INPUT_CACHE_WRITE_1H: "mastra_model_input_cache_write_1h_tokens",
|
|
1293
1331
|
INPUT_AUDIO: "mastra_model_input_audio_tokens",
|
|
1294
1332
|
INPUT_IMAGE: "mastra_model_input_image_tokens",
|
|
1295
1333
|
OUTPUT_TEXT: "mastra_model_output_text_tokens",
|
|
@@ -1319,6 +1357,8 @@ function getTokenMetricSamples(usage) {
|
|
|
1319
1357
|
pushIfPositive(TokenMetrics.INPUT_TEXT, usage.inputDetails.text);
|
|
1320
1358
|
pushIfPositive(TokenMetrics.INPUT_CACHE_READ, usage.inputDetails.cacheRead);
|
|
1321
1359
|
pushIfPositive(TokenMetrics.INPUT_CACHE_WRITE, usage.inputDetails.cacheWrite);
|
|
1360
|
+
pushIfPositive(TokenMetrics.INPUT_CACHE_WRITE_5M, usage.inputDetails.cacheWrite5m);
|
|
1361
|
+
pushIfPositive(TokenMetrics.INPUT_CACHE_WRITE_1H, usage.inputDetails.cacheWrite1h);
|
|
1322
1362
|
pushIfPositive(TokenMetrics.INPUT_AUDIO, usage.inputDetails.audio);
|
|
1323
1363
|
pushIfPositive(TokenMetrics.INPUT_IMAGE, usage.inputDetails.image);
|
|
1324
1364
|
}
|
|
@@ -1385,15 +1425,40 @@ function estimateCosts(args, pricingRegistry = PricingRegistry.getGlobal()) {
|
|
|
1385
1425
|
results.set(TokenMetrics.INPUT_CACHE_READ, result.costContext);
|
|
1386
1426
|
inputDetailResults.push(result);
|
|
1387
1427
|
}
|
|
1388
|
-
|
|
1428
|
+
const cacheWriteDetailResults = [];
|
|
1429
|
+
const cacheWrite5m = usage.inputDetails?.cacheWrite5m ?? 0;
|
|
1430
|
+
const cacheWrite1h = usage.inputDetails?.cacheWrite1h ?? 0;
|
|
1431
|
+
if (cacheWrite5m > 0) {
|
|
1432
|
+
const result = estimateCostForMeter({
|
|
1433
|
+
meter: PricingMeter.INPUT_CACHE_WRITE_5M_TOKENS,
|
|
1434
|
+
tokenCount: cacheWrite5m,
|
|
1435
|
+
...estimateFields
|
|
1436
|
+
});
|
|
1437
|
+
results.set(TokenMetrics.INPUT_CACHE_WRITE_5M, result.costContext);
|
|
1438
|
+
cacheWriteDetailResults.push(result);
|
|
1439
|
+
inputDetailResults.push(result);
|
|
1440
|
+
}
|
|
1441
|
+
if (cacheWrite1h > 0) {
|
|
1442
|
+
const result = estimateCostForMeter({
|
|
1443
|
+
meter: PricingMeter.INPUT_CACHE_WRITE_1H_TOKENS,
|
|
1444
|
+
tokenCount: cacheWrite1h,
|
|
1445
|
+
...estimateFields
|
|
1446
|
+
});
|
|
1447
|
+
results.set(TokenMetrics.INPUT_CACHE_WRITE_1H, result.costContext);
|
|
1448
|
+
cacheWriteDetailResults.push(result);
|
|
1449
|
+
inputDetailResults.push(result);
|
|
1450
|
+
}
|
|
1451
|
+
const unclassifiedCacheWrite = Math.max(0, (usage.inputDetails?.cacheWrite ?? 0) - cacheWrite5m - cacheWrite1h);
|
|
1452
|
+
if (unclassifiedCacheWrite > 0) {
|
|
1389
1453
|
const result = estimateCostForMeter({
|
|
1390
1454
|
meter: PricingMeter.INPUT_CACHE_WRITE_TOKENS,
|
|
1391
|
-
tokenCount:
|
|
1455
|
+
tokenCount: unclassifiedCacheWrite,
|
|
1392
1456
|
...estimateFields
|
|
1393
1457
|
});
|
|
1394
|
-
|
|
1458
|
+
cacheWriteDetailResults.push(result);
|
|
1395
1459
|
inputDetailResults.push(result);
|
|
1396
1460
|
}
|
|
1461
|
+
if (cacheWriteDetailResults.length > 0) setCombinedCostContext(results, TokenMetrics.INPUT_CACHE_WRITE, cacheWriteDetailResults, pricingModel, costMetadata);
|
|
1397
1462
|
if (usage.inputDetails?.image) {
|
|
1398
1463
|
const result = estimateCostForMeter({
|
|
1399
1464
|
meter: PricingMeter.INPUT_IMAGE_TOKENS,
|
|
@@ -1470,6 +1535,20 @@ function estimateCosts(args, pricingRegistry = PricingRegistry.getGlobal()) {
|
|
|
1470
1535
|
function applyErrorContextForUsage(results, usage, errorContext) {
|
|
1471
1536
|
for (const sample of getTokenMetricSamples(usage)) results.set(sample.name, errorContext);
|
|
1472
1537
|
}
|
|
1538
|
+
function setCombinedCostContext(results, metric, detailResults, pricingModel, costMetadata) {
|
|
1539
|
+
const estimatedCosts = detailResults.map((result) => result.costContext.estimatedCost).filter((value) => typeof value === "number");
|
|
1540
|
+
const hasFailedCost = detailResults.some((result) => !result.success);
|
|
1541
|
+
results.set(metric, {
|
|
1542
|
+
provider: pricingModel.provider,
|
|
1543
|
+
model: pricingModel.model,
|
|
1544
|
+
...estimatedCosts.length > 0 && { estimatedCost: estimatedCosts.reduce((sum, value) => sum + value, 0) },
|
|
1545
|
+
...estimatedCosts.length > 0 && { costUnit: pricingModel.currency },
|
|
1546
|
+
costMetadata: hasFailedCost ? {
|
|
1547
|
+
...costMetadata,
|
|
1548
|
+
error: "partial_cost"
|
|
1549
|
+
} : { ...costMetadata }
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1473
1552
|
function setAggregateCostContext(args) {
|
|
1474
1553
|
const { results, totalMetric, fallbackMeter, totalTokenCount, detailResults, pricingModel, pricingTier, costMetadata } = args;
|
|
1475
1554
|
if (totalTokenCount == null) return;
|
|
@@ -1710,14 +1789,24 @@ function extractUsageMetrics(usage, providerMetadata) {
|
|
|
1710
1789
|
if (isDefined(aiSdkDetails?.cacheReadTokens)) inputDetails.cacheRead = aiSdkDetails.cacheReadTokens;
|
|
1711
1790
|
if (isDefined(aiSdkDetails?.cacheWriteTokens)) inputDetails.cacheWrite = aiSdkDetails.cacheWriteTokens;
|
|
1712
1791
|
if (!isDefined(inputDetails.cacheRead) && isDefined(usage.cachedInputTokens)) inputDetails.cacheRead = usage.cachedInputTokens;
|
|
1792
|
+
if (isDefined(usage.cacheCreationInputTokens5m)) inputDetails.cacheWrite5m = usage.cacheCreationInputTokens5m;
|
|
1793
|
+
if (isDefined(usage.cacheCreationInputTokens1h)) inputDetails.cacheWrite1h = usage.cacheCreationInputTokens1h;
|
|
1713
1794
|
if (!isDefined(inputDetails.cacheWrite) && isDefined(usage.cacheCreationInputTokens)) inputDetails.cacheWrite = usage.cacheCreationInputTokens;
|
|
1795
|
+
if (!isDefined(inputDetails.cacheWrite) && (isDefined(inputDetails.cacheWrite5m) || isDefined(inputDetails.cacheWrite1h))) inputDetails.cacheWrite = (inputDetails.cacheWrite5m ?? 0) + (inputDetails.cacheWrite1h ?? 0);
|
|
1714
1796
|
if (isDefined(usage.reasoningTokens)) outputDetails.reasoning = usage.reasoningTokens;
|
|
1715
1797
|
const anthropic = providerMetadata?.anthropic;
|
|
1716
1798
|
if (anthropic) {
|
|
1717
1799
|
const rawV3InputUsage = isV3RawUsage(usage.raw) ? usage.raw.inputTokens : void 0;
|
|
1718
1800
|
const hasV3CachedTotals = rawV3InputUsage?.total !== void 0 && (rawV3InputUsage.cacheRead !== void 0 || rawV3InputUsage.cacheWrite !== void 0);
|
|
1719
1801
|
if (!isDefined(inputDetails.cacheRead) && isDefined(anthropic.cacheReadInputTokens)) inputDetails.cacheRead = anthropic.cacheReadInputTokens;
|
|
1720
|
-
|
|
1802
|
+
const cacheWrite5m = anthropic.cacheCreation?.ephemeral_5m_input_tokens ?? anthropic.cacheCreation?.ephemeral5mInputTokens;
|
|
1803
|
+
const cacheWrite1h = anthropic.cacheCreation?.ephemeral_1h_input_tokens ?? anthropic.cacheCreation?.ephemeral1hInputTokens;
|
|
1804
|
+
if (!isDefined(inputDetails.cacheWrite5m) && isDefined(cacheWrite5m)) inputDetails.cacheWrite5m = cacheWrite5m;
|
|
1805
|
+
if (!isDefined(inputDetails.cacheWrite1h) && isDefined(cacheWrite1h)) inputDetails.cacheWrite1h = cacheWrite1h;
|
|
1806
|
+
if (!isDefined(inputDetails.cacheWrite)) {
|
|
1807
|
+
if (isDefined(anthropic.cacheCreationInputTokens)) inputDetails.cacheWrite = anthropic.cacheCreationInputTokens;
|
|
1808
|
+
else if (isDefined(cacheWrite5m) || isDefined(cacheWrite1h)) inputDetails.cacheWrite = (cacheWrite5m ?? 0) + (cacheWrite1h ?? 0);
|
|
1809
|
+
}
|
|
1721
1810
|
if (!(hasV3CachedTotals || isDefined(usage.cachedInputTokens) && usage.cachedInputTokens > 0 || isDefined(usage.cacheCreationInputTokens) && usage.cacheCreationInputTokens > 0) && (isDefined(inputDetails.cacheRead) || isDefined(inputDetails.cacheWrite))) inputTokens = (usage.inputTokens ?? 0) + (inputDetails.cacheRead ?? 0) + (inputDetails.cacheWrite ?? 0);
|
|
1722
1811
|
}
|
|
1723
1812
|
const google = providerMetadata?.google;
|
|
@@ -1758,6 +1847,8 @@ function mergeInputDetails(a, b) {
|
|
|
1758
1847
|
text: addOptional(a.text, b.text),
|
|
1759
1848
|
cacheRead: addOptional(a.cacheRead, b.cacheRead),
|
|
1760
1849
|
cacheWrite: addOptional(a.cacheWrite, b.cacheWrite),
|
|
1850
|
+
cacheWrite5m: addOptional(a.cacheWrite5m, b.cacheWrite5m),
|
|
1851
|
+
cacheWrite1h: addOptional(a.cacheWrite1h, b.cacheWrite1h),
|
|
1761
1852
|
audio: addOptional(a.audio, b.audio),
|
|
1762
1853
|
image: addOptional(a.image, b.image)
|
|
1763
1854
|
};
|