@copilotz/admin 0.9.36 → 0.9.37

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/index.cjs CHANGED
@@ -63,6 +63,7 @@ __export(index_exports, {
63
63
  formatUsageBucket: () => formatUsageBucket,
64
64
  getUsageDimensionLabel: () => getUsageDimensionLabel,
65
65
  getUsageGroupLabel: () => getUsageGroupLabel,
66
+ getUsageMetricLabel: () => getUsageMetricLabel,
66
67
  getUsageRange: () => getUsageRange,
67
68
  getUsageTotalValue: () => getUsageTotalValue,
68
69
  mergeAdminConfig: () => mergeAdminConfig,
@@ -217,12 +218,16 @@ function createAdminClient(options = {}) {
217
218
  metric: filters.metric,
218
219
  groupBy: filters.groupBy,
219
220
  attribution: filters.attribution,
221
+ kind: filters.kind,
220
222
  threadId: filters.threadId,
221
223
  participantId: filters.participantId,
222
224
  participantType: filters.participantType,
223
225
  namespace: filters.namespace,
224
226
  provider: filters.provider,
225
- model: filters.model
227
+ model: filters.model,
228
+ resource: filters.resource,
229
+ operation: filters.operation,
230
+ status: filters.status
226
231
  }),
227
232
  listThreads: async (listOptions = {}) => await requestJson(`${paths.adminBase}/threads`, {
228
233
  search: listOptions.search,
@@ -1469,7 +1474,11 @@ var EMPTY_USAGE_TOTALS = {
1469
1474
  reasoningTokens: 0,
1470
1475
  totalCalls: 0,
1471
1476
  totalCostUsd: 0,
1472
- totalTokens: 0
1477
+ failedCalls: 0,
1478
+ totalCredits: 0,
1479
+ totalDurationMs: 0,
1480
+ totalTokens: 0,
1481
+ unpricedCalls: 0
1473
1482
  };
1474
1483
  var USAGE_CHART_COLORS = [
1475
1484
  "hsl(var(--primary))",
@@ -1480,53 +1489,66 @@ var USAGE_CHART_COLORS = [
1480
1489
  "hsl(var(--muted-foreground))"
1481
1490
  ];
1482
1491
  function addUsageTotals(target, source) {
1483
- target.cacheCreationInputCostUsd += source.cacheCreationInputCostUsd;
1484
- target.cacheCreationInputTokens += source.cacheCreationInputTokens;
1485
- target.cacheReadInputCostUsd += source.cacheReadInputCostUsd;
1486
- target.cacheReadInputTokens += source.cacheReadInputTokens;
1487
- target.inputCostUsd += source.inputCostUsd;
1488
- target.inputTokens += source.inputTokens;
1489
- target.outputCostUsd += source.outputCostUsd;
1490
- target.outputTokens += source.outputTokens;
1491
- target.reasoningCostUsd += source.reasoningCostUsd;
1492
- target.reasoningTokens += source.reasoningTokens;
1493
- target.totalCalls += source.totalCalls;
1494
- target.totalCostUsd += source.totalCostUsd;
1495
- target.totalTokens += source.totalTokens;
1492
+ target.cacheCreationInputCostUsd += safeUsageNumber(source.cacheCreationInputCostUsd);
1493
+ target.cacheCreationInputTokens += safeUsageNumber(source.cacheCreationInputTokens);
1494
+ target.cacheReadInputCostUsd += safeUsageNumber(source.cacheReadInputCostUsd);
1495
+ target.cacheReadInputTokens += safeUsageNumber(source.cacheReadInputTokens);
1496
+ target.inputCostUsd += safeUsageNumber(source.inputCostUsd);
1497
+ target.inputTokens += safeUsageNumber(source.inputTokens);
1498
+ target.outputCostUsd += safeUsageNumber(source.outputCostUsd);
1499
+ target.outputTokens += safeUsageNumber(source.outputTokens);
1500
+ target.reasoningCostUsd += safeUsageNumber(source.reasoningCostUsd);
1501
+ target.reasoningTokens += safeUsageNumber(source.reasoningTokens);
1502
+ target.totalCalls += safeUsageNumber(source.totalCalls);
1503
+ target.totalCostUsd += safeUsageNumber(source.totalCostUsd);
1504
+ target.failedCalls += safeUsageNumber(source.failedCalls);
1505
+ target.totalCredits += safeUsageNumber(source.totalCredits);
1506
+ target.totalDurationMs += safeUsageNumber(source.totalDurationMs);
1507
+ target.totalTokens += safeUsageNumber(source.totalTokens);
1508
+ target.unpricedCalls += safeUsageNumber(source.unpricedCalls);
1509
+ }
1510
+ function safeUsageNumber(value) {
1511
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
1496
1512
  }
1497
1513
  function getUsageTotalValue(totals, metricKind, dimension) {
1498
- if (metricKind === "calls") return totals.totalCalls;
1514
+ if (metricKind === "calls") return safeUsageNumber(totals.totalCalls);
1515
+ if (metricKind === "duration") {
1516
+ return safeUsageNumber(totals.totalDurationMs);
1517
+ }
1518
+ if (metricKind === "credits") return safeUsageNumber(totals.totalCredits);
1519
+ if (metricKind === "failures") return safeUsageNumber(totals.failedCalls);
1520
+ if (metricKind === "unpriced") return safeUsageNumber(totals.unpricedCalls);
1499
1521
  if (metricKind === "cost") {
1500
1522
  switch (dimension) {
1501
1523
  case "input":
1502
- return totals.inputCostUsd;
1524
+ return safeUsageNumber(totals.inputCostUsd);
1503
1525
  case "output":
1504
- return totals.outputCostUsd;
1526
+ return safeUsageNumber(totals.outputCostUsd);
1505
1527
  case "reasoning":
1506
- return totals.reasoningCostUsd;
1528
+ return safeUsageNumber(totals.reasoningCostUsd);
1507
1529
  case "cacheRead":
1508
- return totals.cacheReadInputCostUsd;
1530
+ return safeUsageNumber(totals.cacheReadInputCostUsd);
1509
1531
  case "cacheWrite":
1510
- return totals.cacheCreationInputCostUsd;
1532
+ return safeUsageNumber(totals.cacheCreationInputCostUsd);
1511
1533
  case "total":
1512
1534
  default:
1513
- return totals.totalCostUsd;
1535
+ return safeUsageNumber(totals.totalCostUsd);
1514
1536
  }
1515
1537
  }
1516
1538
  switch (dimension) {
1517
1539
  case "input":
1518
- return totals.inputTokens;
1540
+ return safeUsageNumber(totals.inputTokens);
1519
1541
  case "output":
1520
- return totals.outputTokens;
1542
+ return safeUsageNumber(totals.outputTokens);
1521
1543
  case "reasoning":
1522
- return totals.reasoningTokens;
1544
+ return safeUsageNumber(totals.reasoningTokens);
1523
1545
  case "cacheRead":
1524
- return totals.cacheReadInputTokens;
1546
+ return safeUsageNumber(totals.cacheReadInputTokens);
1525
1547
  case "cacheWrite":
1526
- return totals.cacheCreationInputTokens;
1548
+ return safeUsageNumber(totals.cacheCreationInputTokens);
1527
1549
  case "total":
1528
1550
  default:
1529
- return totals.totalTokens;
1551
+ return safeUsageNumber(totals.totalTokens);
1530
1552
  }
1531
1553
  }
1532
1554
  function aggregateUsageRows(points, metricKind, dimension) {
@@ -1638,6 +1660,14 @@ function getUsageDimensionLabel(dimension) {
1638
1660
  }
1639
1661
  function getUsageGroupLabel(groupBy) {
1640
1662
  switch (groupBy) {
1663
+ case "kind":
1664
+ return "Kind";
1665
+ case "resource":
1666
+ return "Resource";
1667
+ case "operation":
1668
+ return "Operation";
1669
+ case "status":
1670
+ return "Status";
1641
1671
  case "thread":
1642
1672
  return "Thread";
1643
1673
  case "namespace":
@@ -1651,6 +1681,25 @@ function getUsageGroupLabel(groupBy) {
1651
1681
  return "Participant";
1652
1682
  }
1653
1683
  }
1684
+ function getUsageMetricLabel(metricKind) {
1685
+ switch (metricKind) {
1686
+ case "cost":
1687
+ return "Spend";
1688
+ case "tokens":
1689
+ return "Tokens";
1690
+ case "duration":
1691
+ return "Duration";
1692
+ case "credits":
1693
+ return "Credits";
1694
+ case "failures":
1695
+ return "Failures";
1696
+ case "unpriced":
1697
+ return "Unpriced";
1698
+ case "calls":
1699
+ default:
1700
+ return "Calls";
1701
+ }
1702
+ }
1654
1703
  function formatUsageBucket(bucket, interval) {
1655
1704
  const date = new Date(bucket);
1656
1705
  if (Number.isNaN(date.getTime())) return bucket;
@@ -1693,6 +1742,9 @@ function formatMetricValue(value, metricKind) {
1693
1742
  style: "currency"
1694
1743
  }).format(value);
1695
1744
  }
1745
+ if (metricKind === "duration") {
1746
+ return formatDuration(value);
1747
+ }
1696
1748
  return formatNumber(value);
1697
1749
  }
1698
1750
  function formatCompactMetric(value, metricKind) {
@@ -1705,12 +1757,30 @@ function formatCompactMetric(value, metricKind) {
1705
1757
  style: "currency"
1706
1758
  }).format(value);
1707
1759
  }
1760
+ if (metricKind === "duration") {
1761
+ if (value >= 36e5) return `${(value / 36e5).toFixed(1)}h`;
1762
+ if (value >= 6e4) return `${(value / 6e4).toFixed(1)}m`;
1763
+ if (value >= 1e3) return `${(value / 1e3).toFixed(1)}s`;
1764
+ return `${Math.round(value)}ms`;
1765
+ }
1708
1766
  return new Intl.NumberFormat(void 0, {
1709
1767
  compactDisplay: "short",
1710
1768
  maximumFractionDigits: 1,
1711
1769
  notation: "compact"
1712
1770
  }).format(value);
1713
1771
  }
1772
+ function formatDuration(value) {
1773
+ if (value >= 36e5) {
1774
+ return `${new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value / 36e5)} h`;
1775
+ }
1776
+ if (value >= 6e4) {
1777
+ return `${new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value / 6e4)} min`;
1778
+ }
1779
+ if (value >= 1e3) {
1780
+ return `${new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value / 1e3)} sec`;
1781
+ }
1782
+ return `${formatNumber(Math.round(value))} ms`;
1783
+ }
1714
1784
 
1715
1785
  // src/modules/agents/index.tsx
1716
1786
  var import_jsx_runtime20 = require("react/jsx-runtime");
@@ -3138,6 +3208,22 @@ var USAGE_DIMENSIONS = [
3138
3208
  "cacheRead",
3139
3209
  "cacheWrite"
3140
3210
  ];
3211
+ var USAGE_WORKLOADS = [
3212
+ "all",
3213
+ "llm",
3214
+ "tool",
3215
+ "asset",
3216
+ "rag",
3217
+ "embedding"
3218
+ ];
3219
+ var STATUS_OPTIONS = [
3220
+ "all",
3221
+ "completed",
3222
+ "failed",
3223
+ "cancelled",
3224
+ "aborted",
3225
+ "error"
3226
+ ];
3141
3227
  function usageModule() {
3142
3228
  return {
3143
3229
  group: "operate",
@@ -3164,11 +3250,12 @@ function UsagePage({ context }) {
3164
3250
  "7d"
3165
3251
  );
3166
3252
  const [interval, setInterval] = import_react8.default.useState("day");
3253
+ const [workload, setWorkload] = import_react8.default.useState("all");
3167
3254
  const [metricKind, setMetricKind] = import_react8.default.useState(
3168
- "cost"
3255
+ "calls"
3169
3256
  );
3170
3257
  const [dimension, setDimension] = import_react8.default.useState("total");
3171
- const [groupBy, setGroupBy] = import_react8.default.useState("participant");
3258
+ const [groupBy, setGroupBy] = import_react8.default.useState("kind");
3172
3259
  const [attribution, setAttribution] = import_react8.default.useState(
3173
3260
  "initiatedBy"
3174
3261
  );
@@ -3177,6 +3264,11 @@ function UsagePage({ context }) {
3177
3264
  const [participantId, setParticipantId] = import_react8.default.useState("");
3178
3265
  const [provider, setProvider] = import_react8.default.useState("");
3179
3266
  const [model, setModel] = import_react8.default.useState("");
3267
+ const [resource, setResource] = import_react8.default.useState("");
3268
+ const [operation, setOperation] = import_react8.default.useState("");
3269
+ const [status, setStatus] = import_react8.default.useState(
3270
+ "all"
3271
+ );
3180
3272
  const [customFrom, setCustomFrom] = import_react8.default.useState("");
3181
3273
  const [customTo, setCustomTo] = import_react8.default.useState("");
3182
3274
  const [usage, setUsage] = import_react8.default.useState(null);
@@ -3186,6 +3278,32 @@ function UsagePage({ context }) {
3186
3278
  () => getUsageRange(period, customFrom, customTo),
3187
3279
  [customFrom, customTo, period]
3188
3280
  );
3281
+ const metricOptions = import_react8.default.useMemo(
3282
+ () => getUsageMetricOptions(workload),
3283
+ [workload]
3284
+ );
3285
+ const groupOptions = import_react8.default.useMemo(
3286
+ () => getUsageGroupOptions(workload),
3287
+ [workload]
3288
+ );
3289
+ const showLlmFields = workload === "llm";
3290
+ const showResourceFields = workload !== "llm";
3291
+ const showDimension = metricKind === "tokens" || metricKind === "cost" && workload === "llm";
3292
+ import_react8.default.useEffect(() => {
3293
+ if (!metricOptions.includes(metricKind)) {
3294
+ setMetricKind(metricOptions[0]);
3295
+ }
3296
+ }, [metricKind, metricOptions]);
3297
+ import_react8.default.useEffect(() => {
3298
+ if (!groupOptions.includes(groupBy)) {
3299
+ setGroupBy(groupOptions[0]);
3300
+ }
3301
+ }, [groupBy, groupOptions]);
3302
+ import_react8.default.useEffect(() => {
3303
+ if (!showDimension && dimension !== "total") {
3304
+ setDimension("total");
3305
+ }
3306
+ }, [dimension, showDimension]);
3189
3307
  import_react8.default.useEffect(() => {
3190
3308
  let active = true;
3191
3309
  setIsLoading(true);
@@ -3194,13 +3312,17 @@ function UsagePage({ context }) {
3194
3312
  attribution,
3195
3313
  from: range.from,
3196
3314
  groupBy,
3315
+ kind: workload,
3197
3316
  interval,
3198
3317
  metric: metricKind,
3199
- model: emptyToUndefined(model),
3318
+ model: showLlmFields ? emptyToUndefined(model) : void 0,
3200
3319
  namespace: context.scope.namespace || void 0,
3320
+ operation: showResourceFields ? emptyToUndefined(operation) : void 0,
3201
3321
  participantId: emptyToUndefined(participantId),
3202
3322
  participantType,
3203
- provider: emptyToUndefined(provider),
3323
+ provider: showLlmFields ? emptyToUndefined(provider) : void 0,
3324
+ resource: showResourceFields ? emptyToUndefined(resource) : void 0,
3325
+ status: status === "all" ? void 0 : status,
3204
3326
  threadId: emptyToUndefined(threadId),
3205
3327
  to: range.to
3206
3328
  }).then((next) => {
@@ -3225,12 +3347,18 @@ function UsagePage({ context }) {
3225
3347
  interval,
3226
3348
  metricKind,
3227
3349
  model,
3350
+ operation,
3228
3351
  participantId,
3229
3352
  participantType,
3230
3353
  provider,
3231
3354
  range.from,
3232
3355
  range.to,
3233
- threadId
3356
+ resource,
3357
+ showLlmFields,
3358
+ showResourceFields,
3359
+ status,
3360
+ threadId,
3361
+ workload
3234
3362
  ]);
3235
3363
  const points = usage?.points ?? [];
3236
3364
  const totals = usage?.totals ?? EMPTY_USAGE_TOTALS;
@@ -3247,9 +3375,10 @@ function UsagePage({ context }) {
3247
3375
  PageHeader,
3248
3376
  {
3249
3377
  title: "Usage",
3250
- description: "Inspect cost, token, and call attribution across namespaces, models, participants, threads, and providers.",
3378
+ description: "Inspect metered LLM, tool, and resource activity by workload, actor, status, cost, duration, tokens, and credits.",
3251
3379
  badges: [
3252
- { label: period },
3380
+ { label: workload },
3381
+ { label: getUsageMetricLabel(metricKind), variant: "secondary" },
3253
3382
  { label: getUsageGroupLabel(groupBy), variant: "secondary" },
3254
3383
  ...isLoading ? [{ label: "Loading" }] : []
3255
3384
  ]
@@ -3277,10 +3406,21 @@ function UsagePage({ context }) {
3277
3406
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3278
3407
  UsageSelect,
3279
3408
  {
3280
- label: "Metric",
3409
+ label: "Workload",
3410
+ onValueChange: (value) => setWorkload(value),
3411
+ options: USAGE_WORKLOADS,
3412
+ value: workload,
3413
+ formatOption: getUsageKindLabel
3414
+ }
3415
+ ),
3416
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3417
+ UsageSelect,
3418
+ {
3419
+ label: "Measure",
3281
3420
  onValueChange: (value) => setMetricKind(value),
3282
- options: ["cost", "tokens", "calls"],
3283
- value: metricKind
3421
+ options: metricOptions,
3422
+ value: metricKind,
3423
+ formatOption: getUsageMetricLabel
3284
3424
  }
3285
3425
  ),
3286
3426
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
@@ -3288,20 +3428,22 @@ function UsagePage({ context }) {
3288
3428
  {
3289
3429
  label: "Group",
3290
3430
  onValueChange: (value) => setGroupBy(value),
3291
- options: ["participant", "thread", "namespace", "provider", "model"],
3292
- value: groupBy
3431
+ options: groupOptions,
3432
+ value: groupBy,
3433
+ formatOption: getUsageGroupLabel
3293
3434
  }
3294
3435
  ),
3295
3436
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3296
3437
  UsageSelect,
3297
3438
  {
3298
- label: "Attribution",
3439
+ label: "Actor",
3299
3440
  onValueChange: (value) => setAttribution(value),
3300
3441
  options: ["initiatedBy", "generatedBy"],
3301
- value: attribution
3442
+ value: attribution,
3443
+ formatOption: getUsageAttributionLabel
3302
3444
  }
3303
3445
  ),
3304
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3446
+ showDimension && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3305
3447
  UsageSelect,
3306
3448
  {
3307
3449
  label: "Dimension",
@@ -3336,12 +3478,21 @@ function UsagePage({ context }) {
3336
3478
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3337
3479
  UsageSelect,
3338
3480
  {
3339
- label: "Type",
3481
+ label: "Actor type",
3340
3482
  onValueChange: (value) => setParticipantType(value),
3341
3483
  options: ["all", "human", "agent", "job"],
3342
3484
  value: participantType
3343
3485
  }
3344
3486
  ),
3487
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3488
+ UsageSelect,
3489
+ {
3490
+ label: "Status",
3491
+ onValueChange: (value) => setStatus(value),
3492
+ options: STATUS_OPTIONS,
3493
+ value: status
3494
+ }
3495
+ ),
3345
3496
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3346
3497
  Input,
3347
3498
  {
@@ -3360,46 +3511,86 @@ function UsagePage({ context }) {
3360
3511
  value: participantId
3361
3512
  }
3362
3513
  ),
3363
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3364
- Input,
3365
- {
3366
- className: "h-8 w-[140px]",
3367
- onChange: (event) => setProvider(event.target.value),
3368
- placeholder: "Provider",
3369
- value: provider
3370
- }
3371
- ),
3372
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3373
- Input,
3374
- {
3375
- className: "h-8 w-[140px]",
3376
- onChange: (event) => setModel(event.target.value),
3377
- placeholder: "Model",
3378
- value: model
3379
- }
3380
- )
3514
+ showResourceFields && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3515
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3516
+ Input,
3517
+ {
3518
+ className: "h-8 w-[170px]",
3519
+ onChange: (event) => setResource(event.target.value),
3520
+ placeholder: "Resource / tool",
3521
+ value: resource
3522
+ }
3523
+ ),
3524
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3525
+ Input,
3526
+ {
3527
+ className: "h-8 w-[150px]",
3528
+ onChange: (event) => setOperation(event.target.value),
3529
+ placeholder: "Operation",
3530
+ value: operation
3531
+ }
3532
+ )
3533
+ ] }),
3534
+ showLlmFields && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3535
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3536
+ Input,
3537
+ {
3538
+ className: "h-8 w-[140px]",
3539
+ onChange: (event) => setProvider(event.target.value),
3540
+ placeholder: "Provider",
3541
+ value: provider
3542
+ }
3543
+ ),
3544
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3545
+ Input,
3546
+ {
3547
+ className: "h-8 w-[140px]",
3548
+ onChange: (event) => setModel(event.target.value),
3549
+ placeholder: "Model",
3550
+ value: model
3551
+ }
3552
+ )
3553
+ ] })
3381
3554
  ] }),
