@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.cjs CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  fetchAdminOverview: () => fetchAdminOverview,
40
40
  fetchAdminParticipants: () => fetchAdminParticipants,
41
41
  fetchAdminThreads: () => fetchAdminThreads,
42
+ fetchAdminUsage: () => fetchAdminUsage,
42
43
  fetchCollectionItem: () => fetchCollectionItem,
43
44
  fetchCollectionItems: () => fetchCollectionItems,
44
45
  fetchCollectionNames: () => fetchCollectionNames,
@@ -299,6 +300,21 @@ async function fetchAdminAgents(search, namespace, options) {
299
300
  limit: "8"
300
301
  }, options);
301
302
  }
303
+ async function fetchAdminUsage(filters, options) {
304
+ return await fetchAdminJson("/v1/admin/usage", {
305
+ from: filters.from,
306
+ to: filters.to,
307
+ interval: filters.interval,
308
+ metric: filters.metric,
309
+ groupBy: filters.groupBy,
310
+ threadId: filters.threadId,
311
+ participantId: filters.participantId,
312
+ participantType: filters.participantType,
313
+ namespace: filters.namespace,
314
+ provider: filters.provider,
315
+ model: filters.model
316
+ }, options);
317
+ }
302
318
  async function fetchThreadDetail(threadId, options) {
303
319
  return await fetchAdminJson(
304
320
  `/v1/threads/${encodeURIComponent(threadId)}`,
@@ -1603,10 +1619,11 @@ var DashboardView = ({
1603
1619
  onThreadSearchChange,
1604
1620
  onParticipantSearchChange,
1605
1621
  onAgentSearchChange,
1606
- onThreadClick
1622
+ onThreadClick,
1623
+ namespace
1607
1624
  }) => {
1608
1625
  const [usageMetricKind, setUsageMetricKind] = import_react2.default.useState(
1609
- "tokens"
1626
+ "cost"
1610
1627
  );
1611
1628
  const [usageDimension, setUsageDimension] = import_react2.default.useState(
1612
1629
  "total"
@@ -1617,11 +1634,6 @@ var DashboardView = ({
1617
1634
  usageMetricKind,
1618
1635
  usageDimension
1619
1636
  );
1620
- const usageBreakdownCards = USAGE_DIMENSIONS.map((dimension) => ({
1621
- dimension,
1622
- label: getUsageDimensionLabel(config.labels, dimension),
1623
- value: overview ? getOverviewUsageValue(overview, usageMetricKind, dimension) : 0
1624
- }));
1625
1637
  const cards = [
1626
1638
  {
1627
1639
  label: config.labels.messagesCard,
@@ -1636,7 +1648,7 @@ var DashboardView = ({
1636
1648
  {
1637
1649
  label: config.labels.participantsCard,
1638
1650
  value: overview?.participantTotals.total ?? 0,
1639
- detail: `${overview?.participantTotals.agents ?? 0} agents`
1651
+ detail: `${overview?.participantTotals.agents ?? 0} agents, ${overview?.participantTotals.jobs ?? 0} jobs`
1640
1652
  },
1641
1653
  {
1642
1654
  label: llmSummaryLabel,
@@ -1674,71 +1686,17 @@ var DashboardView = ({
1674
1686
  card.label
1675
1687
  )) })
1676
1688
  ] }),
1677
- config.features.showOverview && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "space-y-4", children: [
1678
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
1679
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionHeading, { title: config.labels.llmUsageTitle }),
1680
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid gap-3 sm:grid-cols-2", children: [
1681
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1", children: [
1682
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: config.labels.usageMetricLabel }),
1683
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1684
- Select,
1685
- {
1686
- value: usageMetricKind,
1687
- onValueChange: (value) => setUsageMetricKind(value),
1688
- children: [
1689
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectTrigger, { className: "h-9 w-full min-w-[160px]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectValue, {}) }),
1690
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(SelectContent, { children: [
1691
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectItem, { value: "tokens", children: config.labels.usageMetricTokens }),
1692
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectItem, { value: "cost", children: config.labels.usageMetricCost })
1693
- ] })
1694
- ]
1695
- }
1696
- )
1697
- ] }),
1698
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1", children: [
1699
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: config.labels.usageDimensionLabel }),
1700
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1701
- Select,
1702
- {
1703
- value: usageDimension,
1704
- onValueChange: (value) => setUsageDimension(value),
1705
- children: [
1706
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectTrigger, { className: "h-9 w-full min-w-[160px]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectValue, {}) }),
1707
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectContent, { children: USAGE_DIMENSIONS.map((dimension) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectItem, { value: dimension, children: getUsageDimensionLabel(config.labels, dimension) }, dimension)) })
1708
- ]
1709
- }
1710
- )
1711
- ] })
1712
- ] })
1713
- ] }),
1714
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: usageBreakdownCards.map((card) => {
1715
- const isSelected = card.dimension === usageDimension;
1716
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1717
- "div",
1718
- {
1719
- className: cn(
1720
- "rounded-xl border bg-card p-5 shadow-sm transition-colors",
1721
- isSelected && "border-primary/60 ring-1 ring-primary/15"
1722
- ),
1723
- children: [
1724
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1725
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1726
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
1727
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-3 text-2xl font-semibold tracking-tight", children: formatMetricValue(card.value, usageMetricKind) })
1728
- ] }),
1729
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Badge, { variant: "outline", children: config.labels.usageDimensionLabel })
1730
- ] }),
1731
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "mt-2 text-xs text-muted-foreground", children: [
1732
- overview?.llmTotals.totalCalls ?? 0,
1733
- " ",
1734
- config.labels.usageCallsDetail
1735
- ] })
1736
- ]
1737
- },
1738
- card.dimension
1739
- );
1740
- }) })
1741
- ] }),
1689
+ config.features.showOverview && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1690
+ UsageExplorer,
1691
+ {
1692
+ config,
1693
+ metricKind: usageMetricKind,
1694
+ namespace,
1695
+ onDimensionChange: setUsageDimension,
1696
+ onMetricKindChange: setUsageMetricKind,
1697
+ selectedDimension: usageDimension
1698
+ }
1699
+ ),
1742
1700
  config.features.showActivity && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "space-y-4", children: [
1743
1701
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionHeading, { title: config.labels.activityTitle }),
1744
1702
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -1808,6 +1766,318 @@ var DashboardView = ({
1808
1766
  function SectionHeading({ title }) {
1809
1767
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold tracking-tight", children: title });
1810
1768
  }
1769
+ function UsageExplorer(props) {
1770
+ const [period, setPeriod] = import_react2.default.useState("7d");
1771
+ const [bucket, setBucket] = import_react2.default.useState("day");
1772
+ const [groupBy, setGroupBy] = import_react2.default.useState("participant");
1773
+ const [participantType, setParticipantType] = import_react2.default.useState("all");
1774
+ const [threadId, setThreadId] = import_react2.default.useState("");
1775
+ const [participantId, setParticipantId] = import_react2.default.useState("");
1776
+ const [provider, setProvider] = import_react2.default.useState("");
1777
+ const [model, setModel] = import_react2.default.useState("");
1778
+ const [customFrom, setCustomFrom] = import_react2.default.useState("");
1779
+ const [customTo, setCustomTo] = import_react2.default.useState("");
1780
+ const [usage, setUsage] = import_react2.default.useState(null);
1781
+ const [isLoading, setIsLoading] = import_react2.default.useState(false);
1782
+ const [error, setError] = import_react2.default.useState(null);
1783
+ const range = import_react2.default.useMemo(() => getUsageRange(period, customFrom, customTo), [
1784
+ customFrom,
1785
+ customTo,
1786
+ period
1787
+ ]);
1788
+ const loadUsage = import_react2.default.useCallback(async () => {
1789
+ setIsLoading(true);
1790
+ setError(null);
1791
+ try {
1792
+ const next = await fetchAdminUsage({
1793
+ from: range.from,
1794
+ to: range.to,
1795
+ interval: bucket,
1796
+ metric: props.metricKind,
1797
+ groupBy,
1798
+ namespace: props.namespace || void 0,
1799
+ participantType,
1800
+ threadId: emptyToUndefined(threadId),
1801
+ participantId: emptyToUndefined(participantId),
1802
+ provider: emptyToUndefined(provider),
1803
+ model: emptyToUndefined(model)
1804
+ }, {
1805
+ baseUrl: props.config.baseUrl,
1806
+ getRequestHeaders: props.config.getRequestHeaders
1807
+ });
1808
+ setUsage(next);
1809
+ } catch (err) {
1810
+ setError(err instanceof Error ? err.message : "Failed to load usage");
1811
+ setUsage(null);
1812
+ } finally {
1813
+ setIsLoading(false);
1814
+ }
1815
+ }, [
1816
+ bucket,
1817
+ groupBy,
1818
+ model,
1819
+ participantId,
1820
+ participantType,
1821
+ props.config.baseUrl,
1822
+ props.config.getRequestHeaders,
1823
+ props.metricKind,
1824
+ props.namespace,
1825
+ provider,
1826
+ range.from,
1827
+ range.to,
1828
+ threadId
1829
+ ]);
1830
+ import_react2.default.useEffect(() => {
1831
+ void loadUsage();
1832
+ }, [loadUsage]);
1833
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "space-y-4", children: [
1834
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between", children: [
1835
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionHeading, { title: props.config.labels.llmUsageTitle }),
1836
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-5", children: [
1837
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1838
+ UsageSelect,
1839
+ {
1840
+ label: "Period",
1841
+ value: period,
1842
+ onValueChange: (value) => setPeriod(value),
1843
+ options: [
1844
+ ["24h", props.config.labels.range24h],
1845
+ ["7d", props.config.labels.range7d],
1846
+ ["30d", props.config.labels.range30d],
1847
+ ["custom", "Custom"]
1848
+ ]
1849
+ }
1850
+ ),
1851
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1852
+ UsageSelect,
1853
+ {
1854
+ label: "Bucket",
1855
+ value: bucket,
1856
+ onValueChange: (value) => setBucket(value),
1857
+ options: [
1858
+ ["minute", "Minute"],
1859
+ ["hour", "Hour"],
1860
+ ["day", "Day"],
1861
+ ["week", "Week"],
1862
+ ["month", "Month"]
1863
+ ]
1864
+ }
1865
+ ),
1866
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1867
+ UsageSelect,
1868
+ {
1869
+ label: props.config.labels.usageMetricLabel,
1870
+ value: props.metricKind,
1871
+ onValueChange: (value) => props.onMetricKindChange(value),
1872
+ options: [
1873
+ ["cost", props.config.labels.usageMetricCost],
1874
+ ["tokens", props.config.labels.usageMetricTokens],
1875
+ ["calls", "Calls"]
1876
+ ]
1877
+ }
1878
+ ),
1879
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1880
+ UsageSelect,
1881
+ {
1882
+ label: "Group by",
1883
+ value: groupBy,
1884
+ onValueChange: (value) => setGroupBy(value),
1885
+ options: [
1886
+ ["participant", "Participant"],
1887
+ ["thread", "Thread"],
1888
+ ["namespace", "Namespace"],
1889
+ ["provider", "Provider"],
1890
+ ["model", "Model"]
1891
+ ]
1892
+ }
1893
+ ),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1895
+ UsageSelect,
1896
+ {
1897
+ label: props.config.labels.usageDimensionLabel,
1898
+ value: props.selectedDimension,
1899
+ onValueChange: (value) => props.onDimensionChange(value),
1900
+ options: USAGE_DIMENSIONS.map((dimension) => [
1901
+ dimension,
1902
+ getUsageDimensionLabel(props.config.labels, dimension)
1903
+ ])
1904
+ }
1905
+ )
1906
+ ] })
1907
+ ] }),
1908
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-6", children: [
1909
+ period === "custom" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1910
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "From", type: "datetime-local", value: customFrom, onChange: setCustomFrom }),
1911
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "To", type: "datetime-local", value: customTo, onChange: setCustomTo })
1912
+ ] }),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1914
+ UsageSelect,
1915
+ {
1916
+ label: "Participant type",
1917
+ value: participantType,
1918
+ onValueChange: (value) => setParticipantType(value),
1919
+ options: [
1920
+ ["all", "All"],
1921
+ ["human", "Human"],
1922
+ ["agent", "Agent"],
1923
+ ["job", "Job"]
1924
+ ]
1925
+ }
1926
+ ),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "Thread", value: threadId, onChange: setThreadId }),
1928
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "Participant", value: participantId, onChange: setParticipantId }),
1929
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "Provider", value: provider, onChange: setProvider }),
1930
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FilterInput, { label: "Model", value: model, onChange: setModel }),
1931
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex items-end", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1932
+ Button,
1933
+ {
1934
+ className: "h-9 w-full",
1935
+ onClick: () => void loadUsage(),
1936
+ disabled: isLoading,
1937
+ variant: "outline",
1938
+ children: props.config.labels.refresh
1939
+ }
1940
+ ) })
1941
+ ] }) }),
1942
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1943
+ UsageChart,
1944
+ {
1945
+ labels: props.config.labels,
1946
+ metricKind: props.metricKind,
1947
+ dimension: props.selectedDimension,
1948
+ points: usage?.points ?? [],
1949
+ bucket,
1950
+ isLoading,
1951
+ error
1952
+ }
1953
+ ),
1954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1955
+ UsageTable,
1956
+ {
1957
+ labels: props.config.labels,
1958
+ metricKind: props.metricKind,
1959
+ dimension: props.selectedDimension,
1960
+ rows: usage?.rows ?? [],
1961
+ isLoading
1962
+ }
1963
+ )
1964
+ ] });
1965
+ }
1966
+ function UsageSelect(props) {
1967
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: props.label }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Select, { value: props.value, onValueChange: props.onValueChange, children: [
1970
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectTrigger, { className: "h-9 w-full min-w-[140px]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectValue, {}) }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectContent, { children: props.options.map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectItem, { value, children: label }, value)) })
1972
+ ] })
1973
+ ] });
1974
+ }
1975
+ function FilterInput(props) {
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1", children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: props.label }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1979
+ Input,
1980
+ {
1981
+ className: "h-9",
1982
+ type: props.type ?? "text",
1983
+ value: props.value,
1984
+ onChange: (event) => props.onChange(event.target.value)
1985
+ }
1986
+ )
1987
+ ] });
1988
+ }
1989
+ function UsageChart(props) {
1990
+ const buckets = import_react2.default.useMemo(() => buildUsageBuckets(props.points), [props.points]);
1991
+ const visibleBuckets = buckets.slice(-18);
1992
+ const groups = import_react2.default.useMemo(() => topUsageGroups(props.points, props.metricKind, props.dimension), [
1993
+ props.dimension,
1994
+ props.metricKind,
1995
+ props.points
1996
+ ]);
1997
+ const maxBucketValue = Math.max(
1998
+ ...visibleBuckets.map(
1999
+ (bucket) => bucket.points.reduce((sum, point) => sum + getUsagePointValue(point, props.metricKind, props.dimension), 0)
2000
+ ),
2001
+ 1
2002
+ );
2003
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: props.error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-destructive", children: props.error }) : props.isLoading && props.points.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: props.labels.loading }) : visibleBuckets.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-4", children: [
2004
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex min-h-56 items-end gap-2", children: visibleBuckets.map((bucket) => {
2005
+ const bucketTotal = bucket.points.reduce((sum, point) => sum + getUsagePointValue(point, props.metricKind, props.dimension), 0);
2006
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col items-center gap-2", children: [
2007
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex h-40 w-full items-end rounded-lg bg-muted px-1 pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2008
+ "div",
2009
+ {
2010
+ className: "flex w-full flex-col-reverse overflow-hidden rounded-md",
2011
+ style: {
2012
+ height: `${Math.max(bucketTotal / maxBucketValue * 100, 6)}%`
2013
+ },
2014
+ children: groups.map((group, index) => {
2015
+ const point = bucket.points.find((item) => item.groupKey === group.key);
2016
+ const value = point ? getUsagePointValue(point, props.metricKind, props.dimension) : 0;
2017
+ if (value <= 0 || bucketTotal <= 0) return null;
2018
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2019
+ "div",
2020
+ {
2021
+ title: `${group.label}: ${formatMetricValue(value, props.metricKind)}`,
2022
+ style: {
2023
+ height: `${value / bucketTotal * 100}%`,
2024
+ backgroundColor: USAGE_CHART_COLORS[index % USAGE_CHART_COLORS.length]
2025
+ }
2026
+ },
2027
+ group.key
2028
+ );
2029
+ })
2030
+ }
2031
+ ) }),
2032
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-center", children: [
2033
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs font-medium", children: formatUsageBucket(bucket.bucket, props.bucket) }),
2034
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[11px] text-muted-foreground", children: formatMetricValue(bucketTotal, props.metricKind) })
2035
+ ] })
2036
+ ] }, bucket.bucket);
2037
+ }) }),
2038
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-wrap gap-3", children: groups.map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
2039
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2040
+ "span",
2041
+ {
2042
+ className: "size-2 rounded-sm",
2043
+ style: { backgroundColor: USAGE_CHART_COLORS[index % USAGE_CHART_COLORS.length] }
2044
+ }
2045
+ ),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "max-w-48 truncate text-muted-foreground", children: group.label })
2047
+ ] }, group.key)) })
2048
+ ] }) });
2049
+ }
2050
+ function UsageTable(props) {
2051
+ const rows = props.rows.slice(0, 40);
2052
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: [
2053
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
2054
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionHeading, { title: "Usage detail" }),
2055
+ props.isLoading && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Badge, { variant: "outline", children: props.labels.loading })
2056
+ ] }),
2057
+ rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full min-w-[720px] text-sm", children: [
2058
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b text-left text-xs uppercase tracking-[0.14em] text-muted-foreground", children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 font-medium", children: "Interval" }),
2060
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 font-medium", children: "Group" }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 text-right font-medium", children: "Input" }),
2062
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 text-right font-medium", children: "Output" }),
2063
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 text-right font-medium", children: "Reasoning" }),
2064
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 text-right font-medium", children: "Cache read" }),
2065
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 pr-4 text-right font-medium", children: "Cache write" }),
2066
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "py-2 text-right font-medium", children: getUsageSummaryLabel(props.labels, props.metricKind, props.dimension) })
2067
+ ] }) }),
2068
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b last:border-0", children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-muted-foreground", children: formatDate(row.bucket) }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "max-w-72 truncate py-2 pr-4 font-medium", children: row.groupLabel }),
2071
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "input"), props.metricKind) }),
2072
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "output"), props.metricKind) }),
2073
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "reasoning"), props.metricKind) }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "cacheRead"), props.metricKind) }),
2075
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 pr-4 text-right", children: formatMetricValue(getUsagePointValue(row, props.metricKind, "cacheWrite"), props.metricKind) }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-2 text-right font-medium", children: formatMetricValue(getUsagePointValue(row, props.metricKind, props.dimension), props.metricKind) })
2077
+ ] }, `${row.bucket}:${row.groupKey}`)) })
2078
+ ] }) })
2079
+ ] });
2080
+ }
1811
2081
  function ActivityChart(props) {
1812
2082
  const trimmedPoints = props.points.slice(-props.maxBars);
1813
2083
  const maxUsageValue = Math.max(
@@ -2006,6 +2276,28 @@ function formatBucket(bucket, interval) {
2006
2276
  day: "numeric"
2007
2277
  });
2008
2278
  }
