@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 +458 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +458 -75
- package/dist/index.js.map +1 -1
- package/dist/styles.css +57 -35
- package/package.json +1 -1
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
|
-
"
|
|
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__ */
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
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,
|