3382
3555
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3383
3556
  MetricStrip,
3384
3557
  {
3385
3558
  items: [
3386
3559
  {
3387
- detail: `${totals.totalCalls} calls`,
3560
+ detail: `${formatMetricValue(totals.unpricedCalls, "unpriced")} unpriced`,
3388
3561
  icon: import_lucide_react12.DollarSign,
3389
- label: "Cost",
3562
+ label: "Spend",
3390
3563
  value: formatMetricValue(totals.totalCostUsd, "cost")
3391
3564
  },
3392
3565
  {
3393
- detail: `${formatMetricValue(totals.inputTokens, "tokens")} input`,
3566
+ detail: `${formatMetricValue(totals.failedCalls, "failures")} failed`,
3394
3567
  icon: import_lucide_react12.Activity,
3568
+ label: "Calls",
3569
+ value: formatMetricValue(totals.totalCalls, "calls")
3570
+ },
3571
+ {
3572
+ detail: "Tool and resource runtime",
3573
+ icon: import_lucide_react12.Clock,
3574
+ label: "Duration",
3575
+ value: formatMetricValue(totals.totalDurationMs, "duration")
3576
+ },
3577
+ {
3578
+ detail: `${formatMetricValue(totals.inputTokens, "tokens")} input`,
3579
+ icon: import_lucide_react12.Sparkles,
3395
3580
  label: "Tokens",
3396
3581
  value: formatMetricValue(totals.totalTokens, "tokens")
3397
3582
  },
3398
3583
  {
3399
- detail: `${formatMetricValue(totals.reasoningTokens, "tokens")} reasoning`,
3400
- icon: import_lucide_react12.Sparkles,
3401
- label: "Calls",
3402
- value: formatMetricValue(totals.totalCalls, "calls")
3584
+ detail: "Custom metering",
3585
+ icon: import_lucide_react12.Coins,
3586
+ label: "Credits",
3587
+ value: formatMetricValue(totals.totalCredits, "credits")
3588
+ },
3589
+ {
3590
+ detail: "Failed or aborted work",
3591
+ icon: import_lucide_react12.AlertTriangle,
3592
+ label: "Failures",
3593
+ value: formatMetricValue(totals.failedCalls, "failures")
3403
3594
  }
3404
3595
  ]
3405
3596
  }