2279
+ function formatUsageBucket(bucket, interval) {
2280
+ const date = new Date(bucket);
2281
+ if (Number.isNaN(date.getTime())) return bucket;
2282
+ if (interval === "minute" || interval === "hour") {
2283
+ return date.toLocaleString(void 0, {
2284
+ month: "short",
2285
+ day: "numeric",
2286
+ hour: "numeric",
2287
+ minute: interval === "minute" ? "2-digit" : void 0
2288
+ });
2289
+ }
2290
+ if (interval === "month") {
2291
+ return date.toLocaleDateString(void 0, {
2292
+ month: "short",
2293
+ year: "numeric"
2294
+ });
2295
+ }
2296
+ return date.toLocaleDateString(void 0, {
2297
+ month: "short",
2298
+ day: "numeric"
2299
+ });
2300
+ }
2009
2301
  function formatDate(value) {
2010
2302
  if (!value) return "No activity";
2011
2303
  const date = new Date(value);
@@ -2028,6 +2320,58 @@ var USAGE_DIMENSIONS = [
2028
2320
  "cacheRead",
2029
2321
  "cacheWrite"
2030
2322
  ];
2323
+ var USAGE_CHART_COLORS = [
2324
+ "hsl(var(--primary))",
2325
+ "hsl(var(--chart-2, 142 76% 36%))",
2326
+ "hsl(var(--chart-3, 38 92% 50%))",
2327
+ "hsl(var(--chart-4, 199 89% 48%))",
2328
+ "hsl(var(--chart-5, 346 77% 49%))",
2329
+ "hsl(var(--muted-foreground))"
2330
+ ];
2331
+ function emptyToUndefined(value) {
2332
+ const trimmed = value.trim();
2333
+ return trimmed.length > 0 ? trimmed : void 0;
2334
+ }
2335
+ function getUsageRange(period, customFrom, customTo) {
2336
+ if (period === "custom") {
2337
+ return {
2338
+ from: customFrom ? new Date(customFrom).toISOString() : void 0,
2339
+ to: customTo ? new Date(customTo).toISOString() : void 0
2340
+ };
2341
+ }
2342
+ const to = /* @__PURE__ */ new Date();
2343
+ const from = new Date(to);
2344
+ if (period === "24h") {
2345
+ from.setHours(from.getHours() - 24);
2346
+ } else if (period === "30d") {
2347
+ from.setDate(from.getDate() - 30);
2348
+ } else {
2349
+ from.setDate(from.getDate() - 7);
2350
+ }
2351
+ return { from: from.toISOString(), to: to.toISOString() };
2352
+ }
2353
+ function buildUsageBuckets(points) {
2354
+ const byBucket = /* @__PURE__ */ new Map();
2355
+ for (const point of points) {
2356
+ const rows = byBucket.get(point.bucket) ?? [];
2357
+ rows.push(point);
2358
+ byBucket.set(point.bucket, rows);
2359
+ }
2360
+ return Array.from(byBucket.entries()).map(([bucket, bucketPoints]) => ({ bucket, points: bucketPoints })).sort((a, b) => new Date(a.bucket).getTime() - new Date(b.bucket).getTime());
2361
+ }
2362
+ function topUsageGroups(points, metricKind, dimension) {
2363
+ const totals = /* @__PURE__ */ new Map();
2364
+ for (const point of points) {
2365
+ const existing = totals.get(point.groupKey) ?? {
2366
+ key: point.groupKey,
2367
+ label: point.groupLabel,
2368
+ value: 0
2369
+ };
2370
+ existing.value += getUsagePointValue(point, metricKind, dimension);
2371
+ totals.set(point.groupKey, existing);
2372
+ }
2373
+ return Array.from(totals.values()).sort((a, b) => b.value - a.value).slice(0, 6);
2374
+ }
2031
2375
  function getUsageDimensionLabel(labels, dimension) {
2032
2376
  switch (dimension) {
2033
2377
  case "input":
@@ -2046,11 +2390,12 @@ function getUsageDimensionLabel(labels, dimension) {
2046
2390
  }
2047
2391
  }
2048
2392
  function getUsageSummaryLabel(labels, metricKind, dimension) {
2049
- const metricLabel = metricKind === "cost" ? labels.usageMetricCost : labels.usageMetricTokens;
2393
+ const metricLabel = metricKind === "cost" ? labels.usageMetricCost : metricKind === "calls" ? "Calls" : labels.usageMetricTokens;
2050
2394
  return `${getUsageDimensionLabel(labels, dimension)} ${metricLabel}`;
2051
2395
  }
2052
2396
  function getOverviewUsageValue(overview, metricKind, dimension) {
2053
2397
  const llmTotals = overview.llmTotals;
2398
+ if (metricKind === "calls") return llmTotals.totalCalls;
2054
2399
  if (metricKind === "cost") {
2055
2400
  switch (dimension) {
2056
2401
  case "input":
@@ -2102,6 +2447,7 @@ function getAgentUsageValue(agent, metricKind, dimension) {
2102
2447
  return agent.totalCostUsd;
2103
2448
  }
2104
2449
  }
2450
+ if (metricKind === "calls") return agent.llmCallCount;
2105
2451
  switch (dimension) {
2106
2452
  case "input":
2107
2453
  return agent.inputTokens;
@@ -2136,6 +2482,42 @@ function getActivityUsageValue(point, metricKind, dimension) {
2136
2482
  return point.totalCostUsd;
2137
2483
  }
2138
2484
  }
2485
+ if (metricKind === "calls") return point.llmCallCount;
2486
+ switch (dimension) {
2487
+ case "input":
2488
+ return point.inputTokens;
2489
+ case "output":
2490
+ return point.outputTokens;
2491
+ case "reasoning":
2492
+ return point.reasoningTokens;
2493
+ case "cacheRead":
2494
+ return point.cacheReadInputTokens;
2495
+ case "cacheWrite":
2496
+ return point.cacheCreationInputTokens;
2497
+ case "total":
2498
+ default:
2499
+ return point.totalTokens;
2500
+ }
2501
+ }
2502
+ function getUsagePointValue(point, metricKind, dimension) {
2503
+ if (metricKind === "cost") {
2504
+ switch (dimension) {
2505
+ case "input":
2506
+ return point.inputCostUsd;
2507
+ case "output":
2508
+ return point.outputCostUsd;
2509
+ case "reasoning":
2510
+ return point.reasoningCostUsd;
2511
+ case "cacheRead":
2512
+ return point.cacheReadInputCostUsd;
2513
+ case "cacheWrite":
2514
+ return point.cacheCreationInputCostUsd;
2515
+ case "total":
2516
+ default:
2517
+ return point.totalCostUsd;
2518
+ }
2519
+ }
2520
+ if (metricKind === "calls") return point.totalCalls;
2139
2521
  switch (dimension) {
2140
2522
  case "input":
2141
2523
  return point.inputTokens;
@@ -3209,6 +3591,7 @@ var CopilotzAdmin = ({
3209
3591
  participants: admin.participants,
3210
3592
  agents: admin.agents,
3211
3593
  interval: admin.filters.interval,
3594
+ namespace,
3212
3595
  threadSearch,
3213
3596
  participantSearch,
3214
3597
  agentSearch,
@@ -3337,6 +3720,7 @@ var CopilotzAdmin = ({
3337
3720
  fetchAdminOverview,
3338
3721
  fetchAdminParticipants,
3339
3722
  fetchAdminThreads,
3723
+ fetchAdminUsage,
3340
3724
  fetchCollectionItem,
3341
3725
  fetchCollectionItems,
3342
3726
  fetchCollectionNames,