@carlonicora/nextjs-jsonapi 2.3.0 → 2.5.0
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/{BlockNoteEditor-PNAVMGHB.js → BlockNoteEditor-TIMGYKAA.js} +19 -19
- package/dist/{BlockNoteEditor-PNAVMGHB.js.map → BlockNoteEditor-TIMGYKAA.js.map} +1 -1
- package/dist/{BlockNoteEditor-3PS3HYVI.mjs → BlockNoteEditor-YFJMXJEU.mjs} +4 -4
- package/dist/billing/index.d.mts +40 -1
- package/dist/billing/index.d.ts +40 -1
- package/dist/billing/index.js +519 -462
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +298 -241
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-QYUYQO2M.js → chunk-7HKJNUW7.js} +1144 -953
- package/dist/chunk-7HKJNUW7.js.map +1 -0
- package/dist/{chunk-MN2BUSEU.mjs → chunk-DJWNNV52.mjs} +2 -2
- package/dist/{chunk-EQEW64CH.mjs → chunk-FS4RVV3K.mjs} +242 -1
- package/dist/chunk-FS4RVV3K.mjs.map +1 -0
- package/dist/{chunk-FSMXD3N7.mjs → chunk-G4YS52SO.mjs} +2547 -2356
- package/dist/chunk-G4YS52SO.mjs.map +1 -0
- package/dist/{chunk-OHJQYVSI.js → chunk-OTEDZ6YW.js} +7 -7
- package/dist/{chunk-OHJQYVSI.js.map → chunk-OTEDZ6YW.js.map} +1 -1
- package/dist/{chunk-X5TPKMBS.js → chunk-RTOC53EZ.js} +247 -6
- package/dist/chunk-RTOC53EZ.js.map +1 -0
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +137 -4
- package/dist/components/index.d.ts +137 -4
- package/dist/components/index.js +16 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +15 -3
- package/dist/{tokenusage-admin.module-5wJ_tZTj.d.ts → config-BIjrg5wd.d.ts} +39 -1
- package/dist/{tokenusage-admin.module-DQVT4O4j.d.mts → config-BRUjtCd1.d.mts} +39 -1
- package/dist/{content.fields-xH3TGvVk.d.mts → content.fields-1AlHDtDb.d.mts} +1 -0
- package/dist/{content.fields-xH3TGvVk.d.ts → content.fields-1AlHDtDb.d.ts} +1 -0
- package/dist/contexts/index.d.mts +22 -1
- package/dist/contexts/index.d.ts +22 -1
- package/dist/contexts/index.js +8 -4
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +7 -3
- package/dist/core/index.d.mts +6 -3
- package/dist/core/index.d.ts +6 -3
- package/dist/core/index.js +8 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +7 -1
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/tokenusage/index.d.mts +413 -14
- package/dist/features/tokenusage/index.d.ts +413 -14
- package/dist/features/tokenusage/index.js +481 -120
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +431 -70
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/package.json +1 -1
- package/src/billing/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/contexts/index.ts +1 -0
- package/src/core/index.ts +10 -0
- package/src/core/registry/ModuleRegistry.ts +4 -0
- package/src/features/administration/components/AdminIndexContainer.tsx +150 -0
- package/src/features/administration/components/__tests__/AdminIndexContainer.spec.tsx +96 -0
- package/src/features/administration/components/index.ts +1 -0
- package/src/features/administration/contexts/AdministrationContext.tsx +47 -0
- package/src/features/administration/contexts/index.ts +1 -0
- package/src/features/administration/data/admin-section.types.ts +30 -0
- package/src/features/administration/i18n-keys.ts +26 -0
- package/src/features/administration/index.ts +3 -0
- package/src/features/billing/contexts/BillingContext.tsx +95 -0
- package/src/features/billing/contexts/index.ts +1 -0
- package/src/features/billing/index.ts +1 -0
- package/src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx +39 -27
- package/src/features/billing/stripe-product/components/containers/ProductsAdminPageContainer.tsx +27 -0
- package/src/features/billing/stripe-product/components/containers/index.ts +1 -0
- package/src/features/company/components/containers/CompaniesListContainer.tsx +25 -0
- package/src/features/company/components/containers/index.ts +1 -0
- package/src/features/company/components/lists/CompaniesList.tsx +12 -1
- package/src/features/company/contexts/CompanyContext.tsx +13 -1
- package/src/features/tokenusage/components/TokenUsageAdminContainer.tsx +20 -4
- package/src/features/tokenusage/components/TokenUsageAdminTiles.tsx +31 -18
- package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +13 -13
- package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +24 -7
- package/src/features/tokenusage/components/TokenUsageReportContainer.tsx +102 -0
- package/src/features/tokenusage/components/TokenUsageReportFilterBar.tsx +34 -0
- package/src/features/tokenusage/components/TokenUsageReportTiles.tsx +161 -0
- package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +15 -13
- package/src/features/tokenusage/components/__tests__/TokenUsageAdminTiles.spec.tsx +4 -2
- package/src/features/tokenusage/components/__tests__/TokenUsageRankedBar.spec.tsx +3 -1
- package/src/features/tokenusage/contexts/TokenUsageAdminContext.tsx +6 -3
- package/src/features/tokenusage/contexts/TokenUsageReportContext.tsx +199 -0
- package/src/features/tokenusage/data/TokenUsageReportService.ts +65 -0
- package/src/features/tokenusage/data/index.ts +9 -0
- package/src/features/tokenusage/data/tokenusage-report-breakdown.interface.ts +12 -0
- package/src/features/tokenusage/data/tokenusage-report-breakdown.ts +94 -0
- package/src/features/tokenusage/data/tokenusage-report-summary.interface.ts +12 -0
- package/src/features/tokenusage/data/tokenusage-report-summary.ts +87 -0
- package/src/features/tokenusage/data/tokenusage-report-timeline.interface.ts +13 -0
- package/src/features/tokenusage/data/tokenusage-report-timeline.ts +94 -0
- package/src/features/tokenusage/data/tokenusage-report.types.ts +51 -0
- package/src/features/tokenusage/i18n-keys.ts +28 -0
- package/src/features/tokenusage/index.ts +18 -0
- package/src/features/tokenusage/lib/config.ts +22 -0
- package/src/features/tokenusage/lib/formatters.ts +100 -0
- package/src/features/tokenusage/lib/metrics.ts +8 -26
- package/src/features/tokenusage/lib/palette.ts +41 -14
- package/src/features/tokenusage/tokenusage-admin.module.ts +4 -0
- package/src/features/tokenusage/tokenusage.modules.ts +40 -0
- package/src/features/user/components/containers/PlatformUsersContainer.tsx +23 -0
- package/src/features/user/components/containers/index.ts +1 -0
- package/src/features/user/components/lists/PlatformUsersList.tsx +45 -0
- package/src/features/user/components/lists/index.ts +1 -0
- package/src/features/user/data/user.fields.ts +1 -0
- package/src/features/user/hooks/useUserTableStructure.tsx +17 -0
- package/src/shadcnui/ui/chart.tsx +30 -3
- package/dist/chunk-EQEW64CH.mjs.map +0 -1
- package/dist/chunk-FSMXD3N7.mjs.map +0 -1
- package/dist/chunk-QYUYQO2M.js.map +0 -1
- package/dist/chunk-X5TPKMBS.js.map +0 -1
- /package/dist/{BlockNoteEditor-3PS3HYVI.mjs.map → BlockNoteEditor-YFJMXJEU.mjs.map} +0 -0
- /package/dist/{chunk-MN2BUSEU.mjs.map → chunk-DJWNNV52.mjs.map} +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { SharedProvider } from "../../../contexts";
|
|
6
|
+
import { usePageUrlGenerator } from "../../../hooks";
|
|
7
|
+
import { BreadcrumbItemData } from "../../../interfaces";
|
|
8
|
+
import { TokenUsageReportFilterBar } from "../components/TokenUsageReportFilterBar";
|
|
9
|
+
import type { TokenUsageReportBreakdownInterface } from "../data/tokenusage-report-breakdown.interface";
|
|
10
|
+
import type { TokenUsageReportSummaryInterface } from "../data/tokenusage-report-summary.interface";
|
|
11
|
+
import type { TokenUsageReportTimelineInterface } from "../data/tokenusage-report-timeline.interface";
|
|
12
|
+
import type { ReportMetric } from "../data/tokenusage-report.types";
|
|
13
|
+
import { TokenUsageReportService } from "../data/TokenUsageReportService";
|
|
14
|
+
|
|
15
|
+
/** Default page the breadcrumb links back to; overridable per host app. */
|
|
16
|
+
const TOKEN_USAGE_REPORT_PAGE_URL = "/tokenusage";
|
|
17
|
+
|
|
18
|
+
/** Rows kept per ranked panel before the backend folds the tail into "other". */
|
|
19
|
+
const DEFAULT_TOP_N = 10;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The unit this surface reports in, always. A game master is billed in credits,
|
|
23
|
+
* so credits are the only number that means anything to them: cost would leak
|
|
24
|
+
* platform margin and a raw token count is an implementation detail nobody is
|
|
25
|
+
* charged for. Pinned rather than exposed as a filter — there is no metric
|
|
26
|
+
* selector on the page.
|
|
27
|
+
*/
|
|
28
|
+
const REPORT_METRIC: ReportMetric = "credits";
|
|
29
|
+
|
|
30
|
+
export type TokenUsageReportFilterState = {
|
|
31
|
+
/** ISO 8601 instant. */
|
|
32
|
+
from: string;
|
|
33
|
+
/** ISO 8601 instant. */
|
|
34
|
+
to: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface TokenUsageReportContextType {
|
|
38
|
+
summary: TokenUsageReportSummaryInterface[];
|
|
39
|
+
timeline: TokenUsageReportTimelineInterface[];
|
|
40
|
+
byOperation: TokenUsageReportBreakdownInterface[];
|
|
41
|
+
/** Empty unless the host app declared a targetLabel. */
|
|
42
|
+
byTarget: TokenUsageReportBreakdownInterface[];
|
|
43
|
+
/** i18n key for the target panel's title; undefined when the host set no targetLabel. */
|
|
44
|
+
targetPanelTitleKey?: string;
|
|
45
|
+
filters: TokenUsageReportFilterState;
|
|
46
|
+
/** Merges a partial patch into the current filters; every key is optional. */
|
|
47
|
+
setFilters: (next: Partial<TokenUsageReportFilterState>) => void;
|
|
48
|
+
isLoading: boolean;
|
|
49
|
+
error: string | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const TokenUsageReportContext = createContext<TokenUsageReportContextType | undefined>(undefined);
|
|
53
|
+
|
|
54
|
+
/** Current calendar month to now, which is the window the page opens on. */
|
|
55
|
+
function defaultRange(): { from: string; to: string } {
|
|
56
|
+
const now = new Date();
|
|
57
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
|
|
58
|
+
return { from: start.toISOString(), to: now.toISOString() };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type TokenUsageReportProviderProps = {
|
|
62
|
+
children: ReactNode;
|
|
63
|
+
/**
|
|
64
|
+
* The Neo4j label the "by target" panel groups by, e.g. "Campaign". The set of
|
|
65
|
+
* things usage can be attributed to is application-specific, so the package
|
|
66
|
+
* cannot pick one — omit it and the panel is skipped rather than rendered
|
|
67
|
+
* empty, and no request is issued.
|
|
68
|
+
*/
|
|
69
|
+
targetLabel?: string;
|
|
70
|
+
/** i18n key for the target panel's title. Required when targetLabel is set. */
|
|
71
|
+
targetPanelTitleKey?: string;
|
|
72
|
+
/** ISO 8601 instant. Defaults to the start of the current month. */
|
|
73
|
+
initialFrom?: string;
|
|
74
|
+
/** ISO 8601 instant. Defaults to now. */
|
|
75
|
+
initialTo?: string;
|
|
76
|
+
/** Rows per ranked panel. Defaults to 10. */
|
|
77
|
+
topN?: number;
|
|
78
|
+
/** Route the breadcrumb links back to. */
|
|
79
|
+
pageUrl?: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Owns the date range of the self-service token-usage page, fetches the three
|
|
84
|
+
* panels behind it, and publishes the filter bar into the page title bar.
|
|
85
|
+
*
|
|
86
|
+
* The filter bar is rendered into `title.functions` here — NOT in the container —
|
|
87
|
+
* because `RoundPageContainer`'s title bar reads `title.functions` from
|
|
88
|
+
* `SharedContext`, and a descendant cannot inject nodes into an ancestor's
|
|
89
|
+
* provider value. That is why the filter state lives at this level.
|
|
90
|
+
*/
|
|
91
|
+
export const TokenUsageReportProvider = ({
|
|
92
|
+
children,
|
|
93
|
+
targetLabel,
|
|
94
|
+
targetPanelTitleKey,
|
|
95
|
+
initialFrom,
|
|
96
|
+
initialTo,
|
|
97
|
+
topN = DEFAULT_TOP_N,
|
|
98
|
+
pageUrl = TOKEN_USAGE_REPORT_PAGE_URL,
|
|
99
|
+
}: TokenUsageReportProviderProps) => {
|
|
100
|
+
const t = useTranslations();
|
|
101
|
+
const generateUrl = usePageUrlGenerator();
|
|
102
|
+
|
|
103
|
+
const [filters, setFilterState] = useState<TokenUsageReportFilterState>(() => {
|
|
104
|
+
const range = defaultRange();
|
|
105
|
+
return { from: initialFrom ?? range.from, to: initialTo ?? range.to };
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const [summary, setSummary] = useState<TokenUsageReportSummaryInterface[]>([]);
|
|
109
|
+
const [timeline, setTimeline] = useState<TokenUsageReportTimelineInterface[]>([]);
|
|
110
|
+
const [byOperation, setByOperation] = useState<TokenUsageReportBreakdownInterface[]>([]);
|
|
111
|
+
const [byTarget, setByTarget] = useState<TokenUsageReportBreakdownInterface[]>([]);
|
|
112
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
113
|
+
const [error, setError] = useState<string | null>(null);
|
|
114
|
+
|
|
115
|
+
const { from, to } = filters;
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
// `cancelled` is the out-of-order guard: a filter change fires a new request
|
|
119
|
+
// while the previous one is still in flight, and without this flag the slower
|
|
120
|
+
// (older) response would land last and overwrite the fresher state.
|
|
121
|
+
let cancelled = false;
|
|
122
|
+
|
|
123
|
+
setIsLoading(true);
|
|
124
|
+
setError(null);
|
|
125
|
+
|
|
126
|
+
const base = { from, to };
|
|
127
|
+
|
|
128
|
+
Promise.all([
|
|
129
|
+
TokenUsageReportService.getSummary(base),
|
|
130
|
+
// granularity is pinned to "day": week/month bucketing buys nothing on the
|
|
131
|
+
// ranges a single tenant browses.
|
|
132
|
+
TokenUsageReportService.getTimeline({ ...base, granularity: "day" }),
|
|
133
|
+
TokenUsageReportService.getBreakdown({ ...base, dimension: "operation", metric: REPORT_METRIC, limit: topN }),
|
|
134
|
+
// No targetLabel means the host app never opted in, so the request is
|
|
135
|
+
// skipped entirely rather than issued and discarded.
|
|
136
|
+
targetLabel
|
|
137
|
+
? TokenUsageReportService.getBreakdown({
|
|
138
|
+
...base,
|
|
139
|
+
dimension: "target",
|
|
140
|
+
targetLabel,
|
|
141
|
+
metric: REPORT_METRIC,
|
|
142
|
+
limit: topN,
|
|
143
|
+
})
|
|
144
|
+
: Promise.resolve<TokenUsageReportBreakdownInterface[]>([]),
|
|
145
|
+
])
|
|
146
|
+
.then(([nextSummary, nextTimeline, nextByOperation, nextByTarget]) => {
|
|
147
|
+
if (cancelled) return;
|
|
148
|
+
setSummary(nextSummary ?? []);
|
|
149
|
+
setTimeline(nextTimeline ?? []);
|
|
150
|
+
setByOperation(nextByOperation ?? []);
|
|
151
|
+
setByTarget(nextByTarget ?? []);
|
|
152
|
+
})
|
|
153
|
+
.catch((err) => {
|
|
154
|
+
if (cancelled) return;
|
|
155
|
+
console.error("Failed to load token usage:", err);
|
|
156
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
157
|
+
})
|
|
158
|
+
.finally(() => {
|
|
159
|
+
if (cancelled) return;
|
|
160
|
+
setIsLoading(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
cancelled = true;
|
|
165
|
+
};
|
|
166
|
+
}, [from, to, targetLabel, topN]);
|
|
167
|
+
|
|
168
|
+
const setFilters = useCallback((next: Partial<TokenUsageReportFilterState>) => {
|
|
169
|
+
setFilterState((prev) => ({ ...prev, ...next }));
|
|
170
|
+
}, []);
|
|
171
|
+
|
|
172
|
+
const breadcrumb = (): BreadcrumbItemData[] => [
|
|
173
|
+
{ name: t("token_usage.report.title"), href: generateUrl({ page: pageUrl }) },
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const title = () => ({
|
|
177
|
+
type: t("token_usage.report.title"),
|
|
178
|
+
functions: <TokenUsageReportFilterBar key="tokenUsageReportFilterBar" onChange={setFilters} />,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const contextValue = useMemo<TokenUsageReportContextType>(
|
|
182
|
+
() => ({ summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error }),
|
|
183
|
+
[summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error],
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<SharedProvider value={{ breadcrumbs: breadcrumb(), title: title() }}>
|
|
188
|
+
<TokenUsageReportContext.Provider value={contextValue}>{children}</TokenUsageReportContext.Provider>
|
|
189
|
+
</SharedProvider>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const useTokenUsageReport = (): TokenUsageReportContextType => {
|
|
194
|
+
const ctx = useContext(TokenUsageReportContext);
|
|
195
|
+
if (!ctx) {
|
|
196
|
+
throw new Error("useTokenUsageReport() called outside <TokenUsageReportProvider>.");
|
|
197
|
+
}
|
|
198
|
+
return ctx;
|
|
199
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AbstractService, EndpointCreator, HttpMethod, Modules } from "../../../core";
|
|
2
|
+
import { TokenUsageReportBreakdownInterface } from "./tokenusage-report-breakdown.interface";
|
|
3
|
+
import { TokenUsageReportSummaryInterface } from "./tokenusage-report-summary.interface";
|
|
4
|
+
import { TokenUsageReportTimelineInterface } from "./tokenusage-report-timeline.interface";
|
|
5
|
+
import { ReportDimension, ReportMetric, TokenUsageReportFilters } from "./tokenusage-report.types";
|
|
6
|
+
|
|
7
|
+
function withFilters(endpoint: EndpointCreator, filters: TokenUsageReportFilters): EndpointCreator {
|
|
8
|
+
endpoint.addAdditionalParam("from", filters.from);
|
|
9
|
+
endpoint.addAdditionalParam("to", filters.to);
|
|
10
|
+
return endpoint;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The self-service token-usage endpoints.
|
|
15
|
+
*
|
|
16
|
+
* No companyId parameter anywhere: the backend scopes every query to the
|
|
17
|
+
* caller's own company through the CLS preamble, so there is nothing for the
|
|
18
|
+
* client to name. No `metric: "cost"` either — the controller rejects it.
|
|
19
|
+
*/
|
|
20
|
+
export class TokenUsageReportService extends AbstractService {
|
|
21
|
+
/** Two rows: window "current" and "previous". */
|
|
22
|
+
static async getSummary(filters: TokenUsageReportFilters): Promise<TokenUsageReportSummaryInterface[]> {
|
|
23
|
+
const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportSummary }), filters);
|
|
24
|
+
|
|
25
|
+
return this.callApi<TokenUsageReportSummaryInterface[]>({
|
|
26
|
+
type: Modules.TokenUsageReportSummary,
|
|
27
|
+
method: HttpMethod.GET,
|
|
28
|
+
endpoint: endpoint.generate(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static async getTimeline(
|
|
33
|
+
params: TokenUsageReportFilters & { granularity: "day" },
|
|
34
|
+
): Promise<TokenUsageReportTimelineInterface[]> {
|
|
35
|
+
const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportTimeline }), params);
|
|
36
|
+
endpoint.addAdditionalParam("granularity", params.granularity);
|
|
37
|
+
|
|
38
|
+
return this.callApi<TokenUsageReportTimelineInterface[]>({
|
|
39
|
+
type: Modules.TokenUsageReportTimeline,
|
|
40
|
+
method: HttpMethod.GET,
|
|
41
|
+
endpoint: endpoint.generate(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static async getBreakdown(
|
|
46
|
+
params: TokenUsageReportFilters & {
|
|
47
|
+
dimension: ReportDimension;
|
|
48
|
+
targetLabel?: string;
|
|
49
|
+
metric: ReportMetric;
|
|
50
|
+
limit?: number;
|
|
51
|
+
},
|
|
52
|
+
): Promise<TokenUsageReportBreakdownInterface[]> {
|
|
53
|
+
const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.TokenUsageReportBreakdown }), params);
|
|
54
|
+
endpoint.addAdditionalParam("dimension", params.dimension);
|
|
55
|
+
if (params.targetLabel) endpoint.addAdditionalParam("targetLabel", params.targetLabel);
|
|
56
|
+
endpoint.addAdditionalParam("metric", params.metric);
|
|
57
|
+
if (params.limit !== undefined) endpoint.addAdditionalParam("limit", String(params.limit));
|
|
58
|
+
|
|
59
|
+
return this.callApi<TokenUsageReportBreakdownInterface[]>({
|
|
60
|
+
type: Modules.TokenUsageReportBreakdown,
|
|
61
|
+
method: HttpMethod.GET,
|
|
62
|
+
endpoint: endpoint.generate(),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -6,3 +6,12 @@ export type { TokenUsageAdminTimelineInterface } from "./tokenusage-admin-timeli
|
|
|
6
6
|
export { TokenUsageAdminBreakdown } from "./tokenusage-admin-breakdown";
|
|
7
7
|
export type { TokenUsageAdminBreakdownInterface } from "./tokenusage-admin-breakdown.interface";
|
|
8
8
|
export { TokenUsageAdminService } from "./TokenUsageAdminService";
|
|
9
|
+
|
|
10
|
+
export * from "./tokenusage-report.types";
|
|
11
|
+
export { TokenUsageReportSummary } from "./tokenusage-report-summary";
|
|
12
|
+
export type { TokenUsageReportSummaryInterface } from "./tokenusage-report-summary.interface";
|
|
13
|
+
export { TokenUsageReportTimeline } from "./tokenusage-report-timeline";
|
|
14
|
+
export type { TokenUsageReportTimelineInterface } from "./tokenusage-report-timeline.interface";
|
|
15
|
+
export { TokenUsageReportBreakdown } from "./tokenusage-report-breakdown";
|
|
16
|
+
export type { TokenUsageReportBreakdownInterface } from "./tokenusage-report-breakdown.interface";
|
|
17
|
+
export { TokenUsageReportService } from "./TokenUsageReportService";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
|
|
3
|
+
export interface TokenUsageReportBreakdownInterface extends ApiDataInterface {
|
|
4
|
+
get label(): string;
|
|
5
|
+
get sublabel(): string | undefined;
|
|
6
|
+
get cost(): number;
|
|
7
|
+
get credits(): number;
|
|
8
|
+
get tokensIn(): number;
|
|
9
|
+
get tokensOut(): number;
|
|
10
|
+
get cached(): number;
|
|
11
|
+
get calls(): number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from "../../../core";
|
|
2
|
+
import { TokenUsageReportBreakdownInterface } from "./tokenusage-report-breakdown.interface";
|
|
3
|
+
import { TokenUsageReportBreakdownInput } from "./tokenusage-report.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One ranked row of a self-service breakdown. The same shape serves both
|
|
7
|
+
* dimensions — operation and target — which is why the two "by-X" panels
|
|
8
|
+
* collapse into a single `breakdown?dimension=` call.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately narrower than the administrative row: no `activeUsers`,
|
|
11
|
+
* `monthlyCredits` or `availableMonthlyCredits`, which are company-fleet
|
|
12
|
+
* figures a single company has no business seeing.
|
|
13
|
+
*/
|
|
14
|
+
export class TokenUsageReportBreakdown extends AbstractApiData implements TokenUsageReportBreakdownInterface {
|
|
15
|
+
private _label: string = "";
|
|
16
|
+
private _sublabel?: string;
|
|
17
|
+
private _cost: number = 0;
|
|
18
|
+
private _credits: number = 0;
|
|
19
|
+
private _tokensIn: number = 0;
|
|
20
|
+
private _tokensOut: number = 0;
|
|
21
|
+
private _cached: number = 0;
|
|
22
|
+
private _calls: number = 0;
|
|
23
|
+
|
|
24
|
+
get label(): string {
|
|
25
|
+
return this._label;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get sublabel(): string | undefined {
|
|
29
|
+
return this._sublabel;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get cost(): number {
|
|
33
|
+
return this._cost;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get credits(): number {
|
|
37
|
+
return this._credits;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get tokensIn(): number {
|
|
41
|
+
return this._tokensIn;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get tokensOut(): number {
|
|
45
|
+
return this._tokensOut;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get cached(): number {
|
|
49
|
+
return this._cached;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get calls(): number {
|
|
53
|
+
return this._calls;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
57
|
+
super.rehydrate(data);
|
|
58
|
+
|
|
59
|
+
const attrs = data.jsonApi.attributes;
|
|
60
|
+
this._label = attrs.label ?? "";
|
|
61
|
+
this._sublabel = attrs.sublabel ?? undefined;
|
|
62
|
+
this._cost = attrs.cost ?? 0;
|
|
63
|
+
this._credits = attrs.credits ?? 0;
|
|
64
|
+
this._tokensIn = attrs.tokensIn ?? 0;
|
|
65
|
+
this._tokensOut = attrs.tokensOut ?? 0;
|
|
66
|
+
this._cached = attrs.cached ?? 0;
|
|
67
|
+
this._calls = attrs.calls ?? 0;
|
|
68
|
+
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
createJsonApi(data: TokenUsageReportBreakdownInput) {
|
|
73
|
+
const response: any = {
|
|
74
|
+
data: {
|
|
75
|
+
type: Modules.TokenUsageReportBreakdown.name,
|
|
76
|
+
id: data.id,
|
|
77
|
+
attributes: {},
|
|
78
|
+
relationships: {},
|
|
79
|
+
},
|
|
80
|
+
included: [],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (data.label !== undefined) response.data.attributes.label = data.label;
|
|
84
|
+
if (data.sublabel !== undefined) response.data.attributes.sublabel = data.sublabel;
|
|
85
|
+
if (data.cost !== undefined) response.data.attributes.cost = data.cost;
|
|
86
|
+
if (data.credits !== undefined) response.data.attributes.credits = data.credits;
|
|
87
|
+
if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;
|
|
88
|
+
if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;
|
|
89
|
+
if (data.cached !== undefined) response.data.attributes.cached = data.cached;
|
|
90
|
+
if (data.calls !== undefined) response.data.attributes.calls = data.calls;
|
|
91
|
+
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
|
|
3
|
+
export interface TokenUsageReportSummaryInterface extends ApiDataInterface {
|
|
4
|
+
/** "current" or "previous" — the equal-length preceding span. */
|
|
5
|
+
get window(): string;
|
|
6
|
+
get cost(): number;
|
|
7
|
+
get credits(): number;
|
|
8
|
+
get tokensIn(): number;
|
|
9
|
+
get tokensOut(): number;
|
|
10
|
+
get cached(): number;
|
|
11
|
+
get calls(): number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from "../../../core";
|
|
2
|
+
import { TokenUsageReportSummaryInterface } from "./tokenusage-report-summary.interface";
|
|
3
|
+
import { TokenUsageReportSummaryInput } from "./tokenusage-report.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One row of the self-service summary: the caller's own company observed over
|
|
7
|
+
* one time window. Two rows are returned per request — "current" and the
|
|
8
|
+
* equal-length "previous" span — so the KPI tiles can render a value and its
|
|
9
|
+
* delta without a second call.
|
|
10
|
+
*
|
|
11
|
+
* There is no `scope` here: a company only ever sees itself, so the
|
|
12
|
+
* customer/platform split that the administrative summary carries is absent.
|
|
13
|
+
*/
|
|
14
|
+
export class TokenUsageReportSummary extends AbstractApiData implements TokenUsageReportSummaryInterface {
|
|
15
|
+
private _window: string = "";
|
|
16
|
+
private _cost: number = 0;
|
|
17
|
+
private _credits: number = 0;
|
|
18
|
+
private _tokensIn: number = 0;
|
|
19
|
+
private _tokensOut: number = 0;
|
|
20
|
+
private _cached: number = 0;
|
|
21
|
+
private _calls: number = 0;
|
|
22
|
+
|
|
23
|
+
get window(): string {
|
|
24
|
+
return this._window;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get cost(): number {
|
|
28
|
+
return this._cost;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get credits(): number {
|
|
32
|
+
return this._credits;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get tokensIn(): number {
|
|
36
|
+
return this._tokensIn;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get tokensOut(): number {
|
|
40
|
+
return this._tokensOut;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get cached(): number {
|
|
44
|
+
return this._cached;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get calls(): number {
|
|
48
|
+
return this._calls;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
52
|
+
super.rehydrate(data);
|
|
53
|
+
|
|
54
|
+
const attrs = data.jsonApi.attributes;
|
|
55
|
+
this._window = attrs.window ?? "";
|
|
56
|
+
this._cost = attrs.cost ?? 0;
|
|
57
|
+
this._credits = attrs.credits ?? 0;
|
|
58
|
+
this._tokensIn = attrs.tokensIn ?? 0;
|
|
59
|
+
this._tokensOut = attrs.tokensOut ?? 0;
|
|
60
|
+
this._cached = attrs.cached ?? 0;
|
|
61
|
+
this._calls = attrs.calls ?? 0;
|
|
62
|
+
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
createJsonApi(data: TokenUsageReportSummaryInput) {
|
|
67
|
+
const response: any = {
|
|
68
|
+
data: {
|
|
69
|
+
type: Modules.TokenUsageReportSummary.name,
|
|
70
|
+
id: data.id,
|
|
71
|
+
attributes: {},
|
|
72
|
+
relationships: {},
|
|
73
|
+
},
|
|
74
|
+
included: [],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (data.window !== undefined) response.data.attributes.window = data.window;
|
|
78
|
+
if (data.cost !== undefined) response.data.attributes.cost = data.cost;
|
|
79
|
+
if (data.credits !== undefined) response.data.attributes.credits = data.credits;
|
|
80
|
+
if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;
|
|
81
|
+
if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;
|
|
82
|
+
if (data.cached !== undefined) response.data.attributes.cached = data.cached;
|
|
83
|
+
if (data.calls !== undefined) response.data.attributes.calls = data.calls;
|
|
84
|
+
|
|
85
|
+
return response;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
|
|
3
|
+
export interface TokenUsageReportTimelineInterface extends ApiDataInterface {
|
|
4
|
+
/** Backend declares this `type: "date"`, so it is a Date in memory. */
|
|
5
|
+
get bucket(): Date;
|
|
6
|
+
get series(): string;
|
|
7
|
+
get cost(): number;
|
|
8
|
+
get credits(): number;
|
|
9
|
+
get tokensIn(): number;
|
|
10
|
+
get tokensOut(): number;
|
|
11
|
+
get cached(): number;
|
|
12
|
+
get calls(): number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AbstractApiData, formatLocalDate, JsonApiHydratedDataInterface, Modules } from "../../../core";
|
|
2
|
+
import { TokenUsageReportTimelineInterface } from "./tokenusage-report-timeline.interface";
|
|
3
|
+
import { TokenUsageReportTimelineInput } from "./tokenusage-report.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One (bucket, series) cell of the self-service usage-over-time chart. Rows
|
|
7
|
+
* arrive flat; the chart pivots them into stacked columns.
|
|
8
|
+
*/
|
|
9
|
+
export class TokenUsageReportTimeline extends AbstractApiData implements TokenUsageReportTimelineInterface {
|
|
10
|
+
private _bucket: Date = new Date(0);
|
|
11
|
+
private _series: string = "";
|
|
12
|
+
private _cost: number = 0;
|
|
13
|
+
private _credits: number = 0;
|
|
14
|
+
private _tokensIn: number = 0;
|
|
15
|
+
private _tokensOut: number = 0;
|
|
16
|
+
private _cached: number = 0;
|
|
17
|
+
private _calls: number = 0;
|
|
18
|
+
|
|
19
|
+
get bucket(): Date {
|
|
20
|
+
return this._bucket;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get series(): string {
|
|
24
|
+
return this._series;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get cost(): number {
|
|
28
|
+
return this._cost;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get credits(): number {
|
|
32
|
+
return this._credits;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get tokensIn(): number {
|
|
36
|
+
return this._tokensIn;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get tokensOut(): number {
|
|
40
|
+
return this._tokensOut;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get cached(): number {
|
|
44
|
+
return this._cached;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get calls(): number {
|
|
48
|
+
return this._calls;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
52
|
+
super.rehydrate(data);
|
|
53
|
+
|
|
54
|
+
const attrs = data.jsonApi.attributes;
|
|
55
|
+
// Wire format is "YYYY-MM-DD"; the interface promises a Date.
|
|
56
|
+
this._bucket = attrs.bucket ? new Date(attrs.bucket) : new Date(0);
|
|
57
|
+
this._series = attrs.series ?? "";
|
|
58
|
+
this._cost = attrs.cost ?? 0;
|
|
59
|
+
this._credits = attrs.credits ?? 0;
|
|
60
|
+
this._tokensIn = attrs.tokensIn ?? 0;
|
|
61
|
+
this._tokensOut = attrs.tokensOut ?? 0;
|
|
62
|
+
this._cached = attrs.cached ?? 0;
|
|
63
|
+
this._calls = attrs.calls ?? 0;
|
|
64
|
+
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
createJsonApi(data: TokenUsageReportTimelineInput) {
|
|
69
|
+
const response: any = {
|
|
70
|
+
data: {
|
|
71
|
+
type: Modules.TokenUsageReportTimeline.name,
|
|
72
|
+
id: data.id,
|
|
73
|
+
attributes: {},
|
|
74
|
+
relationships: {},
|
|
75
|
+
},
|
|
76
|
+
included: [],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// `bucket` is type: "date" on the backend descriptor. formatLocalDate is
|
|
80
|
+
// mandatory — passing the raw Date lets JSON.stringify call .toISOString(),
|
|
81
|
+
// which UTC-shifts and can move the bucket a day. Imported from the package
|
|
82
|
+
// core, never reimplemented inline.
|
|
83
|
+
if (data.bucket !== undefined) response.data.attributes.bucket = formatLocalDate(data.bucket);
|
|
84
|
+
if (data.series !== undefined) response.data.attributes.series = data.series;
|
|
85
|
+
if (data.cost !== undefined) response.data.attributes.cost = data.cost;
|
|
86
|
+
if (data.credits !== undefined) response.data.attributes.credits = data.credits;
|
|
87
|
+
if (data.tokensIn !== undefined) response.data.attributes.tokensIn = data.tokensIn;
|
|
88
|
+
if (data.tokensOut !== undefined) response.data.attributes.tokensOut = data.tokensOut;
|
|
89
|
+
if (data.cached !== undefined) response.data.attributes.cached = data.cached;
|
|
90
|
+
if (data.calls !== undefined) response.data.attributes.calls = data.calls;
|
|
91
|
+
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Metrics the self-service surface may request. `cost` is deliberately absent. */
|
|
2
|
+
export type ReportMetric = "credits" | "tokens";
|
|
3
|
+
|
|
4
|
+
/** Dimensions the self-service breakdown can group by. */
|
|
5
|
+
export type ReportDimension = "operation" | "target";
|
|
6
|
+
|
|
7
|
+
export type TokenUsageReportFilters = {
|
|
8
|
+
/** ISO 8601 instant. */
|
|
9
|
+
from: string;
|
|
10
|
+
/** ISO 8601 instant. */
|
|
11
|
+
to: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Input types for createJsonApi(). `id` is always required — JSON:API resource
|
|
15
|
+
// objects are identified.
|
|
16
|
+
|
|
17
|
+
export type TokenUsageReportSummaryInput = {
|
|
18
|
+
id: string;
|
|
19
|
+
window: string;
|
|
20
|
+
cost: number;
|
|
21
|
+
credits: number;
|
|
22
|
+
tokensIn: number;
|
|
23
|
+
tokensOut: number;
|
|
24
|
+
cached: number;
|
|
25
|
+
calls: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type TokenUsageReportTimelineInput = {
|
|
29
|
+
id: string;
|
|
30
|
+
/** Backend field type is "date" — emitted via formatLocalDate, never raw. */
|
|
31
|
+
bucket: Date;
|
|
32
|
+
series: string;
|
|
33
|
+
cost: number;
|
|
34
|
+
credits: number;
|
|
35
|
+
tokensIn: number;
|
|
36
|
+
tokensOut: number;
|
|
37
|
+
cached: number;
|
|
38
|
+
calls: number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TokenUsageReportBreakdownInput = {
|
|
42
|
+
id: string;
|
|
43
|
+
label: string;
|
|
44
|
+
sublabel?: string;
|
|
45
|
+
cost: number;
|
|
46
|
+
credits: number;
|
|
47
|
+
tokensIn: number;
|
|
48
|
+
tokensOut: number;
|
|
49
|
+
cached: number;
|
|
50
|
+
calls: number;
|
|
51
|
+
};
|
|
@@ -63,3 +63,31 @@ export const TOKEN_USAGE_ADMIN_I18N_KEYS = [
|
|
|
63
63
|
"token_usage.series.other",
|
|
64
64
|
"token_usage.timeline.empty",
|
|
65
65
|
] as const;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The fixed namespace of i18n keys the self-service token-usage feature reads.
|
|
69
|
+
* Consuming apps must define each entry in their messages/<locale>.json — this
|
|
70
|
+
* list is the contract between the package and the app.
|
|
71
|
+
*
|
|
72
|
+
* The target panel's title is NOT listed: its key is supplied per app through
|
|
73
|
+
* the provider's `targetPanelTitleKey`, because what usage is attributed to is
|
|
74
|
+
* application-specific. Operation labels resolve through
|
|
75
|
+
* `token_usage.types.<camelCase>` with the raw key as fallback, so that
|
|
76
|
+
* namespace stays the app's own vocabulary.
|
|
77
|
+
*/
|
|
78
|
+
export const TOKEN_USAGE_REPORT_I18N_KEYS = [
|
|
79
|
+
// Page + KPI tiles
|
|
80
|
+
"token_usage.report.title",
|
|
81
|
+
"token_usage.report.used_in_period",
|
|
82
|
+
"token_usage.report.vs_previous",
|
|
83
|
+
"token_usage.report.monthly_left",
|
|
84
|
+
"token_usage.report.extra_credits",
|
|
85
|
+
"token_usage.report.calls",
|
|
86
|
+
|
|
87
|
+
// Panel titles
|
|
88
|
+
"token_usage.report.usage_over_time",
|
|
89
|
+
"token_usage.report.by_operation",
|
|
90
|
+
|
|
91
|
+
// Shared states
|
|
92
|
+
"token_usage.report.no_data",
|
|
93
|
+
] as const;
|