@copilotz/admin 0.9.23 → 0.9.25

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.js CHANGED
@@ -256,6 +256,21 @@ async function fetchAdminAgents(search, namespace, options) {
256
256
  limit: "8"
257
257
  }, options);
258
258
  }
259
+ async function fetchAdminUsage(filters, options) {
260
+ return await fetchAdminJson("/v1/admin/usage", {
261
+ from: filters.from,
262
+ to: filters.to,
263
+ interval: filters.interval,
264
+ metric: filters.metric,
265
+ groupBy: filters.groupBy,
266
+ threadId: filters.threadId,
267
+ participantId: filters.participantId,
268
+ participantType: filters.participantType,
269
+ namespace: filters.namespace,
270
+ provider: filters.provider,
271
+ model: filters.model
272
+ }, options);
273
+ }
259
274
  async function fetchThreadDetail(threadId, options) {
260
275
  return await fetchAdminJson(
261
276
  `/v1/threads/${encodeURIComponent(threadId)}`,
@@ -1553,7 +1568,7 @@ function Badge({
1553
1568
  }
1554
1569
 
1555
1570
  // src/components/views/DashboardView.tsx
1556
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1571
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1557
1572
  var DashboardView = ({
1558
1573
  config,
1559
1574
  overview,
@@ -1568,10 +1583,11 @@ var DashboardView = ({
1568
1583
  onThreadSearchChange,
1569
1584
  onParticipantSearchChange,
1570
1585
  onAgentSearchChange,
1571
- onThreadClick
1586
+ onThreadClick,
1587
+ namespace
1572
1588
  }) => {
1573
1589
  const [usageMetricKind, setUsageMetricKind] = React5.useState(
1574
- "tokens"
1590
+ "cost"
1575
1591
  );
1576
1592
  const [usageDimension, setUsageDimension] = React5.useState(
1577
1593
  "total"
@@ -1582,11 +1598,6 @@ var DashboardView = ({
1582
1598
  usageMetricKind,
1583
1599
  usageDimension
1584
1600
  );
1585
- const usageBreakdownCards = USAGE_DIMENSIONS.map((dimension) => ({
1586
- dimension,
1587
- label: getUsageDimensionLabel(config.labels, dimension),
1588
- value: overview ? getOverviewUsageValue(overview, usageMetricKind, dimension) : 0
1589
- }));
1590
1601
  const cards = [
1591
1602
  {
1592
1603
  label: config.labels.messagesCard,
@@ -1601,7 +1612,7 @@ var DashboardView = ({
1601
1612
  {
1602
1613
  label: config.labels.participantsCard,
1603
1614
  value: overview?.participantTotals.total ?? 0,
1604
- detail: `${overview?.participantTotals.agents ?? 0} agents`
1615
+ detail: `${overview?.participantTotals.agents ?? 0} agents, ${overview?.participantTotals.jobs ?? 0} jobs`
1605
1616
  },
1606
1617
  {
1607
1618
  label: llmSummaryLabel,
@@ -1639,71 +1650,17 @@ var DashboardView = ({
1639
1650
  card.label
1640
1651
  )) })
1641
1652
  ] }),
1642
- config.features.showOverview && /* @__PURE__ */ jsxs7("section", { className: "space-y-4", children: [
1643
- /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
1644
- /* @__PURE__ */ jsx12(SectionHeading, { title: config.labels.llmUsageTitle }),
1645
- /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 sm:grid-cols-2", children: [
1646
- /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1647
- /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: config.labels.usageMetricLabel }),
1648
- /* @__PURE__ */ jsxs7(
1649
- Select,
1650
- {
1651
- value: usageMetricKind,
1652
- onValueChange: (value) => setUsageMetricKind(value),
1653
- children: [
1654
- /* @__PURE__ */ jsx12(SelectTrigger, { className: "h-9 w-full min-w-[160px]", children: /* @__PURE__ */ jsx12(SelectValue, {}) }),
1655
- /* @__PURE__ */ jsxs7(SelectContent, { children: [
1656
- /* @__PURE__ */ jsx12(SelectItem, { value: "tokens", children: config.labels.usageMetricTokens }),
1657
- /* @__PURE__ */ jsx12(SelectItem, { value: "cost", children: config.labels.usageMetricCost })
1658
- ] })
1659
- ]
1660
- }
1661
- )
1662
- ] }),
1663
- /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1664
- /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: config.labels.usageDimensionLabel }),
1665
- /* @__PURE__ */ jsxs7(
1666
- Select,
1667
- {
1668
- value: usageDimension,
1669
- onValueChange: (value) => setUsageDimension(value),
1670
- children: [
1671
- /* @__PURE__ */ jsx12(SelectTrigger, { className: "h-9 w-full min-w-[160px]", children: /* @__PURE__ */ jsx12(SelectValue, {}) }),
1672
- /* @__PURE__ */ jsx12(SelectContent, { children: USAGE_DIMENSIONS.map((dimension) => /* @__PURE__ */ jsx12(SelectItem, { value: dimension, children: getUsageDimensionLabel(config.labels, dimension) }, dimension)) })
1673
- ]
1674
- }
1675
- )
1676
- ] })
1677
- ] })
1678
- ] }),
1679
- /* @__PURE__ */ jsx12("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: usageBreakdownCards.map((card) => {
1680
- const isSelected = card.dimension === usageDimension;
1681
- return /* @__PURE__ */ jsxs7(
1682
- "div",
1683
- {
1684
- className: cn(
1685
- "rounded-xl border bg-card p-5 shadow-sm transition-colors",
1686
- isSelected && "border-primary/60 ring-1 ring-primary/15"
1687
- ),
1688
- children: [
1689
- /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-3", children: [
1690
- /* @__PURE__ */ jsxs7("div", { children: [
1691
- /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
1692
- /* @__PURE__ */ jsx12("p", { className: "mt-3 text-2xl font-semibold tracking-tight", children: formatMetricValue(card.value, usageMetricKind) })
1693
- ] }),
1694
- isSelected && /* @__PURE__ */ jsx12(Badge, { variant: "outline", children: config.labels.usageDimensionLabel })
1695
- ] }),
1696
- /* @__PURE__ */ jsxs7("p", { className: "mt-2 text-xs text-muted-foreground", children: [
1697
- overview?.llmTotals.totalCalls ?? 0,
1698
- " ",
1699
- config.labels.usageCallsDetail
1700
- ] })
1701
- ]
1702
- },
1703
- card.dimension
1704
- );
1705
- }) })
1706
- ] }),
1653
+ config.features.showOverview && /* @__PURE__ */ jsx12(
1654
+ UsageExplorer,
1655
+ {
1656
+ config,
1657
+ metricKind: usageMetricKind,
1658
+ namespace,
1659
+ onDimensionChange: setUsageDimension,
1660
+ onMetricKindChange: setUsageMetricKind,
1661
+ selectedDimension: usageDimension
1662
+ }
1663
+ ),
1707
1664
  config.features.showActivity && /* @__PURE__ */ jsxs7("section", { className: "space-y-4", children: [
1708
1665
  /* @__PURE__ */ jsx12(SectionHeading, { title: config.labels.activityTitle }),
1709
1666
  /* @__PURE__ */ jsx12(
@@ -1773,6 +1730,318 @@ var DashboardView = ({
1773
1730
  function SectionHeading({ title }) {
1774
1731
  return /* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold tracking-tight", children: title });
1775
1732
  }
1733
+ function UsageExplorer(props) {
1734
+ const [period, setPeriod] = React5.useState("7d");
1735
+ const [bucket, setBucket] = React5.useState("day");
1736
+ const [groupBy, setGroupBy] = React5.useState("participant");
1737
+ const [participantType, setParticipantType] = React5.useState("all");
1738
+ const [threadId, setThreadId] = React5.useState("");
1739
+ const [participantId, setParticipantId] = React5.useState("");
1740
+ const [provider, setProvider] = React5.useState("");
1741
+ const [model, setModel] = React5.useState("");
1742
+ const [customFrom, setCustomFrom] = React5.useState("");
1743
+ const [customTo, setCustomTo] = React5.useState("");
1744
+ const [usage, setUsage] = React5.useState(null);
1745
+ const [isLoading, setIsLoading] = React5.useState(false);
1746
+ const [error, setError] = React5.useState(null);
1747
+ const range = React5.useMemo(() => getUsageRange(period, customFrom, customTo), [
1748
+ customFrom,
1749
+ customTo,
1750
+ period
1751
+ ]);
1752
+ const loadUsage = React5.useCallback(async () => {
1753
+ setIsLoading(true);
1754
+ setError(null);
1755
+ try {
1756
+ const next = await fetchAdminUsage({
1757
+ from: range.from,
1758
+ to: range.to,
1759
+ interval: bucket,
1760
+ metric: props.metricKind,
1761
+ groupBy,
1762
+ namespace: props.namespace || void 0,
1763
+ participantType,
1764
+ threadId: emptyToUndefined(threadId),
1765
+ participantId: emptyToUndefined(participantId),
1766
+ provider: emptyToUndefined(provider),
1767
+ model: emptyToUndefined(model)
1768
+ }, {
1769
+ baseUrl: props.config.baseUrl,
1770
+ getRequestHeaders: props.config.getRequestHeaders
1771
+ });
1772
+ setUsage(next);
1773
+ } catch (err) {
1774
+ setError(err instanceof Error ? err.message : "Failed to load usage");
1775
+ setUsage(null);
1776
+ } finally {
1777
+ setIsLoading(false);
1778
+ }
1779
+ }, [
1780
+ bucket,
1781
+ groupBy,
1782
+ model,
1783
+ participantId,
1784
+ participantType,
1785
+ props.config.baseUrl,
1786
+ props.config.getRequestHeaders,
1787
+ props.metricKind,
1788
+ props.namespace,
1789
+ provider,
1790
+ range.from,
1791
+ range.to,
1792
+ threadId
1793
+ ]);
1794
+ React5.useEffect(() => {
1795
+ void loadUsage();
1796
+ }, [loadUsage]);
1797
+ return /* @__PURE__ */ jsxs7("section", { className: "space-y-4", children: [
1798
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between", children: [
1799
+ /* @__PURE__ */ jsx12(SectionHeading, { title: props.config.labels.llmUsageTitle }),
1800
+ /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-5", children: [
1801
+ /* @__PURE__ */ jsx12(
1802
+ UsageSelect,
1803
+ {
1804
+ label: "Period",
1805
+ value: period,
1806
+ onValueChange: (value) => setPeriod(value),
1807
+ options: [
1808
+ ["24h", props.config.labels.range24h],
1809
+ ["7d", props.config.labels.range7d],
1810
+ ["30d", props.config.labels.range30d],
1811
+ ["custom", "Custom"]
1812
+ ]
1813
+ }
1814
+ ),
1815
+ /* @__PURE__ */ jsx12(
1816
+ UsageSelect,
1817
+ {
1818
+ label: "Bucket",
1819
+ value: bucket,
1820
+ onValueChange: (value) => setBucket(value),
1821
+ options: [
1822
+ ["minute", "Minute"],
1823
+ ["hour", "Hour"],
1824
+ ["day", "Day"],
1825
+ ["week", "Week"],
1826
+ ["month", "Month"]
1827
+ ]
1828
+ }
1829
+ ),
1830
+ /* @__PURE__ */ jsx12(
1831
+ UsageSelect,
1832
+ {
1833
+ label: props.config.labels.usageMetricLabel,
1834
+ value: props.metricKind,
1835
+ onValueChange: (value) => props.onMetricKindChange(value),
1836
+ options: [
1837
+ ["cost", props.config.labels.usageMetricCost],
1838
+ ["tokens", props.config.labels.usageMetricTokens],
1839
+ ["calls", "Calls"]
1840
+ ]
1841
+ }
1842
+ ),
1843
+ /* @__PURE__ */ jsx12(
1844
+ UsageSelect,
1845
+ {
1846
+ label: "Group by",
1847
+ value: groupBy,
1848
+ onValueChange: (value) => setGroupBy(value),
1849
+ options: [
1850
+ ["participant", "Participant"],
1851
+ ["thread", "Thread"],
1852
+ ["namespace", "Namespace"],
1853
+ ["provider", "Provider"],
1854
+ ["model", "Model"]
1855
+ ]
1856
+ }
1857
+ ),
1858
+ /* @__PURE__ */ jsx12(
1859
+ UsageSelect,
1860
+ {
1861
+ label: props.config.labels.usageDimensionLabel,
1862
+ value: props.selectedDimension,
1863
+ onValueChange: (value) => props.onDimensionChange(value),
1864
+ options: USAGE_DIMENSIONS.map((dimension) => [
1865
+ dimension,
1866
+ getUsageDimensionLabel(props.config.labels, dimension)
1867
+ ])
1868
+ }
1869
+ )
1870
+ ] })
1871
+ ] }),
1872
+ /* @__PURE__ */ jsx12("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-6", children: [
1873
+ period === "custom" && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1874
+ /* @__PURE__ */ jsx12(FilterInput, { label: "From", type: "datetime-local", value: customFrom, onChange: setCustomFrom }),
1875
+ /* @__PURE__ */ jsx12(FilterInput, { label: "To", type: "datetime-local", value: customTo, onChange: setCustomTo })
1876
+ ] }),
1877
+ /* @__PURE__ */ jsx12(
1878
+ UsageSelect,
1879
+ {
1880
+ label: "Participant type",
1881
+ value: participantType,
1882
+ onValueChange: (value) => setParticipantType(value),
1883
+ options: [
1884
+ ["all", "All"],
1885
+ ["human", "Human"],
1886
+ ["agent", "Agent"],
1887
+ ["job", "Job"]
1888
+ ]
1889
+ }
1890
+ ),
1891
+ /* @__PURE__ */ jsx12(FilterInput, { label: "Thread", value: threadId, onChange: setThreadId }),
1892
+ /* @__PURE__ */ jsx12(FilterInput, { label: "Participant", value: participantId, onChange: setParticipantId }),
1893
+ /* @__PURE__ */ jsx12(FilterInput, { label: "Provider", value: provider, onChange: setProvider }),
1894
+ /* @__PURE__ */ jsx12(FilterInput, { label: "Model", value: model, onChange: setModel }),
1895
+ /* @__PURE__ */ jsx12("div", { className: "flex items-end", children: /* @__PURE__ */ jsx12(
1896
+ Button,
1897
+ {
1898
+ className: "h-9 w-full",
1899
+ onClick: () => void loadUsage(),
1900
+ disabled: isLoading,
1901
+ variant: "outline",
1902
+ children: props.config.labels.refresh
1903
+ }
1904
+ ) })
1905
+ ] }) }),
1906
+ /* @__PURE__ */ jsx12(
1907
+ UsageChart,
1908
+ {
1909
+ labels: props.config.labels,
1910
+ metricKind: props.metricKind,
1911
+ dimension: props.selectedDimension,
1912
+ points: usage?.points ?? [],
1913
+ bucket,
1914
+ isLoading,
1915
+ error
1916
+ }
1917
+ ),
1918
+ /* @__PURE__ */ jsx12(
1919
+ UsageTable,
1920
+ {
1921
+ labels: props.config.labels,
1922
+ metricKind: props.metricKind,
1923
+ dimension: props.selectedDimension,
1924
+ rows: usage?.rows ?? [],
1925
+ isLoading
1926
+ }
1927
+ )
1928
+ ] });
1929
+ }
1930
+ function UsageSelect(props) {
1931
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1932
+ /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: props.label }),
1933
+ /* @__PURE__ */ jsxs7(Select, { value: props.value, onValueChange: props.onValueChange, children: [
1934
+ /* @__PURE__ */ jsx12(SelectTrigger, { className: "h-9 w-full min-w-[140px]", children: /* @__PURE__ */ jsx12(SelectValue, {}) }),
1935
+ /* @__PURE__ */ jsx12(SelectContent, { children: props.options.map(([value, label]) => /* @__PURE__ */ jsx12(SelectItem, { value, children: label }, value)) })
1936
+ ] })
1937
+ ] });
1938
+ }
1939
+ function FilterInput(props) {
1940
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1941
+ /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: props.label }),
1942
+ /* @__PURE__ */ jsx12(
1943
+ Input,
1944
+ {
1945
+ className: "h-9",
1946
+ type: props.type ?? "text",
1947
+ value: props.value,
1948
+ onChange: (event) => props.onChange(event.target.value)
1949
+ }
1950
+ )
1951
+ ] });
1952
+ }
1953
+ function UsageChart(props) {
1954
+ const buckets = React5.useMemo(() => buildUsageBuckets(props.points), [props.points]);
1955
+ const visibleBuckets = buckets.slice(-18);
1956
+ const groups = React5.useMemo(() => topUsageGroups(props.points, props.metricKind, props.dimension), [
1957
+ props.dimension,
1958
+ props.metricKind,
1959
+ props.points
1960
+ ]);
1961
+ const maxBucketValue = Math.max(
1962
+ ...visibleBuckets.map(
1963
+ (bucket) => bucket.points.reduce((sum, point) => sum + getUsagePointValue(point, props.metricKind, props.dimension), 0)
1964
+ ),
1965
+ 1
1966
+ );
1967
+ return /* @__PURE__ */ jsx12("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: props.error ? /* @__PURE__ */ jsx12("p", { className: "text-sm text-destructive", children: props.error }) : props.isLoading && props.points.length === 0 ? /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: props.labels.loading }) : visibleBuckets.length === 0 ? /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ jsxs7("div", { className: "space-y-4", children: [
1968
+ /* @__PURE__ */ jsx12("div", { className: "flex min-h-56 items-end gap-2", children: visibleBuckets.map((bucket) => {
1969
+ const bucketTotal = bucket.points.reduce((sum, point) => sum + getUsagePointValue(point, props.metricKind, props.dimension), 0);
1970
+ return /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col items-center gap-2", children: [
1971
+ /* @__PURE__ */ jsx12("div", { className: "flex h-40 w-full items-end rounded-lg bg-muted px-1 pb-1", children: /* @__PURE__ */ jsx12(
1972
+ "div",
1973
+ {
1974
+ className: "flex w-full flex-col-reverse overflow-hidden rounded-md",
1975
+ style: {
1976
+ height: `${Math.max(bucketTotal / maxBucketValue * 100, 6)}%`
1977
+ },
1978
+ children: groups.map((group, index) => {
1979
+ const point = bucket.points.find((item) => item.groupKey === group.key);
1980
+ const value = point ? getUsagePointValue(point, props.metricKind, props.dimension) : 0;
1981
+ if (value <= 0 || bucketTotal <= 0) return null;
1982
+ return /* @__PURE__ */ jsx12(
1983
+ "div",
1984
+ {
1985
+ title: `${group.label}: ${formatMetricValue(value, props.metricKind)}`,
1986
+ style: {
1987
+ height: `${value / bucketTotal * 100}%`,
1988
+ backgroundColor: USAGE_CHART_COLORS[index % USAGE_CHART_COLORS.length]
1989
+ }
1990
+ },
1991
+ group.key
1992
+ );
1993
+ })
1994
+ }
1995
+ ) }),
1996
+ /* @__PURE__ */ jsxs7("div", { className: "text-center", children: [
1997
+ /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium", children: formatUsageBucket(bucket.bucket, props.bucket) }),
1998
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] text-muted-foreground", children: formatMetricValue(bucketTotal, props.metricKind) })
1999
+ ] })
2000
+ ] }, bucket.bucket);
2001
+ }) }),
2002
+ /* @__PURE__ */ jsx12("div", { className: "flex flex-wrap gap-3", children: groups.map((group, index) => /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs", children: [
2003
+ /* @__PURE__ */ jsx12(
2004
+ "span",
2005
+ {
2006
+ className: "size-2 rounded-sm",
2007
+ style: { backgroundColor: USAGE_CHART_COLORS[index % USAGE_CHART_COLORS.length] }
2008
+ }
2009
+ ),
2010
+ /* @__PURE__ */ jsx12("span", { className: "max-w-48 truncate text-muted-foreground", children: group.label })
2011
+ ] }, group.key)) })
2012
+ ] }) });
2013
+ }
2014
+ function UsageTable(props) {
2015
+ const rows = props.rows.slice(0, 40);
2016
+ return /* @__PURE__ */ jsxs7("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: [
2017
+ /* @__PURE__ */ jsxs7("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
2018
+ /* @__PURE__ */ jsx12(SectionHeading, { title: "Usage detail" }),
2019
+ props.isLoading && /* @__PURE__ */ jsx12(Badge, { variant: "outline", children: props.labels.loading })
2020
+ ] }),
2021
+ rows.length === 0 ? /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ jsx12("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs7("table", { className: "w-full min-w-[720px] text-sm", children: [
2022
+ /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "border-b text-left text-xs uppercase tracking-[0.14em] text-muted-foreground", children: [
2023
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 font-medium", children: "Interval" }),
2024
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 font-medium", children: "Group" }),
2025
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 text-right font-medium", children: "Input" }),
2026
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 text-right font-medium", children: "Output" }),
2027
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 text-right font-medium", children: "Reasoning" }),
2028
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 text-right font-medium", children: "Cache read" }),
2029
+ /* @__PURE__ */ jsx12("th", { className: "py-2 pr-4 text-right font-medium", children: "Cache write" }),
2030
+ /* @__PURE__ */ jsx12("th", { className: "py-2 text-right font-medium", children: getUsageSummaryLabel(props.labels, props.metricKind, props.dimension) })
2031
+ ] }) }),
2032
+ /* @__PURE__ */ jsx12("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs7("tr", { className: "border-b last:border-0", children: [
2033
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-muted-foreground", children: formatDate(row.bucket) }),
2034
+ /* @__PURE__ */ jsx12("td", { className: "max-w-72 truncate py-2 pr-4 font-medium", children: row.groupLabel }),
2035
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "input"), props.metricKind) }),
2036
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "output"), props.metricKind) }),
2037
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "reasoning"), props.metricKind) }),
2038
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "cacheRead"), props.metricKind) }),
2039
+ /* @__PURE__ */ jsx12("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "cacheWrite"), props.metricKind) }),
2040
+ /* @__PURE__ */ jsx12("td", { className: "py-2 text-right font-medium", children: formatMetricValue(getUsagePointValue(row, props.metricKind, props.dimension), props.metricKind) })
2041
+ ] }, `${row.bucket}:${row.groupKey}`)) })
2042
+ ] }) })
2043
+ ] });
2044
+ }
1776
2045
  function ActivityChart(props) {
1777
2046
  const trimmedPoints = props.points.slice(-props.maxBars);
1778
2047
  const maxUsageValue = Math.max(
@@ -1971,6 +2240,28 @@ function formatBucket(bucket, interval) {
1971
2240
  day: "numeric"
1972
2241
  });
1973
2242
  }
