@lastbrain/ai-ui-react 1.0.64 → 1.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,2CA6NlB"}
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;AAU5C,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,EACP,QAAQ,EACR,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,2CAyNd"}
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
- export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement = true, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
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
  }
@@ -17,6 +17,7 @@ export interface AiPromptPanelProps {
17
17
  onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
18
18
  apiKey?: string;
19
19
  baseUrl?: string;
20
+ showOnlyUserModels?: boolean;
20
21
  }
21
22
  export interface AiPromptPanelRenderProps {
22
23
  models?: ModelRef[];
@@ -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;CAClB;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,2CActD"}
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
- // Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
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: "Loading models..." })), modelOptions.map((model) => {
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,2CAksCrB"}
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,2CAssCrB"}
@@ -232,7 +232,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
232
232
  lbRefreshBasicStatus,
233
233
  lbRefreshStorageStatus,
234
234
  ]);
235
- if (loading || isSelectingApiKey || isLoadingStatus || lbIsLoadingStatus) {
235
+ if (loading ||
236
+ isSelectingApiKey ||
237
+ ((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)) {
236
238
  return (_jsx("button", { ref: buttonRef, style: {
237
239
  ...aiStyles.statusButton,
238
240
  ...aiStyles.statusButtonDisabled,
@@ -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;AAU5C,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,EACP,QAAQ,EACR,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,2CAgOjB"}
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
- export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
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,2CAufjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
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
- await lbClient.selectApiKey(apiKeyId, accessToken);
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":"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,CAgS7C"}
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"}
@@ -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;AAED;;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
+ {"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.64",
3
+ "version": "1.0.65",
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.48"
51
+ "@lastbrain/ai-ui-core": "1.0.49"
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
- // Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
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="">Loading models...</option>
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);
@@ -345,7 +345,11 @@ export function AiStatusButton({
345
345
  lbRefreshStorageStatus,
346
346
  ]);
347
347
 
348
- if (loading || isSelectingApiKey || isLoadingStatus || lbIsLoadingStatus) {
348
+ if (
349
+ loading ||
350
+ isSelectingApiKey ||
351
+ ((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)
352
+ ) {
349
353
  return (
350
354
  <button
351
355
  ref={buttonRef}
@@ -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
- await lbClient.selectApiKey(apiKeyId, accessToken);
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,
@@ -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 {