@burdenoff/fe-libs 2026.1027.14 → 2026.1028.2
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/shared/assistant/api.d.ts +18 -3
- package/dist/shared/assistant/api.js +215 -183
- package/dist/shared/assistant/createSandboxAssistantTransport.js +2 -2
- package/dist/shared/assistant/index.d.ts +1 -0
- package/dist/shared/assistant/operations.d.ts +8 -0
- package/dist/shared/assistant/operations.js +2 -2
- package/dist/shared/assistant/productAiCredits.d.ts +28 -0
- package/dist/shared/assistant/productAiCredits.js +64 -0
- package/dist/shared/assistant/types.d.ts +15 -0
- package/dist/shared/pages/AiPage.js +92 -66
- package/dist/shared/pages/ai/BreakdownPanel.d.ts +6 -0
- package/dist/shared/pages/ai/BreakdownPanel.js +117 -0
- package/dist/shared/pages/ai/CreditsPanel.d.ts +10 -0
- package/dist/shared/pages/ai/CreditsPanel.js +64 -0
- package/dist/shared/pages/ai/contracts.d.ts +35 -0
- package/dist/shared/pages/ai/contracts.js +93 -1
- package/dist/shared-assistant.js +310 -309
- package/package.json +1 -1
|
@@ -28,7 +28,7 @@ function D(e, t) {
|
|
|
28
28
|
}
|
|
29
29
|
function O(O, k) {
|
|
30
30
|
let A = O.mode ?? "assistant", j = O.streamBudgetMs ?? S, M = { accumulateNarration: O.accumulateNarration === !0 }, N = O.ttlSeconds ?? 3600, P = O.isProdRuntime ?? (() => !1), F = O.enableCredits ?? !1, I = _("transport", O.product), L = (e) => async (t) => {
|
|
31
|
-
t.messageId && await f(t.workspaceId, t.sessionId, t.messageId, t.model, t.tokens, e);
|
|
31
|
+
t.messageId && await f(t.workspaceId, t.sessionId, t.messageId, t.model, t.tokens, e, O.product, O.aiCreditLedger);
|
|
32
32
|
}, R = new h({
|
|
33
33
|
product: O.product,
|
|
34
34
|
mode: A,
|
|
@@ -141,7 +141,7 @@ function O(O, k) {
|
|
|
141
141
|
let e = await b(I, n, L(r));
|
|
142
142
|
if (!e.ok) throw x(e.kind);
|
|
143
143
|
}
|
|
144
|
-
return F && l(n, r).catch((e) => {
|
|
144
|
+
return F && l(n, r, O.product, O.aiCreditLedger).catch((e) => {
|
|
145
145
|
t(e) && O.onQuotaExhausted?.(e);
|
|
146
146
|
}), B(e, n, r, 1);
|
|
147
147
|
},
|
|
@@ -302,6 +302,7 @@ export * from './appControls/languageReply';
|
|
|
302
302
|
export * from './appControls/text';
|
|
303
303
|
export * from './turnOutcomeCopy';
|
|
304
304
|
export * from './aiCredits';
|
|
305
|
+
export * from './productAiCredits';
|
|
305
306
|
export * from './usageReporting';
|
|
306
307
|
export * from './writeProposals';
|
|
307
308
|
export * from './writeReplyBinder';
|
|
@@ -40,6 +40,14 @@ export declare const STOP_MY_ASSISTANT_SANDBOX = "\n mutation StopMyAssistantSa
|
|
|
40
40
|
export declare const EXTEND_MY_ASSISTANT_SANDBOX_TTL = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n";
|
|
41
41
|
export declare const CHECK_AI_CREDITS = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n";
|
|
42
42
|
export declare const REPORT_AI_TOKEN_USAGE = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n";
|
|
43
|
+
/**
|
|
44
|
+
* BOFF-3732: the shared, product-keyed AI-credit gate and charge in
|
|
45
|
+
* wspace-prompts-svc. Each works on `<productKey>.ai.credits`, so a product's
|
|
46
|
+
* assistant turns are billed to its own quota, never VibeControls'.
|
|
47
|
+
*/
|
|
48
|
+
export declare const CHECK_AI_CREDITS_FOR = "\n mutation CheckAiCreditsFor($productKey: String!) {\n checkAiCreditsFor(productKey: $productKey) {\n provisioned\n exhausted\n remaining\n }\n }\n";
|
|
49
|
+
export declare const CHARGE_AI_INVOCATION = "\n mutation ChargeAiInvocation($input: PromptChargeAiInvocationInput!) {\n chargeAiInvocation(input: $input) {\n status\n reason\n credits\n }\n }\n";
|
|
50
|
+
export declare const AI_CREDIT_STATUS = "\n query AiCreditStatus($productKey: String!) {\n aiCreditStatus(productKey: $productKey) {\n productKey\n quotaName\n provisioned\n noLimit\n used\n limit\n remaining\n exhausted\n }\n }\n";
|
|
43
51
|
/**
|
|
44
52
|
* BOFF-7801: finished turns' timings for the production latency histograms
|
|
45
53
|
* (wspace-sandbox-svc). Self-scoped — the actor comes from the gateway, and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region src/shared/assistant/operations.ts
|
|
2
|
-
var e = "\n mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) {\n id\n name\n status\n network {\n internalUrl\n }\n }\n }\n", t = "\n query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) {\n id\n status\n errorMessage\n network {\n internalUrl\n }\n }\n }\n", n = "\n query WaitForSandboxRunning($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForSandboxRunning(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n id\n status\n errorMessage\n }\n }\n", r = "\n query WaitForAssistantReady($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForAssistantReady(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n ready\n health\n waited\n }\n }\n", i = "\n query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }\n", a = "\n mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) {\n status\n body\n }\n }\n", o = "\n mutation StopAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", s = "\n mutation ExtendSandboxTtl($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", c = "\n mutation ProvisionAssistantSandbox($input: ProvisionAssistantSandboxInput!) {\n provisionAssistantSandbox(input: $input) {\n id\n name\n status\n }\n }\n", l = "\n mutation StopMyAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopMyAssistantSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", u = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", d = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n", f = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n", p = "\n mutation ReportMyAssistantTurnTimings($input: ReportAssistantTurnTimingsInput!) {\n reportMyAssistantTurnTimings(input: $input) {\n enabled\n accepted\n rejected\n duplicate\n rateLimited\n }\n }\n";
|
|
2
|
+
var e = "\n mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) {\n id\n name\n status\n network {\n internalUrl\n }\n }\n }\n", t = "\n query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) {\n id\n status\n errorMessage\n network {\n internalUrl\n }\n }\n }\n", n = "\n query WaitForSandboxRunning($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForSandboxRunning(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n id\n status\n errorMessage\n }\n }\n", r = "\n query WaitForAssistantReady($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForAssistantReady(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n ready\n health\n waited\n }\n }\n", i = "\n query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }\n", a = "\n mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) {\n status\n body\n }\n }\n", o = "\n mutation StopAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", s = "\n mutation ExtendSandboxTtl($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", c = "\n mutation ProvisionAssistantSandbox($input: ProvisionAssistantSandboxInput!) {\n provisionAssistantSandbox(input: $input) {\n id\n name\n status\n }\n }\n", l = "\n mutation StopMyAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopMyAssistantSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", u = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", d = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n", f = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n", p = "\n mutation CheckAiCreditsFor($productKey: String!) {\n checkAiCreditsFor(productKey: $productKey) {\n provisioned\n exhausted\n remaining\n }\n }\n", m = "\n mutation ChargeAiInvocation($input: PromptChargeAiInvocationInput!) {\n chargeAiInvocation(input: $input) {\n status\n reason\n credits\n }\n }\n", h = "\n query AiCreditStatus($productKey: String!) {\n aiCreditStatus(productKey: $productKey) {\n productKey\n quotaName\n provisioned\n noLimit\n used\n limit\n remaining\n exhausted\n }\n }\n", g = "\n mutation ReportMyAssistantTurnTimings($input: ReportAssistantTurnTimingsInput!) {\n reportMyAssistantTurnTimings(input: $input) {\n enabled\n accepted\n rejected\n duplicate\n rateLimited\n }\n }\n";
|
|
3
3
|
//#endregion
|
|
4
|
-
export { d as CHECK_AI_CREDITS, e as CREATE_SANDBOX, u as EXTEND_MY_ASSISTANT_SANDBOX_TTL, s as EXTEND_SANDBOX_TTL, i as GET_ACTIVE_SANDBOXES, t as GET_SANDBOX, c as PROVISION_ASSISTANT_SANDBOX, a as PROXY_SANDBOX_REQUEST, f as REPORT_AI_TOKEN_USAGE,
|
|
4
|
+
export { h as AI_CREDIT_STATUS, m as CHARGE_AI_INVOCATION, d as CHECK_AI_CREDITS, p as CHECK_AI_CREDITS_FOR, e as CREATE_SANDBOX, u as EXTEND_MY_ASSISTANT_SANDBOX_TTL, s as EXTEND_SANDBOX_TTL, i as GET_ACTIVE_SANDBOXES, t as GET_SANDBOX, c as PROVISION_ASSISTANT_SANDBOX, a as PROXY_SANDBOX_REQUEST, f as REPORT_AI_TOKEN_USAGE, g as REPORT_MY_ASSISTANT_TURN_TIMINGS, o as STOP_ASSISTANT_SANDBOX, l as STOP_MY_ASSISTANT_SANDBOX, r as WAIT_FOR_ASSISTANT_READY, n as WAIT_FOR_SANDBOX_RUNNING };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { UsageReport } from './usageReporting';
|
|
2
|
+
/** How a workspace stands against `<productKey>.ai.credits`. */
|
|
3
|
+
export interface AiCreditStatus {
|
|
4
|
+
productKey: string;
|
|
5
|
+
quotaName: string;
|
|
6
|
+
/** False when the workspace has no assignment for the quota: AI is not metered there. */
|
|
7
|
+
provisioned: boolean;
|
|
8
|
+
noLimit: boolean;
|
|
9
|
+
used: number | null;
|
|
10
|
+
limit: number | null;
|
|
11
|
+
remaining: number | null;
|
|
12
|
+
exhausted: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** The operation key the shared ledger stores for an assistant turn. */
|
|
15
|
+
export declare const ASSISTANT_TURN_OPERATION = "assistant.turn";
|
|
16
|
+
/**
|
|
17
|
+
* A `reportTokenUsage` for `AssistantProductProvider` that charges the
|
|
18
|
+
* product's own AI credits. A turn without a message id or model is skipped:
|
|
19
|
+
* the server needs the first for idempotency and the second for the price.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createAiCreditReporter(productKey: string): (report: UsageReport) => Promise<void>;
|
|
22
|
+
/** Parse an `aiCreditStatus` payload; anything malformed reads as null. */
|
|
23
|
+
export declare function parseAiCreditStatus(value: unknown): AiCreditStatus | null;
|
|
24
|
+
/**
|
|
25
|
+
* The workspace's remaining AI credits for a product, for showing the balance
|
|
26
|
+
* before an AI action. Resolves null when it cannot be read; never throws.
|
|
27
|
+
*/
|
|
28
|
+
export declare function fetchAiCreditStatus(productKey: string, workspaceId?: string): Promise<AiCreditStatus | null>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { graphqlFetch as e } from "../graphql/fetch.js";
|
|
2
|
+
import { AI_CREDIT_STATUS as t, CHARGE_AI_INVOCATION as n } from "./operations.js";
|
|
3
|
+
//#region src/shared/assistant/productAiCredits.ts
|
|
4
|
+
var r = "assistant.turn";
|
|
5
|
+
function i(t) {
|
|
6
|
+
return async (i) => {
|
|
7
|
+
let { workspaceId: a, sessionId: o, messageId: s, model: c, tokens: l } = i;
|
|
8
|
+
if (!s || !c) return;
|
|
9
|
+
let u = Math.max(0, Math.round(l.input ?? 0)), d = Math.max(0, Math.round(l.output ?? 0)), f = Math.max(0, Math.round(l.cache?.read ?? 0)), p = Math.max(0, Math.round(l.cache?.write ?? 0));
|
|
10
|
+
u + d + f + p !== 0 && await e({
|
|
11
|
+
gateway: "workspace",
|
|
12
|
+
query: n,
|
|
13
|
+
operationName: "ChargeAiInvocation",
|
|
14
|
+
variables: { input: {
|
|
15
|
+
productKey: t,
|
|
16
|
+
sessionId: o,
|
|
17
|
+
messageId: s,
|
|
18
|
+
model: c,
|
|
19
|
+
inputTokens: u,
|
|
20
|
+
outputTokens: d,
|
|
21
|
+
cacheReadTokens: f,
|
|
22
|
+
cacheWriteTokens: p,
|
|
23
|
+
operationKey: r
|
|
24
|
+
} },
|
|
25
|
+
workspaceId: a,
|
|
26
|
+
suppressGlobalErrorEvent: !0
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function a(e) {
|
|
31
|
+
return typeof e == "number" && Number.isFinite(e) ? e : null;
|
|
32
|
+
}
|
|
33
|
+
function o(e) {
|
|
34
|
+
if (!e || typeof e != "object") return null;
|
|
35
|
+
let t = e.aiCreditStatus;
|
|
36
|
+
if (!t || typeof t != "object") return null;
|
|
37
|
+
let n = t;
|
|
38
|
+
return typeof n.productKey != "string" || typeof n.quotaName != "string" || typeof n.provisioned != "boolean" || typeof n.noLimit != "boolean" || typeof n.exhausted != "boolean" ? null : {
|
|
39
|
+
productKey: n.productKey,
|
|
40
|
+
quotaName: n.quotaName,
|
|
41
|
+
provisioned: n.provisioned,
|
|
42
|
+
noLimit: n.noLimit,
|
|
43
|
+
used: a(n.used),
|
|
44
|
+
limit: a(n.limit),
|
|
45
|
+
remaining: a(n.remaining),
|
|
46
|
+
exhausted: n.exhausted
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function s(n, r) {
|
|
50
|
+
try {
|
|
51
|
+
return o(await e({
|
|
52
|
+
gateway: "workspace",
|
|
53
|
+
query: t,
|
|
54
|
+
operationName: "AiCreditStatus",
|
|
55
|
+
variables: { productKey: n },
|
|
56
|
+
workspaceId: r ?? !0,
|
|
57
|
+
suppressGlobalErrorEvent: !0
|
|
58
|
+
}));
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { r as ASSISTANT_TURN_OPERATION, i as createAiCreditReporter, s as fetchAiCreditStatus, o as parseAiCreditStatus };
|
|
@@ -30,6 +30,8 @@ export type ProductAssistantMode = string & {
|
|
|
30
30
|
};
|
|
31
31
|
export type AssistantMode = BuiltInAssistantMode | ProductAssistantMode;
|
|
32
32
|
/** Local mirror of a product's PageContext; the transport forwards a subset. */
|
|
33
|
+
/** Where a product's assistant AI credits are gated and charged (see `aiCreditLedger`). */
|
|
34
|
+
export type AiCreditLedger = 'shared' | 'product-service';
|
|
33
35
|
export interface PageContext {
|
|
34
36
|
currentUrl?: string;
|
|
35
37
|
currentPage?: string;
|
|
@@ -164,6 +166,19 @@ export interface SandboxAssistantConfig {
|
|
|
164
166
|
* pauses the next send until it is (`usageReporting.ts`).
|
|
165
167
|
*/
|
|
166
168
|
enableCredits?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Which service gates and charges this product's AI credits (BOFF-3732).
|
|
171
|
+
*
|
|
172
|
+
* - `'shared'` (default): wspace-prompts-svc's product-keyed
|
|
173
|
+
* `checkAiCreditsFor` / `chargeAiInvocation`, against
|
|
174
|
+
* `<product>.ai.credits`.
|
|
175
|
+
* - `'product-service'`: the product's own service declares
|
|
176
|
+
* `checkAiCredits` / `reportAiTokenUsage` and charges its own quota. Only a
|
|
177
|
+
* product whose service really owns those mutations may set it: they are
|
|
178
|
+
* declared once in the supergraph, so any other product setting it bills
|
|
179
|
+
* that service's quota, not its own.
|
|
180
|
+
*/
|
|
181
|
+
aiCreditLedger?: AiCreditLedger;
|
|
167
182
|
/** Overall per-turn deadline. Default 420_000ms. */
|
|
168
183
|
streamBudgetMs?: number;
|
|
169
184
|
/**
|
|
@@ -4,47 +4,49 @@ import { useAuth as n } from "../providers/shell/AuthProvider.js";
|
|
|
4
4
|
import { useActiveContext as r } from "../providers/shell/ActiveContextProvider.js";
|
|
5
5
|
import { MainLandmark as i } from "../components/SkipToContent.js";
|
|
6
6
|
import { PermissionProvider as a, usePermissions as o, useSafePermissions as s } from "../providers/shell/PermissionProvider.js";
|
|
7
|
-
import {
|
|
8
|
-
import { useAiTr as
|
|
9
|
-
import { CatalogPanel as
|
|
10
|
-
import { SummaryPanel as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
7
|
+
import { BREAKDOWN as c, CREDITS as l, MODELS as u, PROVIDERS as d, SUMMARY as f, breakdown as p, catalog as m, credits as h, sevenDays as g, summary as _ } from "./ai/contracts.js";
|
|
8
|
+
import { useAiTr as v } from "./ai/i18n.js";
|
|
9
|
+
import { CatalogPanel as y } from "./ai/CatalogPanel.js";
|
|
10
|
+
import { SummaryPanel as b } from "./ai/SummaryPanel.js";
|
|
11
|
+
import { BreakdownPanel as x } from "./ai/BreakdownPanel.js";
|
|
12
|
+
import { CreditsPanel as S } from "./ai/CreditsPanel.js";
|
|
13
|
+
import { useAiRead as C } from "./ai/useAiRead.js";
|
|
14
|
+
import { useCallback as w, useMemo as T, useState as E } from "react";
|
|
15
|
+
import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
|
|
14
16
|
//#region src/shared/pages/AiPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let r =
|
|
17
|
-
return /* @__PURE__ */
|
|
17
|
+
function A({ productName: t, productId: n }) {
|
|
18
|
+
let r = v(), a = e("workspace");
|
|
19
|
+
return /* @__PURE__ */ k(i, {
|
|
18
20
|
className: "mx-auto w-full max-w-6xl space-y-6 px-4 py-6 sm:px-6",
|
|
19
21
|
"data-product-id": n,
|
|
20
|
-
children: [/* @__PURE__ */
|
|
22
|
+
children: [/* @__PURE__ */ k("header", {
|
|
21
23
|
className: "space-y-2",
|
|
22
|
-
children: [/* @__PURE__ */
|
|
24
|
+
children: [/* @__PURE__ */ O("h1", {
|
|
23
25
|
className: "text-2xl font-semibold text-foreground",
|
|
24
26
|
children: r("title", "{product} AI operations", { product: t })
|
|
25
|
-
}), /* @__PURE__ */
|
|
27
|
+
}), /* @__PURE__ */ O("p", {
|
|
26
28
|
className: "max-w-3xl text-sm text-muted-foreground",
|
|
27
29
|
children: r("intro", "Review the workspace model catalog and recorded AI usage.")
|
|
28
30
|
})]
|
|
29
|
-
}), a ? /* @__PURE__ */
|
|
31
|
+
}), a ? /* @__PURE__ */ O(j, { children: /* @__PURE__ */ O(M, {
|
|
30
32
|
productId: n,
|
|
31
33
|
client: a
|
|
32
|
-
}) }) : /* @__PURE__ */
|
|
34
|
+
}) }) : /* @__PURE__ */ O("p", {
|
|
33
35
|
role: "status",
|
|
34
36
|
children: r("unavailable", "AI operations are unavailable in this context.")
|
|
35
37
|
})]
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
|
-
function
|
|
40
|
+
function j({ children: e }) {
|
|
39
41
|
let t = s(), { workspaceId: n } = r();
|
|
40
|
-
return t ? e : /* @__PURE__ */
|
|
42
|
+
return t ? e : /* @__PURE__ */ O(a, {
|
|
41
43
|
scopeId: n ?? null,
|
|
42
44
|
gateway: "workspace",
|
|
43
45
|
children: e
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
|
-
function
|
|
47
|
-
let a = n(), { workspaceId: s } = r(),
|
|
48
|
+
function M({ productId: e, client: i }) {
|
|
49
|
+
let a = n(), { workspaceId: s } = r(), A = o(), { locale: j } = t(), M = v(), N = T(() => ({
|
|
48
50
|
workspaceId: s ?? "",
|
|
49
51
|
actorId: a.user?.id ?? "",
|
|
50
52
|
profileId: a.activeProfileId,
|
|
@@ -56,78 +58,102 @@ function E({ productId: e, client: i }) {
|
|
|
56
58
|
a.activeProfileId,
|
|
57
59
|
a.tokens?.accessToken,
|
|
58
60
|
a.workspaceToken
|
|
59
|
-
]), [
|
|
60
|
-
scope:
|
|
61
|
+
]), [P, F] = E(g), [I, L] = E({
|
|
62
|
+
scope: N,
|
|
61
63
|
models: 0,
|
|
62
64
|
providers: 0,
|
|
63
65
|
revision: 0
|
|
64
|
-
}),
|
|
66
|
+
}), R = I.scope === N ? I.models : 0, z = I.scope === N ? I.providers : 0, B = I.scope === N ? I.revision : 0, V = (e) => a.isLoading ? "loading" : !a.isAuthenticated || !N.workspaceId || !N.actorId || !N.accessToken ? "unavailable" : A.isLoading || A.scopeId !== N.workspaceId ? "loading" : A.error ? "unavailable" : !A.isSuperAdmin && A.permissions?.actorId !== N.actorId ? "loading" : A.hasPermission(e) ? "ready" : "denied", H = T(() => ({ pagination: {
|
|
65
67
|
limit: 20,
|
|
66
|
-
offset:
|
|
67
|
-
} }), [
|
|
68
|
+
offset: R
|
|
69
|
+
} }), [R]), U = T(() => ({ pagination: {
|
|
68
70
|
limit: 20,
|
|
69
|
-
offset:
|
|
70
|
-
} }), [
|
|
71
|
-
...
|
|
71
|
+
offset: z
|
|
72
|
+
} }), [z]), W = T(() => ({
|
|
73
|
+
...P,
|
|
72
74
|
productKey: e
|
|
73
|
-
}), [
|
|
74
|
-
|
|
75
|
+
}), [P, e]), G = w((e) => m(e, "models"), []), K = w((e) => m(e, "providers"), []), ee = w((t) => _(t, N.workspaceId, e, P), [
|
|
76
|
+
N.workspaceId,
|
|
75
77
|
e,
|
|
76
|
-
|
|
77
|
-
]),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
P
|
|
79
|
+
]), q = C(i, N, V("prompt-model:read"), u, H, G, B), J = C(i, N, V("prompt-provider:read"), d, U, K, B), Y = C(i, N, V("prompt-invocation:read"), f, W, ee, B), X = T(() => ({
|
|
80
|
+
...P,
|
|
81
|
+
productKey: e,
|
|
82
|
+
groupBy: ["MODEL"]
|
|
83
|
+
}), [P, e]), Z = T(() => ({
|
|
84
|
+
...P,
|
|
85
|
+
productKey: e,
|
|
86
|
+
groupBy: ["OPERATION"]
|
|
87
|
+
}), [P, e]), Q = T(() => ({
|
|
88
|
+
...P,
|
|
89
|
+
productKey: e,
|
|
90
|
+
groupBy: ["TRIGGER"]
|
|
91
|
+
}), [P, e]), $ = w((t) => p(t, N.workspaceId, e, "MODEL"), [N.workspaceId, e]), te = w((t) => p(t, N.workspaceId, e, "OPERATION"), [N.workspaceId, e]), ne = w((t) => p(t, N.workspaceId, e, "TRIGGER"), [N.workspaceId, e]), re = T(() => ({ productKey: e }), [e]), ie = w((t) => h(t, e), [e]), ae = C(i, N, V("prompt-invocation:read"), c, X, $, B), oe = C(i, N, V("prompt-invocation:read"), c, Z, te, B), se = C(i, N, V("prompt-invocation:read"), c, Q, ne, B), ce = C(i, N, V("assistant:use"), l, re, ie, B);
|
|
92
|
+
function le() {
|
|
93
|
+
F(g()), L({
|
|
94
|
+
scope: N,
|
|
95
|
+
models: R,
|
|
96
|
+
providers: z,
|
|
97
|
+
revision: B + 1
|
|
84
98
|
});
|
|
85
99
|
}
|
|
86
|
-
return /* @__PURE__ */
|
|
87
|
-
/* @__PURE__ */
|
|
100
|
+
return /* @__PURE__ */ k(D, { children: [
|
|
101
|
+
/* @__PURE__ */ k("div", {
|
|
88
102
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
103
|
+
children: [/* @__PURE__ */ O("p", {
|
|
90
104
|
className: "text-sm text-muted-foreground",
|
|
91
|
-
children:
|
|
92
|
-
}), /* @__PURE__ */
|
|
105
|
+
children: M("readonly", "Read-only workspace view")
|
|
106
|
+
}), /* @__PURE__ */ O("button", {
|
|
93
107
|
type: "button",
|
|
94
|
-
onClick:
|
|
108
|
+
onClick: le,
|
|
95
109
|
className: "rounded-md border border-border px-3 py-2 text-sm font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
96
|
-
children:
|
|
110
|
+
children: M("refresh", "Refresh")
|
|
97
111
|
})]
|
|
98
112
|
}),
|
|
99
|
-
/* @__PURE__ */ S
|
|
100
|
-
state:
|
|
101
|
-
|
|
102
|
-
locale: T
|
|
113
|
+
/* @__PURE__ */ O(S, {
|
|
114
|
+
state: ce,
|
|
115
|
+
locale: j
|
|
103
116
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ O(b, {
|
|
118
|
+
state: Y,
|
|
119
|
+
window: P,
|
|
120
|
+
locale: j
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ O(x, {
|
|
123
|
+
states: {
|
|
124
|
+
MODEL: ae,
|
|
125
|
+
OPERATION: oe,
|
|
126
|
+
TRIGGER: se
|
|
127
|
+
},
|
|
128
|
+
locale: j
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ k("div", {
|
|
105
131
|
className: "grid gap-6 lg:grid-cols-2",
|
|
106
|
-
children: [/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ O(y, {
|
|
107
133
|
kind: "models",
|
|
108
|
-
state:
|
|
109
|
-
offset:
|
|
110
|
-
locale:
|
|
111
|
-
onPage: (e) =>
|
|
112
|
-
scope:
|
|
134
|
+
state: q,
|
|
135
|
+
offset: R,
|
|
136
|
+
locale: j,
|
|
137
|
+
onPage: (e) => L({
|
|
138
|
+
scope: N,
|
|
113
139
|
models: e,
|
|
114
|
-
providers:
|
|
115
|
-
revision:
|
|
140
|
+
providers: z,
|
|
141
|
+
revision: B
|
|
116
142
|
})
|
|
117
|
-
}), /* @__PURE__ */
|
|
143
|
+
}), /* @__PURE__ */ O(y, {
|
|
118
144
|
kind: "providers",
|
|
119
|
-
state:
|
|
120
|
-
offset:
|
|
121
|
-
locale:
|
|
122
|
-
onPage: (e) =>
|
|
123
|
-
scope:
|
|
124
|
-
models:
|
|
145
|
+
state: J,
|
|
146
|
+
offset: z,
|
|
147
|
+
locale: j,
|
|
148
|
+
onPage: (e) => L({
|
|
149
|
+
scope: N,
|
|
150
|
+
models: R,
|
|
125
151
|
providers: e,
|
|
126
|
-
revision:
|
|
152
|
+
revision: B
|
|
127
153
|
})
|
|
128
154
|
})]
|
|
129
155
|
})
|
|
130
156
|
] });
|
|
131
157
|
}
|
|
132
158
|
//#endregion
|
|
133
|
-
export {
|
|
159
|
+
export { A as AiPage };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Breakdown, BreakdownDimension } from './contracts';
|
|
2
|
+
import { ReadState } from './useAiRead';
|
|
3
|
+
export declare function BreakdownPanel({ states, locale, }: {
|
|
4
|
+
states: Record<BreakdownDimension, ReadState<Breakdown>>;
|
|
5
|
+
locale: string;
|
|
6
|
+
}): import('react/jsx-runtime').JSX.Element;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { exactInteger as e, exactUsd as t } from "./contracts.js";
|
|
2
|
+
import { useAiTr as n } from "./i18n.js";
|
|
3
|
+
import { ReadNotice as r } from "./ReadNotice.js";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
//#region src/shared/pages/ai/BreakdownPanel.tsx
|
|
6
|
+
function o({ title: o, state: s, locale: c }) {
|
|
7
|
+
let l = n();
|
|
8
|
+
return /* @__PURE__ */ a("div", {
|
|
9
|
+
className: "min-w-0",
|
|
10
|
+
children: [/* @__PURE__ */ i("h3", {
|
|
11
|
+
className: "text-sm font-semibold",
|
|
12
|
+
children: o
|
|
13
|
+
}), /* @__PURE__ */ i("div", {
|
|
14
|
+
className: "mt-2",
|
|
15
|
+
"aria-live": "polite",
|
|
16
|
+
children: s.status === "ready" ? s.data.rows.length === 0 ? /* @__PURE__ */ i("p", {
|
|
17
|
+
className: "text-sm text-muted-foreground",
|
|
18
|
+
children: l("breakdownEmpty", "No recorded attempts in this window.")
|
|
19
|
+
}) : /* @__PURE__ */ a("div", {
|
|
20
|
+
className: "overflow-x-auto",
|
|
21
|
+
children: [/* @__PURE__ */ a("table", {
|
|
22
|
+
className: "w-full min-w-0 text-left text-sm",
|
|
23
|
+
children: [/* @__PURE__ */ i("thead", {
|
|
24
|
+
className: "text-xs text-muted-foreground",
|
|
25
|
+
children: /* @__PURE__ */ a("tr", { children: [
|
|
26
|
+
/* @__PURE__ */ i("th", {
|
|
27
|
+
scope: "col",
|
|
28
|
+
className: "py-1 pr-3 font-medium",
|
|
29
|
+
children: o
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ i("th", {
|
|
32
|
+
scope: "col",
|
|
33
|
+
className: "py-1 pr-3 text-right font-medium",
|
|
34
|
+
children: l("breakdownAttempts", "Attempts")
|
|
35
|
+
}),
|
|
36
|
+
/* @__PURE__ */ i("th", {
|
|
37
|
+
scope: "col",
|
|
38
|
+
className: "py-1 pr-3 text-right font-medium",
|
|
39
|
+
children: l("breakdownTokens", "Tokens")
|
|
40
|
+
}),
|
|
41
|
+
/* @__PURE__ */ i("th", {
|
|
42
|
+
scope: "col",
|
|
43
|
+
className: "py-1 text-right font-medium",
|
|
44
|
+
children: l("breakdownCost", "Est. cost")
|
|
45
|
+
})
|
|
46
|
+
] })
|
|
47
|
+
}), /* @__PURE__ */ i("tbody", { children: s.data.rows.map((n, r) => /* @__PURE__ */ a("tr", {
|
|
48
|
+
className: "border-t border-border",
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ i("td", {
|
|
51
|
+
className: "max-w-[16rem] break-all py-1 pr-3",
|
|
52
|
+
children: n.key ?? /* @__PURE__ */ i("span", {
|
|
53
|
+
className: "text-muted-foreground",
|
|
54
|
+
children: l("breakdownUnrecorded", "Not recorded")
|
|
55
|
+
})
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ i("td", {
|
|
58
|
+
className: "py-1 pr-3 text-right tabular-nums",
|
|
59
|
+
children: e(n.attempts, c)
|
|
60
|
+
}),
|
|
61
|
+
/* @__PURE__ */ i("td", {
|
|
62
|
+
className: "py-1 pr-3 text-right tabular-nums",
|
|
63
|
+
children: n.totalTokens === null ? l("unknown", "Unknown") : e(n.totalTokens, c)
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ i("td", {
|
|
66
|
+
className: "py-1 text-right tabular-nums",
|
|
67
|
+
children: n.costMicroUsd === null ? l("unknown", "Unknown") : t(n.costMicroUsd, c)
|
|
68
|
+
})
|
|
69
|
+
]
|
|
70
|
+
}, `${n.key ?? ""}-${r}`)) })]
|
|
71
|
+
}), s.data.more && /* @__PURE__ */ i("p", {
|
|
72
|
+
className: "mt-1 text-xs text-muted-foreground",
|
|
73
|
+
children: l("breakdownMore", "Showing the highest-cost groups only.")
|
|
74
|
+
})]
|
|
75
|
+
}) : /* @__PURE__ */ i(r, { status: s.status })
|
|
76
|
+
})]
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function s({ states: e, locale: t }) {
|
|
80
|
+
let r = n(), s = r("breakdown", "Where the spend went");
|
|
81
|
+
return /* @__PURE__ */ a("section", {
|
|
82
|
+
"aria-label": s,
|
|
83
|
+
className: "min-w-0 rounded-xl border border-border bg-card p-4 sm:p-5",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ i("h2", {
|
|
86
|
+
className: "text-lg font-semibold",
|
|
87
|
+
children: s
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ i("p", {
|
|
90
|
+
className: "mt-2 text-sm text-muted-foreground",
|
|
91
|
+
children: r("breakdownNotice", "Recorded attempts in the same window, grouped by model, AI surface and what started them. Costs are estimates.")
|
|
92
|
+
}),
|
|
93
|
+
/* @__PURE__ */ a("div", {
|
|
94
|
+
className: "mt-4 grid gap-6 xl:grid-cols-3",
|
|
95
|
+
children: [
|
|
96
|
+
/* @__PURE__ */ i(o, {
|
|
97
|
+
title: r("byModel", "Model"),
|
|
98
|
+
state: e.MODEL,
|
|
99
|
+
locale: t
|
|
100
|
+
}),
|
|
101
|
+
/* @__PURE__ */ i(o, {
|
|
102
|
+
title: r("bySurface", "AI surface"),
|
|
103
|
+
state: e.OPERATION,
|
|
104
|
+
locale: t
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ i(o, {
|
|
107
|
+
title: r("byTrigger", "Trigger"),
|
|
108
|
+
state: e.TRIGGER,
|
|
109
|
+
locale: t
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { s as BreakdownPanel };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CreditStatus } from './contracts';
|
|
2
|
+
import { ReadState } from './useAiRead';
|
|
3
|
+
/**
|
|
4
|
+
* The workspace's remaining AI credits for this product (BOFF-3732), so the
|
|
5
|
+
* balance is visible before an AI action rather than after a refusal.
|
|
6
|
+
*/
|
|
7
|
+
export declare function CreditsPanel({ state, locale, }: {
|
|
8
|
+
state: ReadState<CreditStatus>;
|
|
9
|
+
locale: string;
|
|
10
|
+
}): import('react/jsx-runtime').JSX.Element;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useAiTr as e } from "./i18n.js";
|
|
2
|
+
import { ReadNotice as t } from "./ReadNotice.js";
|
|
3
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
|
+
//#region src/shared/pages/ai/CreditsPanel.tsx
|
|
5
|
+
function i({ state: i, locale: a }) {
|
|
6
|
+
let o = e(), s = o("credits", "AI credits this cycle"), c = new Intl.NumberFormat(a, { maximumFractionDigits: 0 });
|
|
7
|
+
return /* @__PURE__ */ r("section", {
|
|
8
|
+
"aria-label": s,
|
|
9
|
+
className: "min-w-0 rounded-xl border border-border bg-card p-4 sm:p-5",
|
|
10
|
+
children: [/* @__PURE__ */ n("h2", {
|
|
11
|
+
className: "text-lg font-semibold",
|
|
12
|
+
children: s
|
|
13
|
+
}), /* @__PURE__ */ r("div", {
|
|
14
|
+
className: "mt-3",
|
|
15
|
+
"aria-live": "polite",
|
|
16
|
+
children: [i.status === "ready" ? i.data.provisioned ? i.data.noLimit ? /* @__PURE__ */ n("p", {
|
|
17
|
+
className: "text-sm",
|
|
18
|
+
children: o("creditsUnlimited", "This plan has no AI credit limit.")
|
|
19
|
+
}) : /* @__PURE__ */ r("dl", {
|
|
20
|
+
className: "grid grid-cols-3 gap-3",
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ r("div", {
|
|
23
|
+
className: "min-w-0",
|
|
24
|
+
children: [/* @__PURE__ */ n("dt", {
|
|
25
|
+
className: "text-xs text-muted-foreground",
|
|
26
|
+
children: o("creditsRemaining", "Remaining")
|
|
27
|
+
}), /* @__PURE__ */ n("dd", {
|
|
28
|
+
className: `break-all text-xl font-semibold tabular-nums ${i.data.exhausted ? "text-destructive" : ""}`,
|
|
29
|
+
children: c.format(i.data.remaining ?? 0)
|
|
30
|
+
})]
|
|
31
|
+
}),
|
|
32
|
+
/* @__PURE__ */ r("div", {
|
|
33
|
+
className: "min-w-0",
|
|
34
|
+
children: [/* @__PURE__ */ n("dt", {
|
|
35
|
+
className: "text-xs text-muted-foreground",
|
|
36
|
+
children: o("creditsUsed", "Used")
|
|
37
|
+
}), /* @__PURE__ */ n("dd", {
|
|
38
|
+
className: "break-all text-xl font-semibold tabular-nums",
|
|
39
|
+
children: c.format(i.data.used ?? 0)
|
|
40
|
+
})]
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ r("div", {
|
|
43
|
+
className: "min-w-0",
|
|
44
|
+
children: [/* @__PURE__ */ n("dt", {
|
|
45
|
+
className: "text-xs text-muted-foreground",
|
|
46
|
+
children: o("creditsLimit", "Limit")
|
|
47
|
+
}), /* @__PURE__ */ n("dd", {
|
|
48
|
+
className: "break-all text-xl font-semibold tabular-nums",
|
|
49
|
+
children: c.format(i.data.limit ?? 0)
|
|
50
|
+
})]
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
}) : /* @__PURE__ */ n("p", {
|
|
54
|
+
className: "text-sm text-muted-foreground",
|
|
55
|
+
children: o("creditsNotMetered", "AI use is not metered against a credit budget in this workspace.")
|
|
56
|
+
}) : /* @__PURE__ */ n(t, { status: i.status }), i.status === "ready" && i.data.exhausted && /* @__PURE__ */ n("p", {
|
|
57
|
+
className: "mt-3 text-sm text-destructive",
|
|
58
|
+
children: o("creditsExhausted", "AI credits are used up. AI actions resume next cycle or after an upgrade.")
|
|
59
|
+
})]
|
|
60
|
+
})]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { i as CreditsPanel };
|
|
@@ -43,3 +43,38 @@ export declare function summary(value: unknown, workspaceId: string, productKey:
|
|
|
43
43
|
export declare function exactInteger(value: string, locale: string): string;
|
|
44
44
|
export declare function exactUsd(value: string, locale: string): string;
|
|
45
45
|
export declare function sevenDays(now?: Date): UsageWindow;
|
|
46
|
+
/**
|
|
47
|
+
* BOFF-3732: spend grouped by one dimension (BOFF-9935's promptUsageBreakdownV2),
|
|
48
|
+
* so an admin can tell a runaway workflow from the team asking the assistant.
|
|
49
|
+
*/
|
|
50
|
+
export declare const BREAKDOWN: import('graphql').DocumentNode;
|
|
51
|
+
/** BOFF-3732: the workspace's remaining `<productKey>.ai.credits`. */
|
|
52
|
+
export declare const CREDITS: import('graphql').DocumentNode;
|
|
53
|
+
export type BreakdownDimension = 'MODEL' | 'OPERATION' | 'TRIGGER';
|
|
54
|
+
export declare const BREAKDOWN_DIMENSIONS: readonly BreakdownDimension[];
|
|
55
|
+
/** Rows shown per breakdown; the service orders by known cost, highest first. */
|
|
56
|
+
export declare const BREAKDOWN_ROWS = 10;
|
|
57
|
+
export interface BreakdownRow {
|
|
58
|
+
/** The group's value; null when the ledger rows did not record it (older rows have no trigger). */
|
|
59
|
+
key: string | null;
|
|
60
|
+
attempts: string;
|
|
61
|
+
totalTokens: string | null;
|
|
62
|
+
costMicroUsd: string | null;
|
|
63
|
+
costKnownAttempts: string;
|
|
64
|
+
}
|
|
65
|
+
export interface Breakdown {
|
|
66
|
+
dimension: BreakdownDimension;
|
|
67
|
+
rows: BreakdownRow[];
|
|
68
|
+
/** More groups exist than are shown. */
|
|
69
|
+
more: boolean;
|
|
70
|
+
}
|
|
71
|
+
export declare function breakdown(value: unknown, workspaceId: string, productKey: string, dimension: BreakdownDimension): Breakdown;
|
|
72
|
+
export interface CreditStatus {
|
|
73
|
+
provisioned: boolean;
|
|
74
|
+
noLimit: boolean;
|
|
75
|
+
exhausted: boolean;
|
|
76
|
+
used: number | null;
|
|
77
|
+
limit: number | null;
|
|
78
|
+
remaining: number | null;
|
|
79
|
+
}
|
|
80
|
+
export declare function credits(value: unknown, productKey: string): CreditStatus;
|