@lastbrain/ai-ui-react 1.0.64 → 1.0.66
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 +21 -4
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +72 -8
- 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 +15 -1
- package/dist/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useAiModels.js +3 -4
- package/dist/hooks/useModelManagement.js +4 -3
- 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 +12 -3
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +2 -0
- package/src/components/AiInput.tsx +18 -2
- package/src/components/AiPromptPanel.tsx +25 -3
- package/src/components/AiStatusButton.tsx +149 -20
- package/src/components/AiTextarea.tsx +18 -2
- package/src/context/LBAuthProvider.tsx +16 -1
- package/src/hooks/useAiModels.ts +3 -5
- package/src/hooks/useModelManagement.ts +4 -4
- package/src/hooks/usePrompts.ts +11 -4
- package/src/utils/modelManagement.ts +11 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAW/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAgClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,QAAQ,EACR,WAAoE,EACpE,OAAO,EACP,QAAQ,EACR,eAAuB,EACvB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,GACvB,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAW/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAgClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,QAAQ,EACR,WAAoE,EACpE,OAAO,EACP,QAAQ,EACR,eAAuB,EACvB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,GACvB,EAAE,gBAAgB,2CA+NlB"}
|
|
@@ -182,5 +182,5 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
|
|
|
182
182
|
color: "currentColor",
|
|
183
183
|
opacity: 0.7,
|
|
184
184
|
}, title: "Supprimer", children: _jsx(X, { size: 14 }) })] }, index));
|
|
185
|
-
}) })), _jsx(AiPromptPanel, { isOpen: showPromptPanel, onClose: () => setShowPromptPanel(false), onSubmit: handlePromptSubmit, models: models || undefined, baseUrl: baseUrl, sourceText: context ? `Contexte: ${context}` : undefined }), _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
|
|
185
|
+
}) })), _jsx(AiPromptPanel, { isOpen: showPromptPanel, onClose: () => setShowPromptPanel(false), onSubmit: handlePromptSubmit, models: models || undefined, baseUrl: baseUrl, sourceText: context ? `Contexte: ${context}` : undefined, enableModelManagement: true, showOnlyUserModels: true }), _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
|
|
186
186
|
}
|
|
@@ -4,5 +4,5 @@ export interface AiInputProps extends Omit<BaseAiProps, "type">, Omit<InputHTMLA
|
|
|
4
4
|
uiMode?: "modal" | "drawer";
|
|
5
5
|
enableModelManagement?: boolean;
|
|
6
6
|
}
|
|
7
|
-
export declare function AiInput({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
//# sourceMappingURL=AiInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW5C,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CAwOd"}
|
|
@@ -10,7 +10,8 @@ import { aiStyles } from "../styles/inline";
|
|
|
10
10
|
import { handleAIError } from "../utils/errorHandler";
|
|
11
11
|
import { useLB } from "../context/LBAuthProvider";
|
|
12
12
|
import { LBSigninModal } from "./LBSigninModal";
|
|
13
|
-
|
|
13
|
+
import { useAiContext } from "../context/AiProvider";
|
|
14
|
+
export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement = true, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
|
|
14
15
|
const [isOpen, setIsOpen] = useState(false);
|
|
15
16
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
16
17
|
const [inputValue, setInputValue] = useState(inputProps.value?.toString() || inputProps.defaultValue?.toString() || "");
|
|
@@ -28,6 +29,19 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
|
|
|
28
29
|
// LBProvider n'est pas disponible, ignorer
|
|
29
30
|
lbStatus = undefined;
|
|
30
31
|
}
|
|
32
|
+
let ctxBaseUrl;
|
|
33
|
+
let ctxApiKeyId;
|
|
34
|
+
try {
|
|
35
|
+
const aiContext = useAiContext();
|
|
36
|
+
ctxBaseUrl = aiContext.baseUrl;
|
|
37
|
+
ctxApiKeyId = aiContext.apiKeyId;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
ctxBaseUrl = undefined;
|
|
41
|
+
ctxApiKeyId = undefined;
|
|
42
|
+
}
|
|
43
|
+
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
44
|
+
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
31
45
|
const { models } = useAiModels({
|
|
32
46
|
baseUrl,
|
|
33
47
|
apiKeyId,
|
|
@@ -132,5 +146,5 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
|
|
|
132
146
|
? "Authentication required"
|
|
133
147
|
: hasConfiguration
|
|
134
148
|
? "Generate with AI"
|
|
135
|
-
: "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: inputValue || undefined, apiKey: apiKeyId, baseUrl: baseUrl, enableModelManagement: enableModelManagement })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
|
|
149
|
+
: "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: inputValue || undefined, apiKey: apiKeyId, baseUrl: baseUrl, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
|
|
136
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CA0BtD"}
|
|
@@ -6,17 +6,29 @@ import { aiStyles } from "../styles/inline";
|
|
|
6
6
|
import { handleAIError } from "../utils/errorHandler";
|
|
7
7
|
import { usePrompts, } from "../hooks/usePrompts";
|
|
8
8
|
import { useModelManagement } from "../hooks/useModelManagement";
|
|
9
|
-
import { AiProvider } from "../context/AiProvider";
|
|
9
|
+
import { AiProvider, useAiContext } from "../context/AiProvider";
|
|
10
10
|
export function AiPromptPanel(props) {
|
|
11
11
|
const { apiKey, baseUrl } = props;
|
|
12
|
-
|
|
12
|
+
let hasContext = false;
|
|
13
|
+
try {
|
|
14
|
+
useAiContext();
|
|
15
|
+
hasContext = true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
hasContext = false;
|
|
19
|
+
}
|
|
20
|
+
// Si un contexte existe déjà, ne pas re-wrapper (évite les refetch multiples).
|
|
21
|
+
if (hasContext) {
|
|
22
|
+
return _jsx(AiPromptPanelInternal, { ...props });
|
|
23
|
+
}
|
|
24
|
+
// Sinon, si apiKey/baseUrl sont fournis, wrapper avec AiProvider
|
|
13
25
|
if (apiKey || baseUrl) {
|
|
14
26
|
return (_jsx(AiProvider, { baseUrl: baseUrl || "", apiKeyId: apiKey || "", children: _jsx(AiPromptPanelInternal, { ...props }) }));
|
|
15
27
|
}
|
|
16
28
|
// Sinon, utiliser le contexte existant
|
|
17
29
|
return _jsx(AiPromptPanelInternal, { ...props });
|
|
18
30
|
}
|
|
19
|
-
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, }) {
|
|
31
|
+
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, showOnlyUserModels = false, }) {
|
|
20
32
|
const [selectedModel, setSelectedModel] = useState("");
|
|
21
33
|
const [prompt, setPrompt] = useState("");
|
|
22
34
|
const [promptId, setPromptId] = useState(undefined);
|
|
@@ -90,6 +102,9 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
90
102
|
}));
|
|
91
103
|
}
|
|
92
104
|
const categoryModels = effectiveAvailableModels.filter((m) => m.category === modelCategory);
|
|
105
|
+
if (!showAllModels && showOnlyUserModels) {
|
|
106
|
+
return categoryModels.filter((m) => effectiveUserModels.includes(m.id));
|
|
107
|
+
}
|
|
93
108
|
if (showAllModels) {
|
|
94
109
|
return categoryModels;
|
|
95
110
|
}
|
|
@@ -350,7 +365,9 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
350
365
|
!effectiveUserModels.includes(m.id)).length] }))] }))] }), _jsxs("select", { id: "model-select", value: activeModelId, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
351
366
|
...aiStyles.select,
|
|
352
367
|
...(modelFocused && aiStyles.selectFocus),
|
|
353
|
-
}, children: [modelOptions.length === 0 && (_jsx("option", { value: "", children:
|
|
368
|
+
}, children: [modelOptions.length === 0 && (_jsx("option", { value: "", children: showOnlyUserModels
|
|
369
|
+
? "No active models. Open 'Gérer les modèles'."
|
|
370
|
+
: "Loading models..." })), modelOptions.map((model) => {
|
|
354
371
|
const isActive = effectiveUserModels.includes(model.id);
|
|
355
372
|
return (_jsxs("option", { value: model.id, style: {
|
|
356
373
|
opacity: showAllModels && !isActive ? 0.6 : 1,
|
|
@@ -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,7 +236,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
232
236
|
lbRefreshBasicStatus,
|
|
233
237
|
lbRefreshStorageStatus,
|
|
234
238
|
]);
|
|
235
|
-
if (loading || isSelectingApiKey
|
|
239
|
+
if (loading || isSelectingApiKey) {
|
|
236
240
|
return (_jsx("button", { ref: buttonRef, style: {
|
|
237
241
|
...aiStyles.statusButton,
|
|
238
242
|
...aiStyles.statusButtonDisabled,
|
|
@@ -291,10 +295,40 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
291
295
|
...aiStyles.tooltip,
|
|
292
296
|
...tooltipPosition,
|
|
293
297
|
zIndex: 50,
|
|
294
|
-
}, 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: {
|
|
295
299
|
...aiStyles.tooltipSection,
|
|
296
|
-
|
|
297
|
-
}, 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: {
|
|
298
332
|
display: "flex",
|
|
299
333
|
gap: "8px",
|
|
300
334
|
borderTop: "1px solid var(--ai-border-primary, #374151)",
|
|
@@ -495,13 +529,43 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
495
529
|
"rgba(139, 92, 246, 0.1)";
|
|
496
530
|
e.currentTarget.style.borderColor =
|
|
497
531
|
"rgba(139, 92, 246, 0.3)";
|
|
498
|
-
}, 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 ||
|
|
499
539
|
effectiveStatus.api_key?.env ||
|
|
500
|
-
"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 ||
|
|
501
549
|
effectiveStatus.api_key?.rate_limit_rpm ||
|
|
502
|
-
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
|
|
503
551
|
? "..."
|
|
504
|
-
: 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: {
|
|
505
569
|
display: "flex",
|
|
506
570
|
alignItems: "center",
|
|
507
571
|
gap: "8px",
|
|
@@ -3,5 +3,5 @@ import type { BaseAiProps } from "../types";
|
|
|
3
3
|
export interface AiTextareaProps extends Omit<BaseAiProps, "type">, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "onValue"> {
|
|
4
4
|
uiMode?: "modal" | "drawer";
|
|
5
5
|
}
|
|
6
|
-
export declare function AiTextarea({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
//# sourceMappingURL=AiTextarea.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW5C,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CA+OjB"}
|
|
@@ -10,7 +10,8 @@ import { aiStyles } from "../styles/inline";
|
|
|
10
10
|
import { handleAIError } from "../utils/errorHandler";
|
|
11
11
|
import { useLB } from "../context/LBAuthProvider";
|
|
12
12
|
import { LBSigninModal } from "./LBSigninModal";
|
|
13
|
-
|
|
13
|
+
import { useAiContext } from "../context/AiProvider";
|
|
14
|
+
export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
|
|
14
15
|
const [isOpen, setIsOpen] = useState(false);
|
|
15
16
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
16
17
|
const [textareaValue, setTextareaValue] = useState(textareaProps.value?.toString() ||
|
|
@@ -30,6 +31,19 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
|
|
|
30
31
|
// LBProvider n'est pas disponible, ignorer
|
|
31
32
|
lbStatus = undefined;
|
|
32
33
|
}
|
|
34
|
+
let ctxBaseUrl;
|
|
35
|
+
let ctxApiKeyId;
|
|
36
|
+
try {
|
|
37
|
+
const aiContext = useAiContext();
|
|
38
|
+
ctxBaseUrl = aiContext.baseUrl;
|
|
39
|
+
ctxApiKeyId = aiContext.apiKeyId;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
ctxBaseUrl = undefined;
|
|
43
|
+
ctxApiKeyId = undefined;
|
|
44
|
+
}
|
|
45
|
+
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
46
|
+
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
33
47
|
const { models } = useAiModels({
|
|
34
48
|
baseUrl,
|
|
35
49
|
apiKeyId,
|
|
@@ -146,5 +160,5 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
|
|
|
146
160
|
? "Authentication required"
|
|
147
161
|
: hasConfiguration
|
|
148
162
|
? "Generate with AI"
|
|
149
|
-
: "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
|
|
163
|
+
: "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
|
|
150
164
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAG/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,mEAAmE;IACnE,WAAW,EAAE,GAAG,CAAC;IACjB,uCAAuC;IACvC,aAAa,EAAE,GAAG,CAAC;IACnB,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kEAAkE;IAClE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAG/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,mEAAmE;IACnE,WAAW,EAAE,GAAG,CAAC;IACjB,uCAAuC;IACvC,aAAa,EAAE,GAAG,CAAC;IACnB,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kEAAkE;IAClE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CAsgBjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
|
|
@@ -333,7 +333,20 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
333
333
|
*/
|
|
334
334
|
const switchApiKey = useCallback(async (apiKeyId) => {
|
|
335
335
|
if (state.status === "ready" || accessToken) {
|
|
336
|
-
|
|
336
|
+
// lb_session / login token flow: persist selection server-side when possible.
|
|
337
|
+
// api_key flow may legitimately reject this route; we still support local selection
|
|
338
|
+
// via x-lb-api-key-selected header on subsequent requests.
|
|
339
|
+
if (accessToken || state.session?.sessionToken) {
|
|
340
|
+
await lbClient.selectApiKey(apiKeyId, accessToken);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
try {
|
|
344
|
+
await lbClient.selectApiKey(apiKeyId, accessToken);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
console.warn("[LBProvider] selectApiKey API call failed, applying local switch only", error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
337
350
|
const selectedKey = apiKeys.find((key) => key.id === apiKeyId);
|
|
338
351
|
if (selectedKey) {
|
|
339
352
|
setState((prev) => ({
|
|
@@ -349,6 +362,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
349
362
|
}
|
|
350
363
|
}, [
|
|
351
364
|
state.status,
|
|
365
|
+
state.session?.sessionToken,
|
|
352
366
|
accessToken,
|
|
353
367
|
apiKeys,
|
|
354
368
|
lbClient,
|
|
@@ -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)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePrompts.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrompts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,YAAY,EAAE,CACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"usePrompts.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrompts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,YAAY,EAAE,CACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,CAuS7C"}
|
package/dist/hooks/usePrompts.js
CHANGED
|
@@ -7,6 +7,14 @@ export function usePrompts() {
|
|
|
7
7
|
const [prompts, setPrompts] = useState([]);
|
|
8
8
|
const [loading, setLoading] = useState(false);
|
|
9
9
|
const [error, setError] = useState(null);
|
|
10
|
+
const isAuthTokenCandidate = (value) => {
|
|
11
|
+
if (!value)
|
|
12
|
+
return false;
|
|
13
|
+
const token = value.trim();
|
|
14
|
+
if (!token)
|
|
15
|
+
return false;
|
|
16
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
17
|
+
};
|
|
10
18
|
const fetchPrompts = useCallback(async (options) => {
|
|
11
19
|
try {
|
|
12
20
|
setLoading(true);
|
|
@@ -71,7 +79,7 @@ export function usePrompts() {
|
|
|
71
79
|
console.log("[usePrompts] Fetching prompts from:", endpoint);
|
|
72
80
|
const headers = {};
|
|
73
81
|
// Ajouter l'API key pour les appels publics directs (pas de proxy externe ni auth interne)
|
|
74
|
-
if (isPublicApi && apiKeyId) {
|
|
82
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
75
83
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
76
84
|
}
|
|
77
85
|
const response = await fetch(endpoint, {
|
|
@@ -110,7 +118,7 @@ export function usePrompts() {
|
|
|
110
118
|
? `${baseUrl}/auth/prompts` // Proxy ajoutera /api/ai/
|
|
111
119
|
: "/api/ai/auth/prompts";
|
|
112
120
|
const headers = { "Content-Type": "application/json" };
|
|
113
|
-
if (!isExternalProxy && apiKeyId) {
|
|
121
|
+
if (!isExternalProxy && isAuthTokenCandidate(apiKeyId)) {
|
|
114
122
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
115
123
|
}
|
|
116
124
|
const response = await fetch(endpoint, {
|
|
@@ -148,7 +156,7 @@ export function usePrompts() {
|
|
|
148
156
|
? `${baseUrl}/auth/prompts`
|
|
149
157
|
: "/api/ai/auth/prompts";
|
|
150
158
|
const headers = { "Content-Type": "application/json" };
|
|
151
|
-
if (isPublicApi && apiKeyId) {
|
|
159
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
152
160
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
153
161
|
}
|
|
154
162
|
const response = await fetch(endpoint, {
|
|
@@ -186,7 +194,7 @@ export function usePrompts() {
|
|
|
186
194
|
? `${baseUrl}/auth/prompts?id=${id}`
|
|
187
195
|
: `/api/ai/auth/prompts?id=${id}`;
|
|
188
196
|
const headers = {};
|
|
189
|
-
if (isPublicApi && apiKeyId) {
|
|
197
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
190
198
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
191
199
|
}
|
|
192
200
|
const response = await fetch(endpoint, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CACR,KAAK,CAAC;IACJ,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,CAAC,CACH,CAsDA;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CAkDnB"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilitaires pour la gestion des modèles IA
|
|
3
3
|
*/
|
|
4
|
+
function isAuthTokenCandidate(value) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return false;
|
|
7
|
+
const token = value.trim();
|
|
8
|
+
if (!token)
|
|
9
|
+
return false;
|
|
10
|
+
// Raw API key or JWT access token
|
|
11
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
12
|
+
}
|
|
4
13
|
/**
|
|
5
14
|
* Active ou désactive un modèle pour l'utilisateur courant
|
|
6
15
|
*/
|
|
@@ -10,7 +19,7 @@ export async function toggleUserModel(modelId, isActive, options = {}) {
|
|
|
10
19
|
"Content-Type": "application/json",
|
|
11
20
|
};
|
|
12
21
|
// Ajouter la clé API si fournie
|
|
13
|
-
if (apiKey) {
|
|
22
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
14
23
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
15
24
|
}
|
|
16
25
|
const isPublicApi = baseUrl && baseUrl.includes("/api/public/v1");
|
|
@@ -54,7 +63,7 @@ export async function getAvailableModels(options = {}) {
|
|
|
54
63
|
console.log("[getAvailableModels] isExternalProxy:", isExternalProxy, "isPublicApi:", isPublicApi, "endpoint:", endpoint);
|
|
55
64
|
const headers = {};
|
|
56
65
|
// Ajouter la clé API pour les appels publics directs
|
|
57
|
-
if (isPublicApi && apiKey) {
|
|
66
|
+
if (isPublicApi && isAuthTokenCandidate(apiKey)) {
|
|
58
67
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
59
68
|
}
|
|
60
69
|
const response = await fetch(endpoint, {
|
|
@@ -92,7 +101,7 @@ export async function getUserModels(options = {}) {
|
|
|
92
101
|
console.log("[getUserModels] isExternalProxy:", isExternalProxy, "isPublicApi:", isPublicApi, "endpoint:", endpoint);
|
|
93
102
|
const headers = {};
|
|
94
103
|
// Ajouter la clé API pour tous les types d'appels si disponible
|
|
95
|
-
if (apiKey) {
|
|
104
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
96
105
|
if (isPublicApi) {
|
|
97
106
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
98
107
|
}
|
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.66",
|
|
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.50"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/react": "^19.2.0",
|
|
@@ -296,6 +296,8 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
|
|
|
296
296
|
models={models || undefined}
|
|
297
297
|
baseUrl={baseUrl}
|
|
298
298
|
sourceText={context ? `Contexte: ${context}` : undefined}
|
|
299
|
+
enableModelManagement={true}
|
|
300
|
+
showOnlyUserModels={true}
|
|
299
301
|
/>
|
|
300
302
|
|
|
301
303
|
{/* Modal signin pour les utilisateurs non connectés */}
|
|
@@ -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,6 +194,10 @@ 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
203
|
}
|
|
@@ -582,7 +600,11 @@ function AiPromptPanelInternal({
|
|
|
582
600
|
}}
|
|
583
601
|
>
|
|
584
602
|
{modelOptions.length === 0 && (
|
|
585
|
-
<option value="">
|
|
603
|
+
<option value="">
|
|
604
|
+
{showOnlyUserModels
|
|
605
|
+
? "No active models. Open 'Gérer les modèles'."
|
|
606
|
+
: "Loading models..."}
|
|
607
|
+
</option>
|
|
586
608
|
)}
|
|
587
609
|
{modelOptions.map((model) => {
|
|
588
610
|
const isActive = effectiveUserModels.includes(model.id);
|
|
@@ -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,7 +350,7 @@ export function AiStatusButton({
|
|
|
345
350
|
lbRefreshStorageStatus,
|
|
346
351
|
]);
|
|
347
352
|
|
|
348
|
-
if (loading || isSelectingApiKey
|
|
353
|
+
if (loading || isSelectingApiKey) {
|
|
349
354
|
return (
|
|
350
355
|
<button
|
|
351
356
|
ref={buttonRef}
|
|
@@ -514,13 +519,11 @@ export function AiStatusButton({
|
|
|
514
519
|
>
|
|
515
520
|
{lbStatus === "ready" && user ? (
|
|
516
521
|
<>
|
|
517
|
-
<div style={aiStyles.tooltipHeader}>
|
|
518
|
-
LastBrain Connected
|
|
519
|
-
</div>
|
|
522
|
+
<div style={aiStyles.tooltipHeader}>API Status</div>
|
|
520
523
|
<div
|
|
521
524
|
style={{
|
|
522
525
|
...aiStyles.tooltipSection,
|
|
523
|
-
|
|
526
|
+
...aiStyles.tooltipSectionFirst,
|
|
524
527
|
}}
|
|
525
528
|
>
|
|
526
529
|
<div style={aiStyles.tooltipRow}>
|
|
@@ -528,6 +531,75 @@ export function AiStatusButton({
|
|
|
528
531
|
<span style={aiStyles.tooltipValue}>{user.email}</span>
|
|
529
532
|
</div>
|
|
530
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
|
+
)}
|
|
531
603
|
<div
|
|
532
604
|
style={{
|
|
533
605
|
display: "flex",
|
|
@@ -915,20 +987,46 @@ export function AiStatusButton({
|
|
|
915
987
|
</div>
|
|
916
988
|
<div style={aiStyles.tooltipRow}>
|
|
917
989
|
<span style={aiStyles.tooltipLabel}>Env:</span>
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
+
)}
|
|
923
1007
|
</div>
|
|
924
1008
|
<div style={aiStyles.tooltipRow}>
|
|
925
1009
|
<span style={aiStyles.tooltipLabel}>Rate Limit:</span>
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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
|
+
)}
|
|
932
1030
|
</div>
|
|
933
1031
|
<div style={aiStyles.tooltipRow}>
|
|
934
1032
|
<span style={aiStyles.tooltipLabel}>Auth:</span>
|
|
@@ -944,10 +1042,41 @@ export function AiStatusButton({
|
|
|
944
1042
|
<div style={aiStyles.tooltipSubtitle}>Wallet</div>
|
|
945
1043
|
<div style={aiStyles.tooltipRow}>
|
|
946
1044
|
<span style={aiStyles.tooltipLabel}>Total:</span>
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
+
)}
|
|
951
1080
|
</div>
|
|
952
1081
|
</div>
|
|
953
1082
|
|
|
@@ -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) && (
|
|
@@ -483,7 +483,21 @@ export function LBProvider({
|
|
|
483
483
|
const switchApiKey = useCallback(
|
|
484
484
|
async (apiKeyId: string): Promise<void> => {
|
|
485
485
|
if (state.status === "ready" || accessToken) {
|
|
486
|
-
|
|
486
|
+
// lb_session / login token flow: persist selection server-side when possible.
|
|
487
|
+
// api_key flow may legitimately reject this route; we still support local selection
|
|
488
|
+
// via x-lb-api-key-selected header on subsequent requests.
|
|
489
|
+
if (accessToken || state.session?.sessionToken) {
|
|
490
|
+
await lbClient.selectApiKey(apiKeyId, accessToken);
|
|
491
|
+
} else {
|
|
492
|
+
try {
|
|
493
|
+
await lbClient.selectApiKey(apiKeyId, accessToken);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.warn(
|
|
496
|
+
"[LBProvider] selectApiKey API call failed, applying local switch only",
|
|
497
|
+
error
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
487
501
|
const selectedKey = apiKeys.find((key) => key.id === apiKeyId);
|
|
488
502
|
if (selectedKey) {
|
|
489
503
|
setState((prev) => ({
|
|
@@ -499,6 +513,7 @@ export function LBProvider({
|
|
|
499
513
|
},
|
|
500
514
|
[
|
|
501
515
|
state.status,
|
|
516
|
+
state.session?.sessionToken,
|
|
502
517
|
accessToken,
|
|
503
518
|
apiKeys,
|
|
504
519
|
lbClient,
|
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(() => {
|
package/src/hooks/usePrompts.ts
CHANGED
|
@@ -59,6 +59,13 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
59
59
|
const [loading, setLoading] = useState(false);
|
|
60
60
|
const [error, setError] = useState<string | null>(null);
|
|
61
61
|
|
|
62
|
+
const isAuthTokenCandidate = (value?: string): boolean => {
|
|
63
|
+
if (!value) return false;
|
|
64
|
+
const token = value.trim();
|
|
65
|
+
if (!token) return false;
|
|
66
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
67
|
+
};
|
|
68
|
+
|
|
62
69
|
const fetchPrompts = useCallback(
|
|
63
70
|
async (options?: UsePromptsOptions) => {
|
|
64
71
|
try {
|
|
@@ -133,7 +140,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
133
140
|
|
|
134
141
|
const headers: HeadersInit = {};
|
|
135
142
|
// Ajouter l'API key pour les appels publics directs (pas de proxy externe ni auth interne)
|
|
136
|
-
if (isPublicApi && apiKeyId) {
|
|
143
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
137
144
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
138
145
|
}
|
|
139
146
|
|
|
@@ -179,7 +186,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
179
186
|
: "/api/ai/auth/prompts";
|
|
180
187
|
|
|
181
188
|
const headers: HeadersInit = { "Content-Type": "application/json" };
|
|
182
|
-
if (!isExternalProxy && apiKeyId) {
|
|
189
|
+
if (!isExternalProxy && isAuthTokenCandidate(apiKeyId)) {
|
|
183
190
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
184
191
|
}
|
|
185
192
|
|
|
@@ -225,7 +232,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
225
232
|
: "/api/ai/auth/prompts";
|
|
226
233
|
|
|
227
234
|
const headers: HeadersInit = { "Content-Type": "application/json" };
|
|
228
|
-
if (isPublicApi && apiKeyId) {
|
|
235
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
229
236
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
230
237
|
}
|
|
231
238
|
|
|
@@ -271,7 +278,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
271
278
|
: `/api/ai/auth/prompts?id=${id}`;
|
|
272
279
|
|
|
273
280
|
const headers: HeadersInit = {};
|
|
274
|
-
if (isPublicApi && apiKeyId) {
|
|
281
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
275
282
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
276
283
|
}
|
|
277
284
|
|
|
@@ -7,6 +7,14 @@ export interface ModelToggleOptions {
|
|
|
7
7
|
baseUrl?: string; // URL de base de l'API
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function isAuthTokenCandidate(value?: string): boolean {
|
|
11
|
+
if (!value) return false;
|
|
12
|
+
const token = value.trim();
|
|
13
|
+
if (!token) return false;
|
|
14
|
+
// Raw API key or JWT access token
|
|
15
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Active ou désactive un modèle pour l'utilisateur courant
|
|
12
20
|
*/
|
|
@@ -22,7 +30,7 @@ export async function toggleUserModel(
|
|
|
22
30
|
};
|
|
23
31
|
|
|
24
32
|
// Ajouter la clé API si fournie
|
|
25
|
-
if (apiKey) {
|
|
33
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
26
34
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
27
35
|
}
|
|
28
36
|
|
|
@@ -96,7 +104,7 @@ export async function getAvailableModels(
|
|
|
96
104
|
const headers: Record<string, string> = {};
|
|
97
105
|
|
|
98
106
|
// Ajouter la clé API pour les appels publics directs
|
|
99
|
-
if (isPublicApi && apiKey) {
|
|
107
|
+
if (isPublicApi && isAuthTokenCandidate(apiKey)) {
|
|
100
108
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
101
109
|
}
|
|
102
110
|
|
|
@@ -157,7 +165,7 @@ export async function getUserModels(
|
|
|
157
165
|
const headers: Record<string, string> = {};
|
|
158
166
|
|
|
159
167
|
// Ajouter la clé API pour tous les types d'appels si disponible
|
|
160
|
-
if (apiKey) {
|
|
168
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
161
169
|
if (isPublicApi) {
|
|
162
170
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
163
171
|
} else {
|