@lastbrain/ai-ui-react 1.0.63 → 1.0.65
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/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +1 -1
- package/dist/components/AiInput.d.ts +1 -1
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +16 -2
- package/dist/components/AiPromptPanel.d.ts +1 -0
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +39 -9
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +81 -12
- package/dist/components/AiTextarea.d.ts +1 -1
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +16 -2
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +143 -182
- package/dist/hooks/usePrompts.d.ts.map +1 -1
- package/dist/hooks/usePrompts.js +12 -4
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +23 -8
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +2 -0
- package/src/components/AiInput.tsx +18 -2
- package/src/components/AiPromptPanel.tsx +50 -9
- package/src/components/AiStatusButton.tsx +127 -13
- package/src/components/AiTextarea.tsx +18 -2
- package/src/context/LBAuthProvider.tsx +151 -208
- package/src/hooks/usePrompts.ts +11 -4
- package/src/utils/modelManagement.ts +25 -8
|
@@ -11,6 +11,7 @@ import { aiStyles } from "../styles/inline";
|
|
|
11
11
|
import { handleAIError } from "../utils/errorHandler";
|
|
12
12
|
import { useLB } from "../context/LBAuthProvider";
|
|
13
13
|
import { LBSigninModal } from "./LBSigninModal";
|
|
14
|
+
import { useAiContext } from "../context/AiProvider";
|
|
14
15
|
|
|
15
16
|
export interface AiInputProps
|
|
16
17
|
extends
|
|
@@ -21,8 +22,8 @@ export interface AiInputProps
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export function AiInput({
|
|
24
|
-
baseUrl,
|
|
25
|
-
apiKeyId,
|
|
25
|
+
baseUrl: propBaseUrl,
|
|
26
|
+
apiKeyId: propApiKeyId,
|
|
26
27
|
uiMode = "modal",
|
|
27
28
|
context,
|
|
28
29
|
model,
|
|
@@ -57,6 +58,20 @@ export function AiInput({
|
|
|
57
58
|
lbStatus = undefined;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
let ctxBaseUrl: string | undefined;
|
|
62
|
+
let ctxApiKeyId: string | undefined;
|
|
63
|
+
try {
|
|
64
|
+
const aiContext = useAiContext();
|
|
65
|
+
ctxBaseUrl = aiContext.baseUrl;
|
|
66
|
+
ctxApiKeyId = aiContext.apiKeyId;
|
|
67
|
+
} catch {
|
|
68
|
+
ctxBaseUrl = undefined;
|
|
69
|
+
ctxApiKeyId = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
73
|
+
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
74
|
+
|
|
60
75
|
const { models } = useAiModels({
|
|
61
76
|
baseUrl,
|
|
62
77
|
apiKeyId,
|
|
@@ -237,6 +252,7 @@ export function AiInput({
|
|
|
237
252
|
apiKey={apiKeyId}
|
|
238
253
|
baseUrl={baseUrl}
|
|
239
254
|
enableModelManagement={enableModelManagement}
|
|
255
|
+
showOnlyUserModels={true}
|
|
240
256
|
/>
|
|
241
257
|
)}
|
|
242
258
|
<LBSigninModal
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "../hooks/usePrompts";
|
|
20
20
|
import { useModelManagement } from "../hooks/useModelManagement";
|
|
21
21
|
import { type AIModel } from "../context/AiProvider";
|
|
22
|
-
import { AiProvider } from "../context/AiProvider";
|
|
22
|
+
import { AiProvider, useAiContext } from "../context/AiProvider";
|
|
23
23
|
|
|
24
24
|
export interface AiPromptPanelProps {
|
|
25
25
|
isOpen: boolean;
|
|
@@ -37,6 +37,7 @@ export interface AiPromptPanelProps {
|
|
|
37
37
|
onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
|
|
38
38
|
apiKey?: string;
|
|
39
39
|
baseUrl?: string;
|
|
40
|
+
showOnlyUserModels?: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export interface AiPromptPanelRenderProps {
|
|
@@ -59,8 +60,20 @@ export interface AiPromptPanelRenderProps {
|
|
|
59
60
|
|
|
60
61
|
export function AiPromptPanel(props: AiPromptPanelProps) {
|
|
61
62
|
const { apiKey, baseUrl } = props;
|
|
63
|
+
let hasContext = false;
|
|
64
|
+
try {
|
|
65
|
+
useAiContext();
|
|
66
|
+
hasContext = true;
|
|
67
|
+
} catch {
|
|
68
|
+
hasContext = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Si un contexte existe déjà, ne pas re-wrapper (évite les refetch multiples).
|
|
72
|
+
if (hasContext) {
|
|
73
|
+
return <AiPromptPanelInternal {...props} />;
|
|
74
|
+
}
|
|
62
75
|
|
|
63
|
-
//
|
|
76
|
+
// Sinon, si apiKey/baseUrl sont fournis, wrapper avec AiProvider
|
|
64
77
|
if (apiKey || baseUrl) {
|
|
65
78
|
return (
|
|
66
79
|
<AiProvider baseUrl={baseUrl || ""} apiKeyId={apiKey || ""}>
|
|
@@ -88,6 +101,7 @@ function AiPromptPanelInternal({
|
|
|
88
101
|
onModelToggle,
|
|
89
102
|
apiKey,
|
|
90
103
|
baseUrl,
|
|
104
|
+
showOnlyUserModels = false,
|
|
91
105
|
}: AiPromptPanelProps) {
|
|
92
106
|
const [selectedModel, setSelectedModel] = useState("");
|
|
93
107
|
const [prompt, setPrompt] = useState("");
|
|
@@ -180,13 +194,22 @@ function AiPromptPanelInternal({
|
|
|
180
194
|
(m) => m.category === modelCategory
|
|
181
195
|
);
|
|
182
196
|
|
|
197
|
+
if (!showAllModels && showOnlyUserModels) {
|
|
198
|
+
return categoryModels.filter((m) => effectiveUserModels.includes(m.id));
|
|
199
|
+
}
|
|
200
|
+
|
|
183
201
|
if (showAllModels) {
|
|
184
202
|
return categoryModels;
|
|
185
|
-
} else {
|
|
186
|
-
return categoryModels.filter((m) => effectiveUserModels.includes(m.id));
|
|
187
203
|
}
|
|
204
|
+
|
|
205
|
+
const enabledModels = categoryModels.filter((m) =>
|
|
206
|
+
effectiveUserModels.includes(m.id)
|
|
207
|
+
);
|
|
208
|
+
return enabledModels.length > 0 ? enabledModels : categoryModels;
|
|
188
209
|
};
|
|
189
210
|
|
|
211
|
+
const modelOptions = getFilteredModels();
|
|
212
|
+
|
|
190
213
|
// Fetch prompts when modal opens
|
|
191
214
|
useEffect(() => {
|
|
192
215
|
if (isOpen && (models.length > 0 || enableModelManagement)) {
|
|
@@ -209,7 +232,7 @@ function AiPromptPanelInternal({
|
|
|
209
232
|
]);
|
|
210
233
|
|
|
211
234
|
const handleSubmit = async () => {
|
|
212
|
-
const activeModelId = selectedModel ||
|
|
235
|
+
const activeModelId = selectedModel || modelOptions[0]?.id;
|
|
213
236
|
if (!activeModelId || !prompt.trim()) return;
|
|
214
237
|
setIsGenerating(true);
|
|
215
238
|
try {
|
|
@@ -249,6 +272,20 @@ function AiPromptPanelInternal({
|
|
|
249
272
|
};
|
|
250
273
|
}, []);
|
|
251
274
|
|
|
275
|
+
useEffect(() => {
|
|
276
|
+
if (!isOpen) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (modelOptions.length === 0) {
|
|
280
|
+
setSelectedModel("");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const hasSelected = modelOptions.some((model) => model.id === selectedModel);
|
|
284
|
+
if (!hasSelected) {
|
|
285
|
+
setSelectedModel(modelOptions[0].id);
|
|
286
|
+
}
|
|
287
|
+
}, [isOpen, modelOptions, selectedModel]);
|
|
288
|
+
|
|
252
289
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
253
290
|
if (e.key === "Escape") {
|
|
254
291
|
handleClose();
|
|
@@ -284,7 +321,7 @@ function AiPromptPanelInternal({
|
|
|
284
321
|
|
|
285
322
|
if (!isOpen) return null;
|
|
286
323
|
|
|
287
|
-
const activeModelId = selectedModel ||
|
|
324
|
+
const activeModelId = selectedModel || modelOptions[0]?.id || "";
|
|
288
325
|
const currentModelType = models.find((m) => m.id === activeModelId)?.type;
|
|
289
326
|
const filteredPrompts = prompts.filter((p: Prompt | PublicPrompt) => {
|
|
290
327
|
const matchesType =
|
|
@@ -562,10 +599,14 @@ function AiPromptPanelInternal({
|
|
|
562
599
|
...(modelFocused && aiStyles.selectFocus),
|
|
563
600
|
}}
|
|
564
601
|
>
|
|
565
|
-
{
|
|
566
|
-
<option value="">
|
|
602
|
+
{modelOptions.length === 0 && (
|
|
603
|
+
<option value="">
|
|
604
|
+
{showOnlyUserModels
|
|
605
|
+
? "No active models. Open 'Gérer les modèles'."
|
|
606
|
+
: "Loading models..."}
|
|
607
|
+
</option>
|
|
567
608
|
)}
|
|
568
|
-
{
|
|
609
|
+
{modelOptions.map((model) => {
|
|
569
610
|
const isActive = effectiveUserModels.includes(model.id);
|
|
570
611
|
return (
|
|
571
612
|
<option
|
|
@@ -43,6 +43,9 @@ export function AiStatusButton({
|
|
|
43
43
|
let lbStorageStatus: any = null;
|
|
44
44
|
let lbIsLoadingStatus: boolean = false;
|
|
45
45
|
let lbIsLoadingStorage: boolean = false;
|
|
46
|
+
let lbSelectedKey: any = null;
|
|
47
|
+
let lbRefreshBasicStatus: (() => Promise<void>) | undefined;
|
|
48
|
+
let lbRefreshStorageStatus: ((force?: boolean) => Promise<void>) | undefined;
|
|
46
49
|
|
|
47
50
|
try {
|
|
48
51
|
const lbContext = useLB();
|
|
@@ -58,6 +61,9 @@ export function AiStatusButton({
|
|
|
58
61
|
lbStorageStatus = lbContext.storageStatus;
|
|
59
62
|
lbIsLoadingStatus = lbContext.isLoadingStatus || false;
|
|
60
63
|
lbIsLoadingStorage = lbContext.isLoadingStorage || false;
|
|
64
|
+
lbSelectedKey = lbContext.selectedKey || null;
|
|
65
|
+
lbRefreshBasicStatus = lbContext.refreshBasicStatus;
|
|
66
|
+
lbRefreshStorageStatus = lbContext.refreshStorageStatus;
|
|
61
67
|
} catch {
|
|
62
68
|
// LBProvider n'est pas disponible, ignorer
|
|
63
69
|
lbStatus = undefined;
|
|
@@ -65,13 +71,17 @@ export function AiStatusButton({
|
|
|
65
71
|
logout = undefined;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// Toujours prioriser les données du contexte LB quand disponibles
|
|
75
|
+
// pour éviter d'afficher un status externe obsolète (Unknown/0).
|
|
76
|
+
const lbEffectiveStatus =
|
|
77
|
+
lbStatus === "ready"
|
|
78
|
+
? {
|
|
79
|
+
...(lbApiStatus || {}),
|
|
80
|
+
...(lbBasicStatus || {}),
|
|
81
|
+
storage: lbStorageStatus?.storage || lbApiStatus?.storage,
|
|
82
|
+
}
|
|
83
|
+
: null;
|
|
84
|
+
const effectiveStatus = lbEffectiveStatus || status || null;
|
|
75
85
|
|
|
76
86
|
// Récupérer refetchProviders depuis AiProvider si disponible
|
|
77
87
|
let refetchProviders: (() => Promise<void>) | undefined;
|
|
@@ -94,6 +104,7 @@ export function AiStatusButton({
|
|
|
94
104
|
percentage?: number;
|
|
95
105
|
purchased?: number;
|
|
96
106
|
quota?: number;
|
|
107
|
+
remaining?: number;
|
|
97
108
|
};
|
|
98
109
|
type StorageUsage = {
|
|
99
110
|
used_mb?: number;
|
|
@@ -104,8 +115,6 @@ export function AiStatusButton({
|
|
|
104
115
|
total_mb?: number;
|
|
105
116
|
};
|
|
106
117
|
|
|
107
|
-
const formatNumber = (value: number | null | undefined) =>
|
|
108
|
-
typeof value === "number" ? value.toLocaleString() : "0";
|
|
109
118
|
const formatFixed = (value: number | null | undefined, digits: number) =>
|
|
110
119
|
typeof value === "number" ? value.toFixed(digits) : "0.00";
|
|
111
120
|
const formatStorage = (valueMb: number | null | undefined) => {
|
|
@@ -179,10 +188,13 @@ export function AiStatusButton({
|
|
|
179
188
|
const balanceUsage = effectiveStatus?.balance as BalanceUsage | undefined;
|
|
180
189
|
const storageUsage = effectiveStatus?.storage as StorageUsage | undefined;
|
|
181
190
|
|
|
182
|
-
const
|
|
191
|
+
const balanceUsed = safeNumber(balanceUsage?.used);
|
|
192
|
+
const balanceRemaining = safeNumber(balanceUsage?.remaining);
|
|
193
|
+
const rawBalanceTotal =
|
|
183
194
|
balanceUsage?.total ??
|
|
184
195
|
safeNumber(balanceUsage?.purchased) + safeNumber(balanceUsage?.quota);
|
|
185
|
-
const
|
|
196
|
+
const balanceTotal =
|
|
197
|
+
rawBalanceTotal > 0 ? rawBalanceTotal : balanceUsed + balanceRemaining;
|
|
186
198
|
const balancePercentage =
|
|
187
199
|
balanceUsage?.percentage ??
|
|
188
200
|
(balanceTotal > 0 ? Math.round((balanceUsed / balanceTotal) * 100) : 0);
|
|
@@ -202,6 +214,11 @@ export function AiStatusButton({
|
|
|
202
214
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
203
215
|
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
204
216
|
const canPortal = typeof document !== "undefined";
|
|
217
|
+
const hasApiKeySelected = Boolean(
|
|
218
|
+
effectiveStatus?.apiKey?.id || effectiveStatus?.api_key?.id || lbSelectedKey?.id
|
|
219
|
+
);
|
|
220
|
+
const requiresApiKeySelection =
|
|
221
|
+
lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
|
|
205
222
|
|
|
206
223
|
useLayoutEffect(() => {
|
|
207
224
|
if (!showTooltip || !buttonRef.current) {
|
|
@@ -279,11 +296,17 @@ export function AiStatusButton({
|
|
|
279
296
|
}, [showTooltip, canPortal]);
|
|
280
297
|
|
|
281
298
|
const handleMouseEnter = () => {
|
|
299
|
+
if (requiresApiKeySelection) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
282
302
|
setShowTooltip(true);
|
|
283
303
|
setIsHovered(true);
|
|
284
304
|
};
|
|
285
305
|
|
|
286
306
|
const handleMouseLeave = () => {
|
|
307
|
+
if (requiresApiKeySelection) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
287
310
|
// Keep tooltip visible if hovering over it
|
|
288
311
|
setTimeout(() => {
|
|
289
312
|
if (
|
|
@@ -296,7 +319,37 @@ export function AiStatusButton({
|
|
|
296
319
|
}, 100);
|
|
297
320
|
};
|
|
298
321
|
|
|
299
|
-
|
|
322
|
+
useLayoutEffect(() => {
|
|
323
|
+
if (!showTooltip || requiresApiKeySelection || lbStatus !== "ready") {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (!lbBasicStatus && !lbIsLoadingStatus && lbRefreshBasicStatus) {
|
|
327
|
+
lbRefreshBasicStatus().catch(() => undefined);
|
|
328
|
+
}
|
|
329
|
+
if (
|
|
330
|
+
!lbStorageStatus?.storage &&
|
|
331
|
+
!lbIsLoadingStorage &&
|
|
332
|
+
lbRefreshStorageStatus
|
|
333
|
+
) {
|
|
334
|
+
lbRefreshStorageStatus().catch(() => undefined);
|
|
335
|
+
}
|
|
336
|
+
}, [
|
|
337
|
+
showTooltip,
|
|
338
|
+
requiresApiKeySelection,
|
|
339
|
+
lbStatus,
|
|
340
|
+
lbBasicStatus,
|
|
341
|
+
lbStorageStatus,
|
|
342
|
+
lbIsLoadingStatus,
|
|
343
|
+
lbIsLoadingStorage,
|
|
344
|
+
lbRefreshBasicStatus,
|
|
345
|
+
lbRefreshStorageStatus,
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
if (
|
|
349
|
+
loading ||
|
|
350
|
+
isSelectingApiKey ||
|
|
351
|
+
((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)
|
|
352
|
+
) {
|
|
300
353
|
return (
|
|
301
354
|
<button
|
|
302
355
|
ref={buttonRef}
|
|
@@ -321,6 +374,59 @@ export function AiStatusButton({
|
|
|
321
374
|
);
|
|
322
375
|
}
|
|
323
376
|
|
|
377
|
+
if (requiresApiKeySelection) {
|
|
378
|
+
return (
|
|
379
|
+
<div style={{ position: "relative", display: "inline-block" }}>
|
|
380
|
+
<button
|
|
381
|
+
ref={buttonRef}
|
|
382
|
+
style={{
|
|
383
|
+
...aiStyles.statusButton,
|
|
384
|
+
color: "#f59e0b",
|
|
385
|
+
...(isHovered && aiStyles.statusButtonHover),
|
|
386
|
+
}}
|
|
387
|
+
className={className}
|
|
388
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
389
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
390
|
+
onClick={() => setShowApiKeySelector(true)}
|
|
391
|
+
title="Select an API key to enable AI status and generation"
|
|
392
|
+
>
|
|
393
|
+
<svg
|
|
394
|
+
width="16"
|
|
395
|
+
height="16"
|
|
396
|
+
viewBox="0 0 24 24"
|
|
397
|
+
fill="none"
|
|
398
|
+
stroke="currentColor"
|
|
399
|
+
>
|
|
400
|
+
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
|
401
|
+
</svg>
|
|
402
|
+
</button>
|
|
403
|
+
{showApiKeySelector && apiKeys.length > 0 && (
|
|
404
|
+
<LBApiKeySelector
|
|
405
|
+
isOpen={showApiKeySelector}
|
|
406
|
+
apiKeys={apiKeys}
|
|
407
|
+
onSelect={async (keyId) => {
|
|
408
|
+
setIsSelectingApiKey(true);
|
|
409
|
+
try {
|
|
410
|
+
if (switchApiKey) {
|
|
411
|
+
await switchApiKey(keyId);
|
|
412
|
+
}
|
|
413
|
+
setShowApiKeySelector(false);
|
|
414
|
+
if (refetchProviders) {
|
|
415
|
+
await refetchProviders();
|
|
416
|
+
}
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error("Failed to select API key:", error);
|
|
419
|
+
} finally {
|
|
420
|
+
setIsSelectingApiKey(false);
|
|
421
|
+
}
|
|
422
|
+
}}
|
|
423
|
+
onCancel={() => setShowApiKeySelector(false)}
|
|
424
|
+
/>
|
|
425
|
+
)}
|
|
426
|
+
</div>
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
324
430
|
if (!effectiveStatus) {
|
|
325
431
|
// Si pas de statut API et pas de LBProvider, afficher message simple
|
|
326
432
|
if (!lbStatus && lbStatus !== "ready") {
|
|
@@ -828,6 +934,14 @@ export function AiStatusButton({
|
|
|
828
934
|
req/min
|
|
829
935
|
</span>
|
|
830
936
|
</div>
|
|
937
|
+
<div style={aiStyles.tooltipRow}>
|
|
938
|
+
<span style={aiStyles.tooltipLabel}>Auth:</span>
|
|
939
|
+
<span style={aiStyles.tooltipValue}>
|
|
940
|
+
{lbIsLoadingStatus
|
|
941
|
+
? "..."
|
|
942
|
+
: effectiveStatus?.authType || lbStatus || "unknown"}
|
|
943
|
+
</span>
|
|
944
|
+
</div>
|
|
831
945
|
</div>
|
|
832
946
|
|
|
833
947
|
<div style={aiStyles.tooltipSection}>
|
|
@@ -835,7 +949,7 @@ export function AiStatusButton({
|
|
|
835
949
|
<div style={aiStyles.tooltipRow}>
|
|
836
950
|
<span style={aiStyles.tooltipLabel}>Total:</span>
|
|
837
951
|
<span style={aiStyles.tooltipValue}>
|
|
838
|
-
${formatFixed(balanceUsed,
|
|
952
|
+
${formatFixed(balanceUsed, 2)} / ${formatFixed(balanceTotal, 2)}
|
|
839
953
|
</span>
|
|
840
954
|
{renderUsageCircle(balancePercentage)}
|
|
841
955
|
</div>
|
|
@@ -16,6 +16,7 @@ import { aiStyles } from "../styles/inline";
|
|
|
16
16
|
import { handleAIError } from "../utils/errorHandler";
|
|
17
17
|
import { useLB } from "../context/LBAuthProvider";
|
|
18
18
|
import { LBSigninModal } from "./LBSigninModal";
|
|
19
|
+
import { useAiContext } from "../context/AiProvider";
|
|
19
20
|
|
|
20
21
|
export interface AiTextareaProps
|
|
21
22
|
extends
|
|
@@ -25,8 +26,8 @@ export interface AiTextareaProps
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export function AiTextarea({
|
|
28
|
-
baseUrl,
|
|
29
|
-
apiKeyId,
|
|
29
|
+
baseUrl: propBaseUrl,
|
|
30
|
+
apiKeyId: propApiKeyId,
|
|
30
31
|
uiMode = "modal",
|
|
31
32
|
context,
|
|
32
33
|
model,
|
|
@@ -63,6 +64,20 @@ export function AiTextarea({
|
|
|
63
64
|
lbStatus = undefined;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
let ctxBaseUrl: string | undefined;
|
|
68
|
+
let ctxApiKeyId: string | undefined;
|
|
69
|
+
try {
|
|
70
|
+
const aiContext = useAiContext();
|
|
71
|
+
ctxBaseUrl = aiContext.baseUrl;
|
|
72
|
+
ctxApiKeyId = aiContext.apiKeyId;
|
|
73
|
+
} catch {
|
|
74
|
+
ctxBaseUrl = undefined;
|
|
75
|
+
ctxApiKeyId = undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
79
|
+
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
80
|
+
|
|
66
81
|
const { models } = useAiModels({
|
|
67
82
|
baseUrl,
|
|
68
83
|
apiKeyId,
|
|
@@ -248,6 +263,7 @@ export function AiTextarea({
|
|
|
248
263
|
baseUrl={baseUrl}
|
|
249
264
|
apiKey={apiKeyId}
|
|
250
265
|
enableModelManagement={enableModelManagement}
|
|
266
|
+
showOnlyUserModels={true}
|
|
251
267
|
/>
|
|
252
268
|
)}
|
|
253
269
|
{Boolean(toastData) && (
|