@@ -3408,7 +3599,7 @@ function UsagePage({ context }) {
3408
3599
  EmptyState,
3409
3600
  {
3410
3601
  title: "No usage",
3411
- description: "Usage records will appear here when provider-native usage is captured."
3602
+ description: "Metered LLM, tool, and resource records will appear here after work is captured in the usage ledger."
3412
3603
  }
3413
3604
  ) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3414
3605
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Card, { className: "h-[320px] py-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(CardContent, { className: "h-full px-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(UsageChart, { chartState, metricKind }) }) }),
@@ -3487,6 +3678,7 @@ function UsageRowsTable({
3487
3678
  totals
3488
3679
  }) {
3489
3680
  const totalValue = getUsageTotalValue(totals, metricKind, dimension);
3681
+ const valueHeader = (metricKind === "tokens" || metricKind === "cost") && dimension !== "total" ? `${getUsageDimensionLabel(dimension)} ${getUsageMetricLabel(metricKind)}` : getUsageMetricLabel(metricKind);
3490
3682
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3491
3683
  ResourceTable,
3492
3684
  {
@@ -3504,7 +3696,7 @@ function UsageRowsTable({
3504
3696
  {
3505
3697
  align: "right",
3506
3698
  id: "value",
3507
- header: `${getUsageDimensionLabel(dimension)} ${metricKind}`,
3699
+ header: valueHeader,
3508
3700
  render: (row) => formatMetricValue(row.value, metricKind)
3509
3701
  },
3510
3702
  {
@@ -3521,20 +3713,100 @@ function UsageRowsTable({
3521
3713
  },
3522
3714
  {
3523
3715
  align: "right",
3524
- id: "input",
3525
- header: "Input",
3526
- render: (row) => formatMetricValue(getUsageTotalValue(row, metricKind, "input"), metricKind)
3716
+ id: "spend",
3717
+ header: "Spend",
3718
+ render: (row) => formatMetricValue(row.totalCostUsd, "cost")
3527
3719
  },
3528
3720
  {
3529
3721
  align: "right",
3530
- id: "output",
3531
- header: "Output",
3532
- render: (row) => formatMetricValue(getUsageTotalValue(row, metricKind, "output"), metricKind)
3722
+ id: "duration",
3723
+ header: "Duration",
3724
+ render: (row) => formatMetricValue(row.totalDurationMs, "duration")
3725
+ },
3726
+ {
3727
+ align: "right",
3728
+ id: "tokens",
3729
+ header: "Tokens",
3730
+ render: (row) => formatMetricValue(row.totalTokens, "tokens")
3731
+ },
3732
+ {
3733
+ align: "right",
3734
+ id: "failures",
3735
+ header: "Failures",
3736
+ render: (row) => formatMetricValue(row.failedCalls, "failures")
3737
+ },
3738
+ {
3739
+ align: "right",
3740
+ id: "unpriced",
3741
+ header: "Unpriced",
3742
+ render: (row) => formatMetricValue(row.unpricedCalls, "unpriced")
3533
3743
  }
3534
3744
  ]
3535
3745
  }
3536
3746
  );
3537
3747
  }
3748
+ function getUsageMetricOptions(workload) {
3749
+ if (workload === "llm") {
3750
+ return ["cost", "tokens", "calls", "failures", "unpriced"];
3751
+ }
3752
+ if (workload === "tool") {
3753
+ return ["calls", "duration", "cost", "credits", "failures", "unpriced"];
3754
+ }
3755
+ return [
3756
+ "calls",
3757
+ "cost",
3758
+ "duration",
3759
+ "tokens",
3760
+ "credits",
3761
+ "failures",
3762
+ "unpriced"
3763
+ ];
3764
+ }
3765
+ function getUsageGroupOptions(workload) {
3766
+ if (workload === "llm") {
3767
+ return ["participant", "model", "provider", "thread", "namespace", "status"];
3768
+ }
3769
+ if (workload === "tool") {
3770
+ return [
3771
+ "resource",
3772
+ "operation",
3773
+ "status",
3774
+ "participant",
3775
+ "thread",
3776
+ "namespace"
3777
+ ];
3778
+ }
3779
+ return [
3780
+ "kind",
3781
+ "resource",
3782
+ "operation",
3783
+ "status",
3784
+ "participant",
3785
+ "thread",
3786
+ "namespace"
3787
+ ];
3788
+ }
3789
+ function getUsageKindLabel(kind) {
3790
+ switch (kind) {
3791
+ case "all":
3792
+ return "All";
3793
+ case "llm":
3794
+ return "LLM";
3795
+ case "tool":
3796
+ return "Tools";
3797
+ case "asset":
3798
+ return "Assets";
3799
+ case "rag":
3800
+ return "RAG";
3801
+ case "embedding":
3802
+ return "Embeddings";
3803
+ default:
3804
+ return kind;
3805
+ }
3806
+ }
3807
+ function getUsageAttributionLabel(attribution) {
3808
+ return attribution === "initiatedBy" ? "Initiator" : "Performer";
3809
+ }
3538
3810
  function emptyToUndefined(value) {
3539
3811
  const trimmed = value.trim();
3540
3812
  return trimmed.length > 0 ? trimmed : void 0;
@@ -4023,6 +4295,7 @@ function useCopilotzAdmin(options = {}) {
4023
4295
  formatUsageBucket,
4024
4296
  getUsageDimensionLabel,
4025
4297
  getUsageGroupLabel,
4298
+ getUsageMetricLabel,
4026
4299
  getUsageRange,
4027
4300
  getUsageTotalValue,
4028
4301
  mergeAdminConfig,