@opengeni/react 3.3.2-canary.0 → 3.3.2-canary.1
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/{chunk-2DZVYHVO.js → chunk-7KBJLTKS.js} +33 -9
- package/dist/chunk-7KBJLTKS.js.map +1 -0
- package/dist/{chunk-3AGBGTRT.js → chunk-E5KISKFG.js} +16 -3
- package/dist/chunk-E5KISKFG.js.map +1 -0
- package/dist/{chunk-X4A66EWZ.js → chunk-X5S426G2.js} +38 -44
- package/dist/chunk-X5S426G2.js.map +1 -0
- package/dist/{chunk-XYRDDSXF.js → chunk-ZKNTK7IA.js} +10 -6
- package/dist/{chunk-XYRDDSXF.js.map → chunk-ZKNTK7IA.js.map} +1 -1
- package/dist/composer.js +3 -3
- package/dist/hooks/use-file-attachments.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/model-policy.d.ts +2 -1
- package/dist/model-policy.js +1 -1
- package/dist/realtime.js +2 -2
- package/dist/session.js +1 -1
- package/package.json +2 -2
- package/src/components/composer.tsx +15 -5
- package/src/components/model-policy-picker.tsx +35 -10
- package/src/hooks/use-file-attachments.ts +28 -2
- package/src/model-policy.ts +45 -54
- package/styles/compiled.css +5 -0
- package/dist/chunk-2DZVYHVO.js.map +0 -1
- package/dist/chunk-3AGBGTRT.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js.map +0 -1
package/dist/composer.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
useChatComposer,
|
|
38
38
|
useChatComposerController,
|
|
39
39
|
useVoiceInput
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-ZKNTK7IA.js";
|
|
41
41
|
import "./chunk-IOLVN74D.js";
|
|
42
42
|
import {
|
|
43
43
|
BillingClassMark,
|
|
@@ -47,8 +47,8 @@ import {
|
|
|
47
47
|
PickerBackHeader,
|
|
48
48
|
PickerNavRow,
|
|
49
49
|
defaultModelPolicyPickerMessages
|
|
50
|
-
} from "./chunk-
|
|
51
|
-
import "./chunk-
|
|
50
|
+
} from "./chunk-7KBJLTKS.js";
|
|
51
|
+
import "./chunk-X5S426G2.js";
|
|
52
52
|
import "./chunk-7EAFGICR.js";
|
|
53
53
|
import "./chunk-VZTQ73XT.js";
|
|
54
54
|
import "./chunk-KZ73RL76.js";
|
|
@@ -20,6 +20,8 @@ export type FileAttachment = {
|
|
|
20
20
|
file?: FileAsset | undefined;
|
|
21
21
|
/** Object-URL for an inline preview; minted for `image/*` files only. */
|
|
22
22
|
previewUrl?: string | undefined;
|
|
23
|
+
/** Stable SDK failure code for UI behavior that must not parse error copy. */
|
|
24
|
+
errorCode?: "secure_context_required" | undefined;
|
|
23
25
|
error?: string | undefined;
|
|
24
26
|
};
|
|
25
27
|
export type UseFileAttachmentsResult = {
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
useSessionLineage,
|
|
24
24
|
useSessionMcpApprovalPolicy,
|
|
25
25
|
useTurnQueue
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-E5KISKFG.js";
|
|
27
27
|
import {
|
|
28
28
|
CONNECTION_STATUS_META,
|
|
29
29
|
ConnectionDot,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
useSlashCommands,
|
|
109
109
|
useTranscription,
|
|
110
110
|
useVoiceInput
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-ZKNTK7IA.js";
|
|
112
112
|
import {
|
|
113
113
|
FILE_ONLY_MESSAGE_TEXT,
|
|
114
114
|
composeSendInput,
|
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
PickerBackHeader,
|
|
125
125
|
PickerNavRow,
|
|
126
126
|
defaultModelPolicyPickerMessages
|
|
127
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-7KBJLTKS.js";
|
|
128
128
|
import {
|
|
129
129
|
advancedSourceSummary,
|
|
130
130
|
availabilityReasonLabel,
|
|
@@ -142,7 +142,7 @@ import {
|
|
|
142
142
|
projectPickerRows,
|
|
143
143
|
runnableLatencyModesForModel,
|
|
144
144
|
sortPickerRows
|
|
145
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-X5S426G2.js";
|
|
146
146
|
import {
|
|
147
147
|
ActivityDisclosure,
|
|
148
148
|
ActivityRail,
|
package/dist/model-policy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type ModelPickerBillingClass } from "@opengeni/sdk/model-picker-order";
|
|
1
2
|
import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from "@opengeni/sdk";
|
|
2
|
-
export type PickerBillingClass =
|
|
3
|
+
export type PickerBillingClass = ModelPickerBillingClass;
|
|
3
4
|
export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
|
|
4
5
|
id: string;
|
|
5
6
|
label: string;
|
package/dist/model-policy.js
CHANGED
package/dist/realtime.js
CHANGED
package/dist/session.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "3.3.2-canary.
|
|
3
|
+
"version": "3.3.2-canary.1",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"@dnd-kit/utilities": "3.2.2",
|
|
121
121
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
122
122
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
123
|
-
"@opengeni/sdk": "^3.3.2-canary.
|
|
123
|
+
"@opengeni/sdk": "^3.3.2-canary.1",
|
|
124
124
|
"clsx": "^2.1.1",
|
|
125
125
|
"dequal": "2.0.3",
|
|
126
126
|
"lucide-react": "^1.8.0",
|
|
@@ -1650,6 +1650,7 @@ function AttachmentChips({
|
|
|
1650
1650
|
<div className="flex flex-wrap gap-2 px-3 py-2">
|
|
1651
1651
|
{attachments.map((attachment) => {
|
|
1652
1652
|
const failed = attachment.status === "failed";
|
|
1653
|
+
const secureContextRequired = attachment.errorCode === "secure_context_required";
|
|
1653
1654
|
const statusText =
|
|
1654
1655
|
attachment.status === "uploading"
|
|
1655
1656
|
? messages.uploading
|
|
@@ -1660,10 +1661,15 @@ function AttachmentChips({
|
|
|
1660
1661
|
<div
|
|
1661
1662
|
key={attachment.id}
|
|
1662
1663
|
className={cn(
|
|
1663
|
-
"flex min-w-0
|
|
1664
|
+
"flex min-w-0 items-center gap-2 rounded-og-md border px-2 py-1.5 text-og-sm",
|
|
1664
1665
|
failed
|
|
1665
|
-
?
|
|
1666
|
-
|
|
1666
|
+
? cn(
|
|
1667
|
+
"border-og-status-failed/40 bg-og-status-failed/10",
|
|
1668
|
+
secureContextRequired
|
|
1669
|
+
? "max-w-full items-start sm:max-w-[32rem]"
|
|
1670
|
+
: "max-w-[240px]",
|
|
1671
|
+
)
|
|
1672
|
+
: "max-w-[240px] border-og-border bg-og-surface-2",
|
|
1667
1673
|
)}
|
|
1668
1674
|
>
|
|
1669
1675
|
{attachment.previewUrl && lightbox ? (
|
|
@@ -1706,7 +1712,11 @@ function AttachmentChips({
|
|
|
1706
1712
|
)}
|
|
1707
1713
|
<div className="min-w-0 flex-1">
|
|
1708
1714
|
<div className="truncate font-medium text-og-fg">{attachment.name}</div>
|
|
1709
|
-
{
|
|
1715
|
+
{secureContextRequired ? (
|
|
1716
|
+
<div className="break-words text-og-xs leading-4 text-og-status-failed">
|
|
1717
|
+
{statusText}
|
|
1718
|
+
</div>
|
|
1719
|
+
) : failed ? (
|
|
1710
1720
|
<ComposerTip tip={statusText}>
|
|
1711
1721
|
<div className="truncate text-og-xs text-og-status-failed">{statusText}</div>
|
|
1712
1722
|
</ComposerTip>
|
|
@@ -1717,7 +1727,7 @@ function AttachmentChips({
|
|
|
1717
1727
|
{attachment.status === "uploading" ? (
|
|
1718
1728
|
<LoaderCircleIcon className="size-3.5 shrink-0 animate-og-spin" />
|
|
1719
1729
|
) : null}
|
|
1720
|
-
{failed && onRetry ? (
|
|
1730
|
+
{failed && !secureContextRequired && onRetry ? (
|
|
1721
1731
|
<ComposerTip tip={messages.retryUpload}>
|
|
1722
1732
|
<button
|
|
1723
1733
|
type="button"
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
findPickerRow,
|
|
27
27
|
groupPickerRowsByBillingClass,
|
|
28
28
|
labelReasoningEffort,
|
|
29
|
+
payerSummaryForModel,
|
|
29
30
|
projectClientModelRows,
|
|
30
31
|
runnableLatencyModesForModel,
|
|
31
32
|
type PickerBillingClass,
|
|
@@ -68,7 +69,7 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
|
|
|
68
69
|
external: "Provider terms and limits apply",
|
|
69
70
|
codex_subscription: "ChatGPT / Codex plan",
|
|
70
71
|
supergrok_subscription: "SuperGrok / xAI plan",
|
|
71
|
-
byok: "Billed to
|
|
72
|
+
byok: "Billed to the workspace provider account",
|
|
72
73
|
},
|
|
73
74
|
};
|
|
74
75
|
|
|
@@ -158,7 +159,7 @@ export function BillingClassMark(props: {
|
|
|
158
159
|
external: "External provider",
|
|
159
160
|
codex_subscription: "Codex",
|
|
160
161
|
supergrok_subscription: "SuperGrok",
|
|
161
|
-
byok: "
|
|
162
|
+
byok: "Workspace provider account",
|
|
162
163
|
};
|
|
163
164
|
const label = props["aria-label"] ?? labels[props.billingClass];
|
|
164
165
|
const accessibility =
|
|
@@ -195,6 +196,18 @@ function isCodexModel(model: ClientModel): boolean {
|
|
|
195
196
|
return model.id.startsWith("codex/") || model.source === "codex";
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
function billingClassForMissingSelection(modelId: string): PickerBillingClass {
|
|
200
|
+
if (modelId.startsWith("workspace-gateway/")) return "byok";
|
|
201
|
+
if (modelId.startsWith("workspace-openrouter/")) return "byok";
|
|
202
|
+
// A deployment OpenRouter ID does not encode its workspace-facing cost.
|
|
203
|
+
// Missing rows therefore use the credits-safe rail instead of falsely
|
|
204
|
+
// claiming that an unknown former selection was externally funded.
|
|
205
|
+
if (modelId.startsWith("openrouter/")) return "opengeni_credits";
|
|
206
|
+
if (modelId.startsWith("codex/")) return "codex_subscription";
|
|
207
|
+
if (modelId.startsWith("supergrok/")) return "supergrok_subscription";
|
|
208
|
+
return "opengeni_credits";
|
|
209
|
+
}
|
|
210
|
+
|
|
198
211
|
function applyCodexOnly(
|
|
199
212
|
rows: ClientPickerModelRow[],
|
|
200
213
|
codexOnly: boolean,
|
|
@@ -227,7 +240,11 @@ function defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerN
|
|
|
227
240
|
? { level: "models", rail: onlyRail, modelId: null }
|
|
228
241
|
: { level: "providers", rail: null, modelId: null };
|
|
229
242
|
}
|
|
230
|
-
return {
|
|
243
|
+
return {
|
|
244
|
+
level: "thinking",
|
|
245
|
+
rail: selected.billingClass,
|
|
246
|
+
modelId: selected.id,
|
|
247
|
+
};
|
|
231
248
|
}
|
|
232
249
|
|
|
233
250
|
function usePickerNavState(
|
|
@@ -352,7 +369,11 @@ export function ModelPolicyPickerMenu(
|
|
|
352
369
|
|
|
353
370
|
const effectiveNav =
|
|
354
371
|
nav.level === "providers" && groups.length === 1
|
|
355
|
-
? {
|
|
372
|
+
? {
|
|
373
|
+
level: "models" as const,
|
|
374
|
+
rail: groups[0]!.billingClass,
|
|
375
|
+
modelId: null,
|
|
376
|
+
}
|
|
356
377
|
: nav;
|
|
357
378
|
const activeGroup =
|
|
358
379
|
effectiveNav.rail === null
|
|
@@ -416,14 +437,21 @@ export function ModelPolicyPickerMenu(
|
|
|
416
437
|
<PickerNavRow
|
|
417
438
|
key={`${row.billingClass}:${row.id}`}
|
|
418
439
|
label={row.label}
|
|
419
|
-
hint={row.unavailableReason ??
|
|
440
|
+
hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}
|
|
420
441
|
disabled={!row.selectable}
|
|
421
442
|
title={row.unavailableReason ?? undefined}
|
|
422
443
|
active={row.id === props.model}
|
|
423
444
|
testId={`model-picker-choice-${row.id}`}
|
|
424
445
|
onClick={() => {
|
|
425
446
|
if (row.selectable) {
|
|
426
|
-
go(
|
|
447
|
+
go(
|
|
448
|
+
{
|
|
449
|
+
level: "thinking",
|
|
450
|
+
rail: row.billingClass,
|
|
451
|
+
modelId: row.id,
|
|
452
|
+
},
|
|
453
|
+
1,
|
|
454
|
+
);
|
|
427
455
|
}
|
|
428
456
|
}}
|
|
429
457
|
/>
|
|
@@ -605,10 +633,7 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
|
|
|
605
633
|
)}
|
|
606
634
|
>
|
|
607
635
|
<BillingClassMark
|
|
608
|
-
billingClass={
|
|
609
|
-
selected?.billingClass ??
|
|
610
|
-
(props.model.startsWith("codex/") ? "codex_subscription" : "opengeni_credits")
|
|
611
|
-
}
|
|
636
|
+
billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}
|
|
612
637
|
className="text-og-fg"
|
|
613
638
|
/>
|
|
614
639
|
<span className="og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden">
|
|
@@ -26,6 +26,8 @@ export type FileAttachment = {
|
|
|
26
26
|
file?: FileAsset | undefined;
|
|
27
27
|
/** Object-URL for an inline preview; minted for `image/*` files only. */
|
|
28
28
|
previewUrl?: string | undefined;
|
|
29
|
+
/** Stable SDK failure code for UI behavior that must not parse error copy. */
|
|
30
|
+
errorCode?: "secure_context_required" | undefined;
|
|
29
31
|
error?: string | undefined;
|
|
30
32
|
};
|
|
31
33
|
|
|
@@ -76,6 +78,27 @@ export type UseFileAttachmentsResult = {
|
|
|
76
78
|
|
|
77
79
|
const isImage = (file: File): boolean => file.type.startsWith("image/");
|
|
78
80
|
|
|
81
|
+
let fallbackAttachmentId = 0;
|
|
82
|
+
|
|
83
|
+
function createAttachmentId(): string {
|
|
84
|
+
const cryptoSource = globalThis.crypto;
|
|
85
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
86
|
+
fallbackAttachmentId += 1;
|
|
87
|
+
// This id is only a browser-local React key and retry lookup, never durable
|
|
88
|
+
// authority. Keep attachment tracking usable when HTTP withholds randomUUID
|
|
89
|
+
// or Web Crypto is unavailable so the SDK's typed failure reaches the card.
|
|
90
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function secureContextRequiredErrorCode(error: unknown): "secure_context_required" | undefined {
|
|
94
|
+
return typeof error === "object" &&
|
|
95
|
+
error !== null &&
|
|
96
|
+
"code" in error &&
|
|
97
|
+
error.code === "secure_context_required"
|
|
98
|
+
? error.code
|
|
99
|
+
: undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
/**
|
|
80
103
|
* Upload-and-track state for files attached to the next message. Owns the
|
|
81
104
|
* full client-side upload layer: a per-file `uploading | ready | failed`
|
|
@@ -198,6 +221,7 @@ export function useFileAttachments(
|
|
|
198
221
|
name: asset.filename,
|
|
199
222
|
contentType: asset.contentType,
|
|
200
223
|
sizeBytes: asset.sizeBytes,
|
|
224
|
+
errorCode: undefined,
|
|
201
225
|
error: undefined,
|
|
202
226
|
}
|
|
203
227
|
: attachment,
|
|
@@ -212,6 +236,7 @@ export function useFileAttachments(
|
|
|
212
236
|
? {
|
|
213
237
|
...attachment,
|
|
214
238
|
status: "failed",
|
|
239
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
215
240
|
error: error instanceof Error ? error.message : String(error),
|
|
216
241
|
}
|
|
217
242
|
: attachment,
|
|
@@ -225,7 +250,7 @@ export function useFileAttachments(
|
|
|
225
250
|
const addFiles = useCallback(
|
|
226
251
|
(files: Iterable<File>) => {
|
|
227
252
|
for (const file of files) {
|
|
228
|
-
const id =
|
|
253
|
+
const id = createAttachmentId();
|
|
229
254
|
sources.current.set(id, file);
|
|
230
255
|
const previewUrl = isImage(file) ? URL.createObjectURL(file) : undefined;
|
|
231
256
|
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
@@ -255,7 +280,7 @@ export function useFileAttachments(
|
|
|
255
280
|
setAttachments((current) =>
|
|
256
281
|
current.map((attachment) =>
|
|
257
282
|
attachment.id === id
|
|
258
|
-
? { ...attachment, status: "uploading", error: undefined }
|
|
283
|
+
? { ...attachment, status: "uploading", errorCode: undefined, error: undefined }
|
|
259
284
|
: attachment,
|
|
260
285
|
),
|
|
261
286
|
);
|
|
@@ -305,6 +330,7 @@ export function useFileAttachments(
|
|
|
305
330
|
sizeBytes: file.sizeBytes,
|
|
306
331
|
status: "ready",
|
|
307
332
|
file,
|
|
333
|
+
errorCode: undefined,
|
|
308
334
|
error: undefined,
|
|
309
335
|
}
|
|
310
336
|
: {
|
package/src/model-policy.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareModelPickerOrder,
|
|
3
|
+
modelPickerBillingClassFor,
|
|
4
|
+
type ModelPickerBillingClass,
|
|
5
|
+
} from "@opengeni/sdk/model-picker-order";
|
|
1
6
|
import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from "@opengeni/sdk";
|
|
2
7
|
|
|
3
|
-
export type PickerBillingClass =
|
|
4
|
-
| "opengeni_credits"
|
|
5
|
-
| "external"
|
|
6
|
-
| "codex_subscription"
|
|
7
|
-
| "supergrok_subscription"
|
|
8
|
-
| "byok";
|
|
8
|
+
export type PickerBillingClass = ModelPickerBillingClass;
|
|
9
9
|
|
|
10
10
|
export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
|
|
11
11
|
id: string;
|
|
@@ -23,20 +23,12 @@ export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogM
|
|
|
23
23
|
|
|
24
24
|
export type LatencyModeId = "standard" | "priority" | "fast";
|
|
25
25
|
|
|
26
|
-
const BILLING_CLASS_ORDER: PickerBillingClass[] = [
|
|
27
|
-
"opengeni_credits",
|
|
28
|
-
"external",
|
|
29
|
-
"codex_subscription",
|
|
30
|
-
"supergrok_subscription",
|
|
31
|
-
"byok",
|
|
32
|
-
];
|
|
33
|
-
|
|
34
26
|
const BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {
|
|
35
27
|
opengeni_credits: "OpenGeni",
|
|
36
28
|
external: "External",
|
|
37
29
|
codex_subscription: "Codex",
|
|
38
30
|
supergrok_subscription: "SuperGrok",
|
|
39
|
-
byok: "
|
|
31
|
+
byok: "Workspace providers",
|
|
40
32
|
};
|
|
41
33
|
|
|
42
34
|
const AVAILABILITY_REASON_LABELS: Record<string, string> = {
|
|
@@ -50,33 +42,10 @@ const AVAILABILITY_REASON_LABELS: Record<string, string> = {
|
|
|
50
42
|
};
|
|
51
43
|
|
|
52
44
|
export function billingClassForModel(model: ClientModel): PickerBillingClass {
|
|
53
|
-
if (model.
|
|
54
|
-
return "external";
|
|
55
|
-
}
|
|
56
|
-
if (model.source === "supergrok") {
|
|
57
|
-
return "supergrok_subscription";
|
|
58
|
-
}
|
|
59
|
-
if (model.source === "codex") {
|
|
60
|
-
return "codex_subscription";
|
|
61
|
-
}
|
|
62
|
-
if (model.source === "workspace_gateway") {
|
|
63
|
-
return "byok";
|
|
64
|
-
}
|
|
65
|
-
if (model.credentialSource?.kind === "connected_subscription") {
|
|
66
|
-
return model.credentialSource.provider === "xai"
|
|
67
|
-
? "supergrok_subscription"
|
|
68
|
-
: "codex_subscription";
|
|
69
|
-
}
|
|
70
|
-
if (model.credentialSource?.kind === "workspace_connection") {
|
|
45
|
+
if (model.provider === "workspace-gateway" || model.provider === "workspace-openrouter") {
|
|
71
46
|
return "byok";
|
|
72
47
|
}
|
|
73
|
-
|
|
74
|
-
return "codex_subscription";
|
|
75
|
-
}
|
|
76
|
-
if (model.billing?.upstreamPayer === "workspace") {
|
|
77
|
-
return "byok";
|
|
78
|
-
}
|
|
79
|
-
return "opengeni_credits";
|
|
48
|
+
return modelPickerBillingClassFor(model);
|
|
80
49
|
}
|
|
81
50
|
|
|
82
51
|
export function billingClassLabel(billingClass: PickerBillingClass): string {
|
|
@@ -144,6 +113,23 @@ export function labelReasoningEffort(effort: ReasoningEffort): string {
|
|
|
144
113
|
}
|
|
145
114
|
|
|
146
115
|
export function payerSummaryForModel(model: ClientModel): string {
|
|
116
|
+
if (model.cost === "free") {
|
|
117
|
+
return "Free in this deployment";
|
|
118
|
+
}
|
|
119
|
+
if (model.cost === "credits") {
|
|
120
|
+
return "OpenGeni credits";
|
|
121
|
+
}
|
|
122
|
+
if (model.cost === "subscription") {
|
|
123
|
+
return model.source === "supergrok"
|
|
124
|
+
? "SuperGrok subscription · external billing"
|
|
125
|
+
: "Codex subscription · external billing";
|
|
126
|
+
}
|
|
127
|
+
if (model.cost === "workspace") {
|
|
128
|
+
return workspaceProviderPayerSummary(model);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Older client-config payloads do not carry `cost`; preserve their existing
|
|
132
|
+
// settlement-derived label until every deployment has rolled forward.
|
|
147
133
|
const billing = model.billing;
|
|
148
134
|
if (!billing) {
|
|
149
135
|
return "Route unknown";
|
|
@@ -157,7 +143,7 @@ export function payerSummaryForModel(model: ClientModel): string {
|
|
|
157
143
|
: "Codex subscription · external billing";
|
|
158
144
|
}
|
|
159
145
|
if (billing.upstreamPayer === "workspace") {
|
|
160
|
-
return
|
|
146
|
+
return workspaceProviderPayerSummary(model);
|
|
161
147
|
}
|
|
162
148
|
return "External provider · no OpenGeni credits";
|
|
163
149
|
}
|
|
@@ -175,7 +161,13 @@ export function advancedSourceSummary(model: ClientModel): string | null {
|
|
|
175
161
|
: "Connected Codex subscription";
|
|
176
162
|
}
|
|
177
163
|
if (source.kind === "workspace_connection") {
|
|
178
|
-
|
|
164
|
+
if (model.provider === "workspace-openrouter") {
|
|
165
|
+
return "Workspace OpenRouter connection";
|
|
166
|
+
}
|
|
167
|
+
if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
|
|
168
|
+
return "Workspace Vercel AI Gateway";
|
|
169
|
+
}
|
|
170
|
+
return "Workspace provider connection";
|
|
179
171
|
}
|
|
180
172
|
if (source.kind === "deployment") {
|
|
181
173
|
return source.mechanism === "azure_ad_bearer"
|
|
@@ -185,6 +177,16 @@ export function advancedSourceSummary(model: ClientModel): string | null {
|
|
|
185
177
|
return null;
|
|
186
178
|
}
|
|
187
179
|
|
|
180
|
+
function workspaceProviderPayerSummary(model: ClientModel): string {
|
|
181
|
+
if (model.provider === "workspace-openrouter") {
|
|
182
|
+
return "Billed to the workspace OpenRouter account";
|
|
183
|
+
}
|
|
184
|
+
if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
|
|
185
|
+
return "Billed to the workspace Vercel account";
|
|
186
|
+
}
|
|
187
|
+
return "Billed to the workspace provider account";
|
|
188
|
+
}
|
|
189
|
+
|
|
188
190
|
export function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerModelRow[] {
|
|
189
191
|
return models
|
|
190
192
|
.filter((catalog) => {
|
|
@@ -232,18 +234,7 @@ export function projectClientModelRows(models: ClientModel[]): PickerModelRow<Cl
|
|
|
232
234
|
export function sortPickerRows<TCatalog extends ClientModel>(
|
|
233
235
|
rows: PickerModelRow<TCatalog>[],
|
|
234
236
|
): PickerModelRow<TCatalog>[] {
|
|
235
|
-
return [...rows].sort(
|
|
236
|
-
const classDelta =
|
|
237
|
-
BILLING_CLASS_ORDER.indexOf(left.billingClass) -
|
|
238
|
-
BILLING_CLASS_ORDER.indexOf(right.billingClass);
|
|
239
|
-
if (classDelta !== 0) {
|
|
240
|
-
return classDelta;
|
|
241
|
-
}
|
|
242
|
-
if (left.selectable !== right.selectable) {
|
|
243
|
-
return left.selectable ? -1 : 1;
|
|
244
|
-
}
|
|
245
|
-
return left.label.localeCompare(right.label);
|
|
246
|
-
});
|
|
237
|
+
return [...rows].sort(compareModelPickerOrder);
|
|
247
238
|
}
|
|
248
239
|
|
|
249
240
|
export function findPickerRow<TCatalog extends ClientModel>(
|
package/styles/compiled.css
CHANGED
|
@@ -4495,6 +4495,11 @@
|
|
|
4495
4495
|
max-width: 12rem;
|
|
4496
4496
|
}
|
|
4497
4497
|
}
|
|
4498
|
+
:where(.og-root).sm\:max-w-\[32rem\],:where(.og-root) .sm\:max-w-\[32rem\] {
|
|
4499
|
+
@media (width >= 40rem) {
|
|
4500
|
+
max-width: 32rem;
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4498
4503
|
:where(.og-root).sm\:max-w-\[82\%\],:where(.og-root) .sm\:max-w-\[82\%\] {
|
|
4499
4504
|
@media (width >= 40rem) {
|
|
4500
4505
|
max-width: 82%;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/model-policy-picker.tsx"],"sourcesContent":["import type { ClientModel, LatencyMode, ReasoningEffort } from \"@opengeni/sdk\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n Globe2Icon,\n KeyRoundIcon,\n ZapIcon,\n} from \"lucide-react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { DropdownMenu } from \"radix-ui\";\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type SVGProps,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenSource, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n effortOptionsForModel,\n findPickerRow,\n groupPickerRowsByBillingClass,\n labelReasoningEffort,\n projectClientModelRows,\n runnableLatencyModesForModel,\n type PickerBillingClass,\n type PickerModelRow,\n} from \"../model-policy\";\n\ntype ClientPickerModelRow = PickerModelRow<ClientModel>;\n\ntype NavLevel = \"providers\" | \"models\" | \"thinking\";\n\ntype PickerNavState = {\n level: NavLevel;\n rail: PickerBillingClass | null;\n modelId: string | null;\n};\n\ntype NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);\n\nexport type ModelPolicyPickerMessages = {\n label: string;\n loading: string;\n noModels: string;\n thinking: string;\n fast: string;\n fastRateHint: string;\n codexOnly: string;\n billingHints: Record<PickerBillingClass, string>;\n};\n\nexport const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {\n label: \"Model and effort\",\n loading: \"Loading model catalog…\",\n noModels: \"No models available.\",\n thinking: \"Thinking\",\n fast: \"Fast\",\n fastRateHint: \"2× rate\",\n codexOnly: \"Codex-only session\",\n billingHints: {\n opengeni_credits: \"Will use credits\",\n external: \"Provider terms and limits apply\",\n codex_subscription: \"ChatGPT / Codex plan\",\n supergrok_subscription: \"SuperGrok / xAI plan\",\n byok: \"Billed to your AI Gateway\",\n },\n};\n\nexport type ModelPolicyPickerProps = {\n /** Lightweight deployment models. Catalog rows take precedence when supplied. */\n models?: ClientModel[] | undefined;\n /** Catalog-backed rows with availability and billing-class truth. */\n rows?: PickerModelRow[] | undefined;\n model: string;\n effort: ReasoningEffort;\n latencyMode: LatencyMode;\n disabled?: boolean | undefined;\n loading?: boolean | undefined;\n error?: string | null | undefined;\n /** Controlled menu state. Omit for the built-in uncontrolled behavior. */\n open?: boolean | undefined;\n /** Initial menu state when `open` is uncontrolled. */\n defaultOpen?: boolean | undefined;\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Non-Codex models stay visible but cannot be selected. */\n codexOnly?: boolean | undefined;\n /** Resets drill-down navigation when the session scope changes. */\n sessionKey?: string | undefined;\n /** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /** Hide latency controls on surfaces whose saved policy does not include latency. */\n allowLatencyMode?: boolean | undefined;\n /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */\n contentClassName?: string | undefined;\n /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */\n contentStyle?: CSSProperties | undefined;\n className?: string | undefined;\n messages?: Partial<ModelPolicyPickerMessages> | undefined;\n onModelChange: (modelId: string) => void;\n onEffortChange: (effort: ReasoningEffort) => void;\n onLatencyModeChange: (latencyMode: LatencyMode) => void;\n};\n\nconst SLIDE_EASE = [0.22, 1, 0.36, 1] as const;\n\nfunction OpenGeniMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg\n viewBox=\"0 0 140 133\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.2}\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <g transform=\"translate(-17.5,-20.999893188476562) scale(1.75)\">\n <g transform=\"translate(0,-952.36218)\">\n <path d=\"m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z\" />\n </g>\n </g>\n </svg>\n );\n}\n\nfunction ChatGptMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n </svg>\n );\n}\n\nfunction XaiMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 466.04 516.93\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <polygon points=\"0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71\" />\n <polygon points=\"0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92\" />\n <polygon points=\"466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0\" />\n <polygon points=\"380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92\" />\n </svg>\n );\n}\n\nexport function BillingClassMark(props: {\n billingClass: PickerBillingClass;\n className?: string | undefined;\n \"aria-label\"?: string | undefined;\n}) {\n const labels: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n external: \"External provider\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n byok: \"Bring your own key\",\n };\n const label = props[\"aria-label\"] ?? labels[props.billingClass];\n const accessibility =\n label.length === 0\n ? { \"aria-hidden\": true as const }\n : { role: \"img\" as const, \"aria-label\": label };\n const shell = cn(\n \"inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle\",\n props.className,\n );\n const mark = \"size-3.5\";\n return (\n <span\n className={shell}\n data-testid={`billing-class-icon-${props.billingClass}`}\n {...accessibility}\n >\n {props.billingClass === \"opengeni_credits\" ? (\n <OpenGeniMark className={mark} />\n ) : props.billingClass === \"external\" ? (\n <Globe2Icon className={mark} aria-hidden />\n ) : props.billingClass === \"codex_subscription\" ? (\n <ChatGptMark className={mark} />\n ) : props.billingClass === \"supergrok_subscription\" ? (\n <XaiMark className={mark} />\n ) : (\n <KeyRoundIcon className={mark} aria-hidden />\n )}\n </span>\n );\n}\n\nfunction isCodexModel(model: ClientModel): boolean {\n return model.id.startsWith(\"codex/\") || model.source === \"codex\";\n}\n\nfunction applyCodexOnly(\n rows: ClientPickerModelRow[],\n codexOnly: boolean,\n unavailableReason: string,\n): ClientPickerModelRow[] {\n if (!codexOnly) return rows;\n return rows.map((row) =>\n isCodexModel(row.catalog)\n ? row\n : {\n ...row,\n selectable: false,\n unavailableReason: row.unavailableReason ?? unavailableReason,\n },\n );\n}\n\nfunction effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {\n const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);\n}\n\nfunction defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {\n const selected = findPickerRow(rows, modelId);\n if (!selected) {\n const rails = new Set(rows.map((row) => row.billingClass));\n const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;\n return onlyRail\n ? { level: \"models\", rail: onlyRail, modelId: null }\n : { level: \"providers\", rail: null, modelId: null };\n }\n return { level: \"thinking\", rail: selected.billingClass, modelId: selected.id };\n}\n\nfunction usePickerNavState(\n sessionKey: string | undefined,\n rows: ClientPickerModelRow[],\n model: string,\n): [PickerNavState, (next: NavUpdater) => void] {\n const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));\n useEffect(() => {\n setState(defaultNavState(rows, model));\n // The menu deliberately preserves its drill-down while the same session is mounted.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessionKey]);\n return [state, setState];\n}\n\nexport function PickerNavRow(props: {\n label: string;\n hint?: string | undefined;\n icon?: ReactNode;\n trailing?: ReactNode;\n showChevron?: boolean | undefined;\n disabled?: boolean | undefined;\n title?: string | undefined;\n active?: boolean | undefined;\n testId?: string | undefined;\n onClick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n disabled={props.disabled}\n title={props.title}\n onClick={props.onClick}\n data-testid={props.testId}\n className={cn(\n \"flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\",\n props.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n {props.icon}\n <span className=\"min-w-0 flex-1\">\n <span className={cn(\"block truncate text-og-menu\", props.active && \"font-medium\")}>\n {props.label}\n </span>\n {props.hint ? (\n <span className=\"mt-0.5 block truncate text-og-control text-og-fg-subtle\">\n {props.hint}\n </span>\n ) : null}\n </span>\n {props.trailing ? <span className=\"ml-auto shrink-0\">{props.trailing}</span> : null}\n {props.showChevron === false ? null : (\n <ChevronRightIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n )}\n </button>\n );\n}\n\nexport function PickerBackHeader(props: {\n label: string;\n icon?: ReactNode;\n onBack: () => void;\n trailing?: ReactNode;\n}) {\n return (\n <div className=\"mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5\">\n <button\n type=\"button\"\n onClick={props.onBack}\n data-testid=\"model-picker-back\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\"\n >\n <ChevronLeftIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n {props.icon}\n <span className=\"min-w-0 flex-1 truncate text-og-menu font-medium\">{props.label}</span>\n </button>\n {props.trailing ? <div className=\"relative z-10 shrink-0\">{props.trailing}</div> : null}\n </div>\n );\n}\n\nexport function PickerAnimatedPage(props: {\n pageKey: string;\n direction: 1 | -1;\n children: ReactNode;\n}) {\n const reduceMotion = useReducedMotion();\n return (\n <motion.div\n key={props.pageKey}\n initial={reduceMotion ? false : { x: props.direction * 14, opacity: 0 }}\n animate={{ x: 0, opacity: 1 }}\n transition={{ duration: reduceMotion ? 0 : 0.18, ease: SLIDE_EASE }}\n className=\"w-full\"\n >\n {props.children}\n </motion.div>\n );\n}\n\nexport function ModelPolicyPickerMenu(\n props: ModelPolicyPickerProps & {\n nav?: PickerNavState | undefined;\n onNavChange?: ((next: NavUpdater) => void) | undefined;\n },\n) {\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const rows = effectiveRows(props);\n const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);\n const [localNav, setLocalNav] = usePickerNavState(\n props.nav === undefined ? props.sessionKey : undefined,\n rows,\n props.model,\n );\n const nav = props.nav ?? localNav;\n const setNav = props.onNavChange ?? setLocalNav;\n const [direction, setDirection] = useState<1 | -1>(1);\n\n const effectiveNav =\n nav.level === \"providers\" && groups.length === 1\n ? { level: \"models\" as const, rail: groups[0]!.billingClass, modelId: null }\n : nav;\n const activeGroup =\n effectiveNav.rail === null\n ? undefined\n : groups.find((group) => group.billingClass === effectiveNav.rail);\n const focusModel =\n effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);\n const selectedRow = findPickerRow(rows, props.model);\n\n const go = (next: PickerNavState, nextDirection: 1 | -1) => {\n setDirection(nextDirection);\n setNav(next);\n };\n\n const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {\n if (!row.selectable) return;\n if (row.id !== props.model) props.onModelChange(row.id);\n props.onEffortChange(effort);\n };\n\n const pageKey =\n effectiveNav.level === \"providers\"\n ? \"providers\"\n : effectiveNav.level === \"models\"\n ? `models:${effectiveNav.rail ?? \"\"}`\n : `thinking:${effectiveNav.modelId ?? \"\"}`;\n\n let body: ReactNode;\n if (props.loading) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.loading}</p>;\n } else if (groups.length === 0) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.noModels}</p>;\n } else if (effectiveNav.level === \"providers\") {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n active={selectedRow?.billingClass === group.billingClass}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n } else if (effectiveNav.level === \"models\" && activeGroup) {\n body = (\n <div data-testid=\"model-picker-models\">\n {groups.length > 1 ? (\n <PickerBackHeader\n label={activeGroup.label}\n icon={<BillingClassMark billingClass={activeGroup.billingClass} />}\n onBack={() => go({ level: \"providers\", rail: null, modelId: null }, -1)}\n />\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {activeGroup.rows.map((row) => (\n <PickerNavRow\n key={`${row.billingClass}:${row.id}`}\n label={row.label}\n hint={row.unavailableReason ?? undefined}\n disabled={!row.selectable}\n title={row.unavailableReason ?? undefined}\n active={row.id === props.model}\n testId={`model-picker-choice-${row.id}`}\n onClick={() => {\n if (row.selectable) {\n go({ level: \"thinking\", rail: row.billingClass, modelId: row.id }, 1);\n }\n }}\n />\n ))}\n </div>\n </div>\n );\n } else if (effectiveNav.level === \"thinking\" && focusModel) {\n const activeModel = focusModel.id === props.model;\n const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes(\"fast\");\n body = (\n <div data-testid=\"model-picker-reasoning\">\n <PickerBackHeader\n label={focusModel.label}\n icon={<BillingClassMark billingClass={focusModel.billingClass} />}\n onBack={() => go({ level: \"models\", rail: focusModel.billingClass, modelId: null }, -1)}\n trailing={\n supportsFast && props.allowLatencyMode !== false ? (\n <button\n type=\"button\"\n disabled={!focusModel.selectable}\n aria-pressed={activeModel && props.latencyMode === \"fast\"}\n aria-label={\n activeModel && props.latencyMode === \"fast\"\n ? `Disable ${messages.fast}`\n : `Enable ${messages.fast}`\n }\n title={`${messages.fast} · ${messages.fastRateHint}`}\n data-testid=\"model-picker-fast\"\n onClick={() => {\n if (!activeModel) {\n props.onModelChange(focusModel.id);\n }\n props.onLatencyModeChange(\n activeModel && props.latencyMode === \"fast\" ? \"standard\" : \"fast\",\n );\n }}\n className={cn(\n \"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n activeModel && props.latencyMode === \"fast\" && \"text-og-fg\",\n )}\n >\n <ZapIcon\n className={cn(\n \"size-3.5\",\n activeModel && props.latencyMode === \"fast\" && \"fill-current\",\n )}\n />\n </button>\n ) : null\n }\n />\n <p className=\"px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase\">\n {messages.thinking}\n </p>\n {focusModel.catalog.capabilities?.inputModalities.includes(\"image\") === false ? (\n <p className=\"px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle\">\n Unsupported attachments stay in the session but are hidden from this model.\n </p>\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {effortOptionsForModel(focusModel.catalog).map((effort) => {\n const selected = activeModel && effort === props.effort;\n return (\n <button\n key={effort}\n type=\"button\"\n disabled={!focusModel.selectable}\n onClick={() => selectEffort(focusModel, effort)}\n className={cn(\n \"flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n selected && \"bg-og-surface-2\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{labelReasoningEffort(effort)}</span>\n {selected ? (\n <CheckIcon\n className=\"ml-auto size-3.5 shrink-0\"\n data-testid=\"model-picker-effort-check\"\n />\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n } else {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div data-testid=\"model-picker-menu\" className=\"relative overflow-hidden\">\n {props.error ? (\n <p className=\"px-2 py-1 text-og-control text-og-status-failed\" role=\"alert\">\n {props.error}\n </p>\n ) : null}\n <PickerAnimatedPage pageKey={pageKey} direction={direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\n/** @internal Shared state seam kept outside the Radix portal for deterministic testing. */\nexport function useModelPolicyPickerState(props: ModelPolicyPickerProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);\n const open = props.open ?? uncontrolledOpen;\n const rows = effectiveRows(props);\n const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);\n const previousControlledOpen = useRef(props.open);\n useEffect(() => {\n const wasOpen = previousControlledOpen.current;\n previousControlledOpen.current = props.open;\n if (props.open === true && wasOpen !== true) {\n setNav(defaultNavState(rows, props.model));\n }\n // Reset only on the controlled closed-to-open edge; ordinary rerenders\n // deliberately preserve the current drill-down page.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.open]);\n const setOpen = (next: boolean) => {\n if (next) {\n setNav(defaultNavState(rows, props.model));\n }\n if (props.open === undefined) setUncontrolledOpen(next);\n props.onOpenChange?.(next);\n };\n return { open, setOpen, rows, nav, setNav };\n}\n\nexport function ModelPolicyPicker(props: ModelPolicyPickerProps) {\n const trigger = usePortalTokenSource<HTMLButtonElement>();\n const portalStyle = usePortalTokenStyle(trigger.source);\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const { open, setOpen, rows, nav, setNav } = useModelPolicyPickerState(props);\n const selected = findPickerRow(rows, props.model);\n\n if (props.loading) {\n return (\n <span\n className={cn(\n \"og-root inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2\",\n props.className,\n )}\n aria-label={messages.loading}\n data-testid=\"model-picker-loading\"\n />\n );\n }\n return (\n <DropdownMenu.Root open={open} onOpenChange={setOpen}>\n <DropdownMenu.Trigger asChild>\n <button\n ref={trigger.ref}\n type=\"button\"\n disabled={props.disabled}\n aria-label={messages.label}\n className={cn(\n \"og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2\",\n props.className,\n )}\n >\n <BillingClassMark\n billingClass={\n selected?.billingClass ??\n (props.model.startsWith(\"codex/\") ? \"codex_subscription\" : \"opengeni_credits\")\n }\n className=\"text-og-fg\"\n />\n <span className=\"og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden\">\n {selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden\">\n {selected?.shortLabel ?? selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-effort max-sm:hidden\">\n {labelReasoningEffort(props.effort)}\n </span>\n {props.latencyMode === \"fast\" ? (\n <ZapIcon\n className=\"size-3.5 shrink-0 fill-current stroke-current text-og-fg\"\n aria-label={messages.fast}\n data-testid=\"model-picker-fast-icon\"\n />\n ) : null}\n <ChevronDownIcon className=\"size-3 shrink-0\" />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n align=\"start\"\n side={props.menuSide ?? \"bottom\"}\n sideOffset={8}\n collisionPadding={12}\n onCloseAutoFocus={(event) => event.preventDefault()}\n className={cn(\n \"og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg\",\n props.contentClassName,\n )}\n style={{ ...portalStyle, ...props.contentStyle }}\n data-testid=\"model-picker-content\"\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />\n </div>\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":[]}
|