@lastbrain/ai-ui-react 1.0.65 → 1.0.67
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/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +72 -10
- package/dist/context/AiProvider.d.ts.map +1 -1
- package/dist/context/AiProvider.js +24 -0
- package/dist/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useAiModels.js +3 -4
- package/dist/hooks/useModelManagement.js +4 -3
- package/package.json +2 -2
- package/src/components/AiStatusButton.tsx +149 -24
- package/src/context/AiProvider.tsx +27 -0
- package/src/hooks/useAiModels.ts +3 -5
- package/src/hooks/useModelManagement.ts +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CAm0CrB"}
|
|
@@ -118,6 +118,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
118
118
|
(storageAllocated > 0
|
|
119
119
|
? Math.round((storageUsed / storageAllocated) * 100)
|
|
120
120
|
: 0);
|
|
121
|
+
const showFastStatusSkeleton = lbStatus === "ready" &&
|
|
122
|
+
(lbIsLoadingStatus || isLoadingStatus) &&
|
|
123
|
+
!lbBasicStatus &&
|
|
124
|
+
!effectiveStatus;
|
|
121
125
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
122
126
|
const [isHovered, setIsHovered] = useState(false);
|
|
123
127
|
const [tooltipPosition, setTooltipPosition] = useState({});
|
|
@@ -232,9 +236,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
232
236
|
lbRefreshBasicStatus,
|
|
233
237
|
lbRefreshStorageStatus,
|
|
234
238
|
]);
|
|
235
|
-
if (loading ||
|
|
236
|
-
isSelectingApiKey ||
|
|
237
|
-
((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)) {
|
|
239
|
+
if (loading || isSelectingApiKey) {
|
|
238
240
|
return (_jsx("button", { ref: buttonRef, style: {
|
|
239
241
|
...aiStyles.statusButton,
|
|
240
242
|
...aiStyles.statusButtonDisabled,
|
|
@@ -293,10 +295,40 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
293
295
|
...aiStyles.tooltip,
|
|
294
296
|
...tooltipPosition,
|
|
295
297
|
zIndex: 50,
|
|
296
|
-
}, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: lbStatus === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "
|
|
298
|
+
}, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: lbStatus === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), _jsx("div", { style: {
|
|
297
299
|
...aiStyles.tooltipSection,
|
|
298
|
-
|
|
299
|
-
}, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: aiStyles.tooltipValue, children: user.email })] }) }), _jsxs("div", { style: {
|
|
300
|
+
...aiStyles.tooltipSectionFirst,
|
|
301
|
+
}, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: aiStyles.tooltipValue, children: user.email })] }) }), showFastStatusSkeleton && (_jsxs(_Fragment, { children: [_jsxs("div", { style: aiStyles.tooltipSection, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("div", { style: {
|
|
302
|
+
height: "16px",
|
|
303
|
+
width: "110px",
|
|
304
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
305
|
+
borderRadius: "4px",
|
|
306
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
307
|
+
} })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("div", { style: {
|
|
308
|
+
height: "16px",
|
|
309
|
+
width: "48px",
|
|
310
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
311
|
+
borderRadius: "4px",
|
|
312
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
313
|
+
} })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsx("div", { style: {
|
|
314
|
+
height: "16px",
|
|
315
|
+
width: "84px",
|
|
316
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
317
|
+
borderRadius: "4px",
|
|
318
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
319
|
+
} })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("div", { style: {
|
|
320
|
+
height: "16px",
|
|
321
|
+
width: "72px",
|
|
322
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
323
|
+
borderRadius: "4px",
|
|
324
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
325
|
+
} })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsx("div", { style: {
|
|
326
|
+
height: "16px",
|
|
327
|
+
width: "120px",
|
|
328
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
329
|
+
borderRadius: "4px",
|
|
330
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
331
|
+
} })] })] })] })), _jsxs("div", { style: {
|
|
300
332
|
display: "flex",
|
|
301
333
|
gap: "8px",
|
|
302
334
|
borderTop: "1px solid var(--ai-border-primary, #374151)",
|
|
@@ -497,13 +529,43 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
497
529
|
"rgba(139, 92, 246, 0.1)";
|
|
498
530
|
e.currentTarget.style.borderColor =
|
|
499
531
|
"rgba(139, 92, 246, 0.3)";
|
|
500
|
-
}, title: "Change API Key", children: _jsx(ArrowRightLeft, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("
|
|
532
|
+
}, title: "Change API Key", children: _jsx(ArrowRightLeft, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (_jsx("div", { style: {
|
|
533
|
+
height: "16px",
|
|
534
|
+
width: "48px",
|
|
535
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
536
|
+
borderRadius: "4px",
|
|
537
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
538
|
+
} })) : (_jsx("span", { style: aiStyles.tooltipValue, children: effectiveStatus.apiKey?.env ||
|
|
501
539
|
effectiveStatus.api_key?.env ||
|
|
502
|
-
"N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }),
|
|
540
|
+
"N/A" }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), lbIsLoadingStatus &&
|
|
541
|
+
!effectiveStatus?.apiKey?.rate_limit_rpm &&
|
|
542
|
+
!effectiveStatus?.api_key?.rate_limit_rpm ? (_jsx("div", { style: {
|
|
543
|
+
height: "16px",
|
|
544
|
+
width: "92px",
|
|
545
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
546
|
+
borderRadius: "4px",
|
|
547
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
548
|
+
} })) : (_jsxs("span", { style: aiStyles.tooltipValue, children: [effectiveStatus.apiKey?.rate_limit_rpm ||
|
|
503
549
|
effectiveStatus.api_key?.rate_limit_rpm ||
|
|
504
|
-
0, " ", "req/min"] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
|
|
550
|
+
0, " ", "req/min"] }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
|
|
505
551
|
? "..."
|
|
506
|
-
: effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }),
|
|
552
|
+
: effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), lbIsLoadingStatus && !effectiveStatus?.balance ? (_jsxs("div", { style: {
|
|
553
|
+
display: "flex",
|
|
554
|
+
alignItems: "center",
|
|
555
|
+
gap: "8px",
|
|
556
|
+
}, children: [_jsx("div", { style: {
|
|
557
|
+
height: "16px",
|
|
558
|
+
width: "120px",
|
|
559
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
560
|
+
borderRadius: "4px",
|
|
561
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
562
|
+
} }), _jsx("div", { style: {
|
|
563
|
+
width: "28px",
|
|
564
|
+
height: "28px",
|
|
565
|
+
borderRadius: "50%",
|
|
566
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
567
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
568
|
+
} })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 2), " / $", formatFixed(balanceTotal, 2)] }), renderUsageCircle(balancePercentage)] }))] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), lbIsLoadingStorage ? (_jsxs("div", { style: {
|
|
507
569
|
display: "flex",
|
|
508
570
|
alignItems: "center",
|
|
509
571
|
gap: "8px",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IAErB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,eAAe,EAAE,CACf,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,KAC1C,QAAQ,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,QAAQ,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,QAAQ,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAuSjB;AAED,wBAAgB,YAAY,IAAI,cAAc,CAM7C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -3,8 +3,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useContext, useState, useEffect, useCallback, useRef, } from "react";
|
|
4
4
|
import { createClient } from "@lastbrain/ai-ui-core";
|
|
5
5
|
import { getUserModels } from "../utils/modelManagement";
|
|
6
|
+
import { useLB } from "./LBAuthProvider";
|
|
6
7
|
const AiContext = createContext(undefined);
|
|
7
8
|
export function AiProvider({ baseUrl, apiKeyId, uiMode = "modal", children, }) {
|
|
9
|
+
let lbStatus;
|
|
10
|
+
let lbSelectedKeyId;
|
|
11
|
+
try {
|
|
12
|
+
const lb = useLB();
|
|
13
|
+
lbStatus = lb.status;
|
|
14
|
+
lbSelectedKeyId = lb.selectedKey?.id;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
lbStatus = undefined;
|
|
18
|
+
lbSelectedKeyId = undefined;
|
|
19
|
+
}
|
|
8
20
|
const [providers, setProviders] = useState([]);
|
|
9
21
|
const [allModels, setAllModels] = useState([]);
|
|
10
22
|
const [availableModels, setAvailableModels] = useState([]);
|
|
@@ -188,6 +200,18 @@ export function AiProvider({ baseUrl, apiKeyId, uiMode = "modal", children, }) {
|
|
|
188
200
|
fetchProviders(); // Fetch providers + available models en même temps
|
|
189
201
|
fetchUserModels();
|
|
190
202
|
}, [fetchProviders, fetchUserModels]);
|
|
203
|
+
// Après authentification (ex: lb_session), forcer un refetch propre des modèles.
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (lbStatus !== "ready") {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
hasFetchedProviders.current = false;
|
|
209
|
+
hasFetchedUserModels.current = false;
|
|
210
|
+
providersAvailable.current = true;
|
|
211
|
+
userModelsAvailable.current = true;
|
|
212
|
+
fetchProviders();
|
|
213
|
+
fetchUserModels();
|
|
214
|
+
}, [lbStatus, lbSelectedKeyId, fetchProviders, fetchUserModels]);
|
|
191
215
|
// Helpers pour filtrer les modèles par type
|
|
192
216
|
const getModelsByType = useCallback((type) => {
|
|
193
217
|
const filtered = allModels.filter((model) => model.type === type);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAiModels.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiModels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"useAiModels.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiModels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA4F3E"}
|
|
@@ -7,10 +7,9 @@ import { useAiContext } from "../context/AiProvider";
|
|
|
7
7
|
*/
|
|
8
8
|
export function useAiModels(options) {
|
|
9
9
|
const context = useAiContext();
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
const useContextData =
|
|
13
|
-
(!options?.apiKeyId || options.apiKeyId === context.apiKeyId);
|
|
10
|
+
// Utiliser le contexte dès que la base URL correspond.
|
|
11
|
+
// L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
|
|
12
|
+
const useContextData = !options?.baseUrl || options.baseUrl === context.baseUrl;
|
|
14
13
|
// Filtrer les modèles selon le type demandé
|
|
15
14
|
const filteredModels = useMemo(() => {
|
|
16
15
|
console.log("[useAiModels] Filtering models:", {
|
|
@@ -10,9 +10,10 @@ export function useModelManagement(options = {}) {
|
|
|
10
10
|
const context = useAiContext();
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
12
|
const [error, setError] = useState(null);
|
|
13
|
-
// Utiliser les données du contexte si
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// Utiliser les données du contexte si la base URL correspond.
|
|
14
|
+
// L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
|
|
15
|
+
// devoir casser le cache/provider de modèles déjà présent.
|
|
16
|
+
const useContextData = !options.baseUrl || options.baseUrl === context.baseUrl;
|
|
16
17
|
// Filtrer par catégorie si nécessaire
|
|
17
18
|
const filteredModels = useMemo(() => {
|
|
18
19
|
if (!useContextData)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/ai-ui-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.67",
|
|
4
4
|
"description": "Headless React components for LastBrain AI UI Kit",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"lucide-react": "^0.257.0",
|
|
51
|
-
"@lastbrain/ai-ui-core": "1.0.
|
|
51
|
+
"@lastbrain/ai-ui-core": "1.0.51"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/react": "^19.2.0",
|
|
@@ -207,6 +207,11 @@ export function AiStatusButton({
|
|
|
207
207
|
(storageAllocated > 0
|
|
208
208
|
? Math.round((storageUsed / storageAllocated) * 100)
|
|
209
209
|
: 0);
|
|
210
|
+
const showFastStatusSkeleton =
|
|
211
|
+
lbStatus === "ready" &&
|
|
212
|
+
(lbIsLoadingStatus || isLoadingStatus) &&
|
|
213
|
+
!lbBasicStatus &&
|
|
214
|
+
!effectiveStatus;
|
|
210
215
|
|
|
211
216
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
212
217
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -345,11 +350,7 @@ export function AiStatusButton({
|
|
|
345
350
|
lbRefreshStorageStatus,
|
|
346
351
|
]);
|
|
347
352
|
|
|
348
|
-
if (
|
|
349
|
-
loading ||
|
|
350
|
-
isSelectingApiKey ||
|
|
351
|
-
((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)
|
|
352
|
-
) {
|
|
353
|
+
if (loading || isSelectingApiKey) {
|
|
353
354
|
return (
|
|
354
355
|
<button
|
|
355
356
|
ref={buttonRef}
|
|
@@ -518,13 +519,11 @@ export function AiStatusButton({
|
|
|
518
519
|
>
|
|
519
520
|
{lbStatus === "ready" && user ? (
|
|
520
521
|
<>
|
|
521
|
-
<div style={aiStyles.tooltipHeader}>
|
|
522
|
-
LastBrain Connected
|
|
523
|
-
</div>
|
|
522
|
+
<div style={aiStyles.tooltipHeader}>API Status</div>
|
|
524
523
|
<div
|
|
525
524
|
style={{
|
|
526
525
|
...aiStyles.tooltipSection,
|
|
527
|
-
|
|
526
|
+
...aiStyles.tooltipSectionFirst,
|
|
528
527
|
}}
|
|
529
528
|
>
|
|
530
529
|
<div style={aiStyles.tooltipRow}>
|
|
@@ -532,6 +531,75 @@ export function AiStatusButton({
|
|
|
532
531
|
<span style={aiStyles.tooltipValue}>{user.email}</span>
|
|
533
532
|
</div>
|
|
534
533
|
</div>
|
|
534
|
+
{showFastStatusSkeleton && (
|
|
535
|
+
<>
|
|
536
|
+
<div style={aiStyles.tooltipSection}>
|
|
537
|
+
<div style={aiStyles.tooltipRow}>
|
|
538
|
+
<span style={aiStyles.tooltipLabel}>API Key:</span>
|
|
539
|
+
<div
|
|
540
|
+
style={{
|
|
541
|
+
height: "16px",
|
|
542
|
+
width: "110px",
|
|
543
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
544
|
+
borderRadius: "4px",
|
|
545
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
546
|
+
}}
|
|
547
|
+
/>
|
|
548
|
+
</div>
|
|
549
|
+
<div style={aiStyles.tooltipRow}>
|
|
550
|
+
<span style={aiStyles.tooltipLabel}>Env:</span>
|
|
551
|
+
<div
|
|
552
|
+
style={{
|
|
553
|
+
height: "16px",
|
|
554
|
+
width: "48px",
|
|
555
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
556
|
+
borderRadius: "4px",
|
|
557
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
558
|
+
}}
|
|
559
|
+
/>
|
|
560
|
+
</div>
|
|
561
|
+
<div style={aiStyles.tooltipRow}>
|
|
562
|
+
<span style={aiStyles.tooltipLabel}>Rate Limit:</span>
|
|
563
|
+
<div
|
|
564
|
+
style={{
|
|
565
|
+
height: "16px",
|
|
566
|
+
width: "84px",
|
|
567
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
568
|
+
borderRadius: "4px",
|
|
569
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
570
|
+
}}
|
|
571
|
+
/>
|
|
572
|
+
</div>
|
|
573
|
+
<div style={aiStyles.tooltipRow}>
|
|
574
|
+
<span style={aiStyles.tooltipLabel}>Auth:</span>
|
|
575
|
+
<div
|
|
576
|
+
style={{
|
|
577
|
+
height: "16px",
|
|
578
|
+
width: "72px",
|
|
579
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
580
|
+
borderRadius: "4px",
|
|
581
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
582
|
+
}}
|
|
583
|
+
/>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
<div style={aiStyles.tooltipSection}>
|
|
587
|
+
<div style={aiStyles.tooltipSubtitle}>Wallet</div>
|
|
588
|
+
<div style={aiStyles.tooltipRow}>
|
|
589
|
+
<span style={aiStyles.tooltipLabel}>Total:</span>
|
|
590
|
+
<div
|
|
591
|
+
style={{
|
|
592
|
+
height: "16px",
|
|
593
|
+
width: "120px",
|
|
594
|
+
background: "rgba(139, 92, 246, 0.12)",
|
|
595
|
+
borderRadius: "4px",
|
|
596
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
597
|
+
}}
|
|
598
|
+
/>
|
|
599
|
+
</div>
|
|
600
|
+
</div>
|
|
601
|
+
</>
|
|
602
|
+
)}
|
|
535
603
|
<div
|
|
536
604
|
style={{
|
|
537
605
|
display: "flex",
|
|
@@ -919,20 +987,46 @@ export function AiStatusButton({
|
|
|
919
987
|
</div>
|
|
920
988
|
<div style={aiStyles.tooltipRow}>
|
|
921
989
|
<span style={aiStyles.tooltipLabel}>Env:</span>
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
990
|
+
{lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (
|
|
991
|
+
<div
|
|
992
|
+
style={{
|
|
993
|
+
height: "16px",
|
|
994
|
+
width: "48px",
|
|
995
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
996
|
+
borderRadius: "4px",
|
|
997
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
998
|
+
}}
|
|
999
|
+
/>
|
|
1000
|
+
) : (
|
|
1001
|
+
<span style={aiStyles.tooltipValue}>
|
|
1002
|
+
{effectiveStatus.apiKey?.env ||
|
|
1003
|
+
effectiveStatus.api_key?.env ||
|
|
1004
|
+
"N/A"}
|
|
1005
|
+
</span>
|
|
1006
|
+
)}
|
|
927
1007
|
</div>
|
|
928
1008
|
<div style={aiStyles.tooltipRow}>
|
|
929
1009
|
<span style={aiStyles.tooltipLabel}>Rate Limit:</span>
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1010
|
+
{lbIsLoadingStatus &&
|
|
1011
|
+
!effectiveStatus?.apiKey?.rate_limit_rpm &&
|
|
1012
|
+
!effectiveStatus?.api_key?.rate_limit_rpm ? (
|
|
1013
|
+
<div
|
|
1014
|
+
style={{
|
|
1015
|
+
height: "16px",
|
|
1016
|
+
width: "92px",
|
|
1017
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
1018
|
+
borderRadius: "4px",
|
|
1019
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
1020
|
+
}}
|
|
1021
|
+
/>
|
|
1022
|
+
) : (
|
|
1023
|
+
<span style={aiStyles.tooltipValue}>
|
|
1024
|
+
{effectiveStatus.apiKey?.rate_limit_rpm ||
|
|
1025
|
+
effectiveStatus.api_key?.rate_limit_rpm ||
|
|
1026
|
+
0}{" "}
|
|
1027
|
+
req/min
|
|
1028
|
+
</span>
|
|
1029
|
+
)}
|
|
936
1030
|
</div>
|
|
937
1031
|
<div style={aiStyles.tooltipRow}>
|
|
938
1032
|
<span style={aiStyles.tooltipLabel}>Auth:</span>
|
|
@@ -948,10 +1042,41 @@ export function AiStatusButton({
|
|
|
948
1042
|
<div style={aiStyles.tooltipSubtitle}>Wallet</div>
|
|
949
1043
|
<div style={aiStyles.tooltipRow}>
|
|
950
1044
|
<span style={aiStyles.tooltipLabel}>Total:</span>
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1045
|
+
{lbIsLoadingStatus && !effectiveStatus?.balance ? (
|
|
1046
|
+
<div
|
|
1047
|
+
style={{
|
|
1048
|
+
display: "flex",
|
|
1049
|
+
alignItems: "center",
|
|
1050
|
+
gap: "8px",
|
|
1051
|
+
}}
|
|
1052
|
+
>
|
|
1053
|
+
<div
|
|
1054
|
+
style={{
|
|
1055
|
+
height: "16px",
|
|
1056
|
+
width: "120px",
|
|
1057
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
1058
|
+
borderRadius: "4px",
|
|
1059
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
1060
|
+
}}
|
|
1061
|
+
/>
|
|
1062
|
+
<div
|
|
1063
|
+
style={{
|
|
1064
|
+
width: "28px",
|
|
1065
|
+
height: "28px",
|
|
1066
|
+
borderRadius: "50%",
|
|
1067
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
1068
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
1069
|
+
}}
|
|
1070
|
+
/>
|
|
1071
|
+
</div>
|
|
1072
|
+
) : (
|
|
1073
|
+
<>
|
|
1074
|
+
<span style={aiStyles.tooltipValue}>
|
|
1075
|
+
${formatFixed(balanceUsed, 2)} / ${formatFixed(balanceTotal, 2)}
|
|
1076
|
+
</span>
|
|
1077
|
+
{renderUsageCircle(balancePercentage)}
|
|
1078
|
+
</>
|
|
1079
|
+
)}
|
|
955
1080
|
</div>
|
|
956
1081
|
</div>
|
|
957
1082
|
|
|
@@ -13,6 +13,7 @@ import type { UiMode } from "../types";
|
|
|
13
13
|
import type { ModelRef } from "@lastbrain/ai-ui-core";
|
|
14
14
|
import { createClient } from "@lastbrain/ai-ui-core";
|
|
15
15
|
import { getUserModels } from "../utils/modelManagement";
|
|
16
|
+
import { useLB } from "./LBAuthProvider";
|
|
16
17
|
|
|
17
18
|
export interface AIModel {
|
|
18
19
|
id: string;
|
|
@@ -70,6 +71,17 @@ export function AiProvider({
|
|
|
70
71
|
uiMode = "modal",
|
|
71
72
|
children,
|
|
72
73
|
}: AiProviderProps) {
|
|
74
|
+
let lbStatus: string | undefined;
|
|
75
|
+
let lbSelectedKeyId: string | undefined;
|
|
76
|
+
try {
|
|
77
|
+
const lb = useLB();
|
|
78
|
+
lbStatus = lb.status;
|
|
79
|
+
lbSelectedKeyId = lb.selectedKey?.id;
|
|
80
|
+
} catch {
|
|
81
|
+
lbStatus = undefined;
|
|
82
|
+
lbSelectedKeyId = undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
const [providers, setProviders] = useState<ProviderData[]>([]);
|
|
74
86
|
const [allModels, setAllModels] = useState<ModelRef[]>([]);
|
|
75
87
|
const [availableModels, setAvailableModels] = useState<AIModel[]>([]);
|
|
@@ -289,6 +301,21 @@ export function AiProvider({
|
|
|
289
301
|
fetchUserModels();
|
|
290
302
|
}, [fetchProviders, fetchUserModels]);
|
|
291
303
|
|
|
304
|
+
// Après authentification (ex: lb_session), forcer un refetch propre des modèles.
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
if (lbStatus !== "ready") {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
hasFetchedProviders.current = false;
|
|
311
|
+
hasFetchedUserModels.current = false;
|
|
312
|
+
providersAvailable.current = true;
|
|
313
|
+
userModelsAvailable.current = true;
|
|
314
|
+
|
|
315
|
+
fetchProviders();
|
|
316
|
+
fetchUserModels();
|
|
317
|
+
}, [lbStatus, lbSelectedKeyId, fetchProviders, fetchUserModels]);
|
|
318
|
+
|
|
292
319
|
// Helpers pour filtrer les modèles par type
|
|
293
320
|
const getModelsByType = useCallback(
|
|
294
321
|
(type: "text" | "language" | "image" | "embed") => {
|
package/src/hooks/useAiModels.ts
CHANGED
|
@@ -24,11 +24,9 @@ export interface UseAiModelsResult {
|
|
|
24
24
|
export function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult {
|
|
25
25
|
const context = useAiContext();
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
const useContextData =
|
|
30
|
-
(!options?.baseUrl || options.baseUrl === context.baseUrl) &&
|
|
31
|
-
(!options?.apiKeyId || options.apiKeyId === context.apiKeyId);
|
|
27
|
+
// Utiliser le contexte dès que la base URL correspond.
|
|
28
|
+
// L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
|
|
29
|
+
const useContextData = !options?.baseUrl || options.baseUrl === context.baseUrl;
|
|
32
30
|
|
|
33
31
|
// Filtrer les modèles selon le type demandé
|
|
34
32
|
const filteredModels = useMemo(() => {
|
|
@@ -41,10 +41,10 @@ export function useModelManagement(
|
|
|
41
41
|
const [loading, setLoading] = useState(false);
|
|
42
42
|
const [error, setError] = useState<string | null>(null);
|
|
43
43
|
|
|
44
|
-
// Utiliser les données du contexte si
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
// Utiliser les données du contexte si la base URL correspond.
|
|
45
|
+
// L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
|
|
46
|
+
// devoir casser le cache/provider de modèles déjà présent.
|
|
47
|
+
const useContextData = !options.baseUrl || options.baseUrl === context.baseUrl;
|
|
48
48
|
|
|
49
49
|
// Filtrer par catégorie si nécessaire
|
|
50
50
|
const filteredModels = useMemo(() => {
|