2243
+ function formatUsageBucket(bucket, interval) {
2244
+ const date = new Date(bucket);
2245
+ if (Number.isNaN(date.getTime())) return bucket;
2246
+ if (interval === "minute" || interval === "hour") {
2247
+ return date.toLocaleString(void 0, {
2248
+ month: "short",
2249
+ day: "numeric",
2250
+ hour: "numeric",
2251
+ minute: interval === "minute" ? "2-digit" : void 0
2252
+ });
2253
+ }
2254
+ if (interval === "month") {
2255
+ return date.toLocaleDateString(void 0, {
2256
+ month: "short",
2257
+ year: "numeric"
2258
+ });
2259
+ }
2260
+ return date.toLocaleDateString(void 0, {
2261
+ month: "short",
2262
+ day: "numeric"
2263
+ });
2264
+ }
1974
2265
  function formatDate(value) {
1975
2266
  if (!value) return "No activity";
1976
2267
  const date = new Date(value);
@@ -1993,6 +2284,58 @@ var USAGE_DIMENSIONS = [
1993
2284
  "cacheRead",
1994
2285
  "cacheWrite"
1995
2286
  ];
2287
+ var USAGE_CHART_COLORS = [
2288
+ "hsl(var(--primary))",
2289
+ "hsl(var(--chart-2, 142 76% 36%))",
2290
+ "hsl(var(--chart-3, 38 92% 50%))",
2291
+ "hsl(var(--chart-4, 199 89% 48%))",
2292
+ "hsl(var(--chart-5, 346 77% 49%))",
2293
+ "hsl(var(--muted-foreground))"
2294
+ ];
2295
+ function emptyToUndefined(value) {
2296
+ const trimmed = value.trim();
2297
+ return trimmed.length > 0 ? trimmed : void 0;
2298
+ }
2299
+ function getUsageRange(period, customFrom, customTo) {
2300
+ if (period === "custom") {
2301
+ return {
2302
+ from: customFrom ? new Date(customFrom).toISOString() : void 0,
2303
+ to: customTo ? new Date(customTo).toISOString() : void 0
2304
+ };
2305
+ }
2306
+ const to = /* @__PURE__ */ new Date();
2307
+ const from = new Date(to);
2308
+ if (period === "24h") {
2309
+ from.setHours(from.getHours() - 24);
2310
+ } else if (period === "30d") {
2311
+ from.setDate(from.getDate() - 30);
2312
+ } else {
2313
+ from.setDate(from.getDate() - 7);
2314
+ }
2315
+ return { from: from.toISOString(), to: to.toISOString() };
2316
+ }
2317
+ function buildUsageBuckets(points) {
2318
+ const byBucket = /* @__PURE__ */ new Map();
2319
+ for (const point of points) {
2320
+ const rows = byBucket.get(point.bucket) ?? [];
2321
+ rows.push(point);
2322
+ byBucket.set(point.bucket, rows);
2323
+ }
2324
+ return Array.from(byBucket.entries()).map(([bucket, bucketPoints]) => ({ bucket, points: bucketPoints })).sort((a, b) => new Date(a.bucket).getTime() - new Date(b.bucket).getTime());
2325
+ }
2326
+ function topUsageGroups(points, metricKind, dimension) {
2327
+ const totals = /* @__PURE__ */ new Map();
2328
+ for (const point of points) {
2329
+ const existing = totals.get(point.groupKey) ?? {
2330
+ key: point.groupKey,
2331
+ label: point.groupLabel,
2332
+ value: 0
2333
+ };
2334
+ existing.value += getUsagePointValue(point, metricKind, dimension);
2335
+ totals.set(point.groupKey, existing);
2336
+ }
2337
+ return Array.from(totals.values()).sort((a, b) => b.value - a.value).slice(0, 6);
2338
+ }
1996
2339
  function getUsageDimensionLabel(labels, dimension) {
1997
2340
  switch (dimension) {
1998
2341
  case "input":
@@ -2011,11 +2354,12 @@ function getUsageDimensionLabel(labels, dimension) {
2011
2354
  }
2012
2355
  }
2013
2356
  function getUsageSummaryLabel(labels, metricKind, dimension) {
2014
- const metricLabel = metricKind === "cost" ? labels.usageMetricCost : labels.usageMetricTokens;
2357
+ const metricLabel = metricKind === "cost" ? labels.usageMetricCost : metricKind === "calls" ? "Calls" : labels.usageMetricTokens;
2015
2358
  return `${getUsageDimensionLabel(labels, dimension)} ${metricLabel}`;
2016
2359
  }
2017
2360
  function getOverviewUsageValue(overview, metricKind, dimension) {
2018
2361
  const llmTotals = overview.llmTotals;
2362
+ if (metricKind === "calls") return llmTotals.totalCalls;
2019
2363
  if (metricKind === "cost") {
2020
2364
  switch (dimension) {
2021
2365
  case "input":
@@ -2067,6 +2411,7 @@ function getAgentUsageValue(agent, metricKind, dimension) {
2067
2411
  return agent.totalCostUsd;
2068
2412
  }
2069
2413
  }
2414
+ if (metricKind === "calls") return agent.llmCallCount;
2070
2415
  switch (dimension) {
2071
2416
  case "input":
2072
2417
  return agent.inputTokens;
@@ -2101,6 +2446,42 @@ function getActivityUsageValue(point, metricKind, dimension) {
2101
2446
  return point.totalCostUsd;
2102
2447
  }
2103
2448
  }
2449
+ if (metricKind === "calls") return point.llmCallCount;
2450
+ switch (dimension) {
2451
+ case "input":
2452
+ return point.inputTokens;
2453
+ case "output":
2454
+ return point.outputTokens;
2455
+ case "reasoning":
2456
+ return point.reasoningTokens;
2457
+ case "cacheRead":
2458
+ return point.cacheReadInputTokens;
2459
+ case "cacheWrite":
2460
+ return point.cacheCreationInputTokens;
2461
+ case "total":
2462
+ default:
2463
+ return point.totalTokens;
2464
+ }
2465
+ }
2466
+ function getUsagePointValue(point, metricKind, dimension) {
2467
+ if (metricKind === "cost") {
2468
+ switch (dimension) {
2469
+ case "input":
2470
+ return point.inputCostUsd;
2471
+ case "output":
2472
+ return point.outputCostUsd;
2473
+ case "reasoning":
2474
+ return point.reasoningCostUsd;
2475
+ case "cacheRead":
2476
+ return point.cacheReadInputCostUsd;
2477
+ case "cacheWrite":
2478
+ return point.cacheCreationInputCostUsd;
2479
+ case "total":
2480
+ default:
2481
+ return point.totalCostUsd;
2482
+ }
2483
+ }
2484
+ if (metricKind === "calls") return point.totalCalls;
2104
2485
  switch (dimension) {
2105
2486
  case "input":
2106
2487
  return point.inputTokens;
@@ -3182,6 +3563,7 @@ var CopilotzAdmin = ({
3182
3563
  participants: admin.participants,
3183
3564
  agents: admin.agents,
3184
3565
  interval: admin.filters.interval,
3566
+ namespace,
3185
3567
  threadSearch,
3186
3568
  participantSearch,
3187
3569
  agentSearch,
@@ -3309,6 +3691,7 @@ export {
3309
3691
  fetchAdminOverview,
3310
3692
  fetchAdminParticipants,
3311
3693
  fetchAdminThreads,
3694
+ fetchAdminUsage,
3312
3695
  fetchCollectionItem,
3313
3696
  fetchCollectionItems,
3314
3697
  fetchCollectionNames,