@lastbrain/ai-ui-react 1.0.34 → 1.0.35

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.
Files changed (34) hide show
  1. package/dist/components/AiChipLabel.js +1 -1
  2. package/dist/components/AiContextButton.d.ts.map +1 -1
  3. package/dist/components/AiContextButton.js +1 -0
  4. package/dist/components/AiImageButton.d.ts.map +1 -1
  5. package/dist/components/AiImageButton.js +2 -1
  6. package/dist/components/AiInput.js +1 -1
  7. package/dist/components/AiPromptPanel.d.ts +1 -1
  8. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  9. package/dist/components/AiPromptPanel.js +9 -5
  10. package/dist/components/AiSelect.js +1 -1
  11. package/dist/components/AiTextarea.js +1 -1
  12. package/dist/context/AiProvider.d.ts +28 -0
  13. package/dist/context/AiProvider.d.ts.map +1 -1
  14. package/dist/context/AiProvider.js +103 -1
  15. package/dist/hooks/useAiModels.d.ts +5 -0
  16. package/dist/hooks/useAiModels.d.ts.map +1 -1
  17. package/dist/hooks/useAiModels.js +40 -24
  18. package/dist/hooks/useModelManagement.d.ts +2 -10
  19. package/dist/hooks/useModelManagement.d.ts.map +1 -1
  20. package/dist/hooks/useModelManagement.js +42 -150
  21. package/dist/utils/modelManagement.d.ts.map +1 -1
  22. package/dist/utils/modelManagement.js +7 -3
  23. package/package.json +2 -2
  24. package/src/components/AiChipLabel.tsx +1 -1
  25. package/src/components/AiContextButton.tsx +1 -0
  26. package/src/components/AiImageButton.tsx +2 -1
  27. package/src/components/AiInput.tsx +1 -1
  28. package/src/components/AiPromptPanel.tsx +20 -1
  29. package/src/components/AiSelect.tsx +1 -1
  30. package/src/components/AiTextarea.tsx +1 -1
  31. package/src/context/AiProvider.tsx +156 -1
  32. package/src/hooks/useAiModels.ts +49 -27
  33. package/src/hooks/useModelManagement.ts +47 -203
  34. package/src/utils/modelManagement.ts +7 -3
@@ -42,7 +42,7 @@ export function AiChipInput({ value = [], onChange, placeholder = "Tapez et appu
42
42
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
43
43
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
44
44
  // Hooks pour l'IA avec les valeurs du contexte
45
- const { models } = useAiModels({ baseUrl, apiKeyId });
45
+ const { models } = useAiModels({ baseUrl, apiKeyId, modelType: "text-or-language" });
46
46
  const { generateText } = useAiCallText({ baseUrl, apiKeyId });
47
47
  const addChip = (text) => {
48
48
  if (!text.trim())
@@ -1 +1 @@
1
- {"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAEvE,WAAW,EAAE,GAAG,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,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,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAyC,EACzC,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAA0C,EAC1C,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,GAAG,WAAW,EACf,EAAE,oBAAoB,2CA6ftB"}
1
+ {"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAEvE,WAAW,EAAE,GAAG,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,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,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAyC,EACzC,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAA0C,EAC1C,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,GAAG,WAAW,EACf,EAAE,oBAAoB,2CA8ftB"}
@@ -24,6 +24,7 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
24
24
  const { models, loading: modelsLoading } = useAiModels({
25
25
  baseUrl,
26
26
  apiKeyId,
27
+ modelType: "text-or-language",
27
28
  });
28
29
  const { generateText: callText, loading } = useAiCallText({
29
30
  baseUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,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,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAkXpB"}
1
+ {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,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,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAmXpB"}
@@ -19,7 +19,8 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
19
19
  const aiContext = useAiContext();
20
20
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
21
21
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
22
- const { models } = useAiModels({ baseUrl, apiKeyId });
22
+ // Récupérer uniquement les modèles image
23
+ const { models } = useAiModels({ baseUrl, apiKeyId, modelType: "image" });
23
24
  const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
24
25
  const handleOpenPanel = () => {
25
26
  setIsOpen(true);
@@ -15,7 +15,7 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
15
15
  const [isButtonHovered, setIsButtonHovered] = useState(false);
16
16
  const inputRef = useRef(null);
17
17
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
18
- const { models } = useAiModels({ baseUrl, apiKeyId });
18
+ const { models } = useAiModels({ baseUrl, apiKeyId, modelType: "text-or-language" });
19
19
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
20
20
  const hasConfiguration = Boolean(model && prompt);
21
21
  const handleOpenPanel = () => {
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { ModelRef } from "@lastbrain/ai-ui-core";
3
3
  import type { UiMode } from "../types";
4
- import { type AIModel } from "../hooks/useModelManagement";
4
+ import { type AIModel } from "../context/AiProvider";
5
5
  export interface AiPromptPanelProps {
6
6
  isOpen: boolean;
7
7
  onClose: () => void;
@@ -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;AAQvC,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAG/E,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,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,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"}
@@ -539,11 +539,15 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
539
539
  maxWidth: "600px",
540
540
  maxHeight: "80vh",
541
541
  overflow: "auto",
542
- }, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: "Gestion des mod\u00E8les IA" }), _jsx("button", { style: aiStyles.modalCloseButton, onClick: () => setIsModelManagementOpen(false), "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { style: aiStyles.modalBody, children: [_jsx("div", { style: { marginBottom: "16px" }, children: _jsx("p", { style: {
543
- fontSize: "14px",
544
- color: "#6b7280",
545
- margin: "0 0 16px 0",
546
- }, children: "Activez ou d\u00E9sactivez les mod\u00E8les selon vos besoins" }) }), _jsx("div", { style: {
542
+ }, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: "Gestion des mod\u00E8les IA" }), _jsx("button", { style: aiStyles.modalCloseButton, onClick: () => setIsModelManagementOpen(false), "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { style: aiStyles.modalBody, children: [_jsxs("div", { style: { marginBottom: "16px" }, children: [_jsx("p", { style: {
543
+ fontSize: "14px",
544
+ color: "#6b7280",
545
+ margin: "0 0 16px 0",
546
+ }, children: "Activez ou d\u00E9sactivez les mod\u00E8les selon vos besoins" }), _jsxs("p", { style: {
547
+ fontSize: "12px",
548
+ color: "#9ca3af",
549
+ margin: "0",
550
+ }, children: [effectiveAvailableModels.filter((m) => m.category === "text").length, " ", "mod\u00E8les disponibles \u2022", " ", effectiveUserModels.filter((id) => effectiveAvailableModels.some((m) => m.id === id && m.category === "text")).length, " ", "activ\u00E9s"] })] }), _jsx("div", { style: {
547
551
  display: "flex",
548
552
  flexDirection: "column",
549
553
  gap: "12px",
@@ -11,7 +11,7 @@ export function AiSelect({ baseUrl, apiKeyId, uiMode = "modal", context, model,
11
11
  const [isOpen, setIsOpen] = useState(false);
12
12
  const [isFocused, setIsFocused] = useState(false);
13
13
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
14
- const { models } = useAiModels({ baseUrl, apiKeyId });
14
+ const { models } = useAiModels({ baseUrl, apiKeyId, modelType: "text-or-language" });
15
15
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
16
16
  const handleOpenPanel = () => {
17
17
  setIsOpen(true);
@@ -17,7 +17,7 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
17
17
  const [isButtonHovered, setIsButtonHovered] = useState(false);
18
18
  const textareaRef = useRef(null);
19
19
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
20
- const { models } = useAiModels({ baseUrl, apiKeyId });
20
+ const { models } = useAiModels({ baseUrl, apiKeyId, modelType: "text-or-language" });
21
21
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
22
22
  const hasConfiguration = Boolean(model && prompt);
23
23
  const handleOpenPanel = () => {
@@ -1,9 +1,37 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { UiMode } from "../types";
3
+ import type { ModelRef } from "@lastbrain/ai-ui-core";
4
+ export interface AIModel {
5
+ id: string;
6
+ name: string;
7
+ description?: string;
8
+ provider: string;
9
+ category: "text" | "image" | "audio" | "video";
10
+ isActive?: boolean;
11
+ isPro?: boolean;
12
+ costPer1M?: number;
13
+ }
14
+ export interface ProviderData {
15
+ id: string;
16
+ name: string;
17
+ description?: string;
18
+ models: ModelRef[];
19
+ }
3
20
  export interface AiContextValue {
4
21
  baseUrl: string;
5
22
  apiKeyId: string;
6
23
  uiMode: UiMode;
24
+ providers: ProviderData[];
25
+ allModels: ModelRef[];
26
+ availableModels: AIModel[];
27
+ userModels: string[];
28
+ loadingProviders: boolean;
29
+ loadingUserModels: boolean;
30
+ refetchProviders: () => Promise<void>;
31
+ refetchUserModels: () => Promise<void>;
32
+ getModelsByType: (type: "text" | "language" | "image" | "embed") => ModelRef[];
33
+ getTextModels: () => ModelRef[];
34
+ getImageModels: () => ModelRef[];
7
35
  }
8
36
  declare const AiContext: import("react").Context<AiContextValue | undefined>;
9
37
  export interface AiProviderProps {
@@ -1 +1 @@
1
- {"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAQjB;AAED,wBAAgB,YAAY,IAAI,cAAc,CAM7C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IAErB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,KAAK,QAAQ,EAAE,CAAC;IAC/E,aAAa,EAAE,MAAM,QAAQ,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,QAAQ,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAwHjB;AAED,wBAAgB,YAAY,IAAI,cAAc,CAM7C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -1,12 +1,114 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext } from "react";
3
+ import { createContext, useContext, useState, useEffect, useCallback, } from "react";
4
+ import { getAvailableModels, getUserModels } from "../utils/modelManagement";
4
5
  const AiContext = createContext(undefined);
5
6
  export function AiProvider({ baseUrl, apiKeyId, uiMode = "modal", children, }) {
7
+ const [providers, setProviders] = useState([]);
8
+ const [allModels, setAllModels] = useState([]);
9
+ const [availableModels, setAvailableModels] = useState([]);
10
+ const [userModels, setUserModels] = useState([]);
11
+ const [loadingProviders, setLoadingProviders] = useState(false);
12
+ const [loadingUserModels, setLoadingUserModels] = useState(false);
13
+ // Récupérer les providers et leurs modèles + available models en parallèle
14
+ const fetchProviders = useCallback(async () => {
15
+ if (loadingProviders)
16
+ return; // Éviter les appels multiples
17
+ setLoadingProviders(true);
18
+ try {
19
+ // Fetch providers et available models en parallèle
20
+ const [providersResponse, availableModelsData] = await Promise.all([
21
+ fetch(`${baseUrl}/provider`, {
22
+ method: "GET",
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ ...(apiKeyId ? { Authorization: `Bearer ${apiKeyId}` } : {}),
26
+ },
27
+ }),
28
+ getAvailableModels({ baseUrl, apiKey: apiKeyId }).catch((error) => {
29
+ console.warn("[AiProvider] Could not fetch available models:", error);
30
+ return [];
31
+ }),
32
+ ]);
33
+ if (!providersResponse.ok) {
34
+ throw new Error(`Failed to fetch providers: ${providersResponse.status}`);
35
+ }
36
+ const providersData = await providersResponse.json();
37
+ if (providersData.providers && Array.isArray(providersData.providers)) {
38
+ setProviders(providersData.providers);
39
+ // Extraire tous les modèles
40
+ const models = [];
41
+ for (const provider of providersData.providers) {
42
+ if (provider.models && Array.isArray(provider.models)) {
43
+ models.push(...provider.models);
44
+ }
45
+ }
46
+ setAllModels(models);
47
+ }
48
+ // Stocker available models
49
+ setAvailableModels(availableModelsData);
50
+ }
51
+ catch (error) {
52
+ console.error("[AiProvider] Error fetching providers:", error);
53
+ // En cas d'erreur, utiliser des valeurs vides
54
+ setProviders([]);
55
+ setAllModels([]);
56
+ setAvailableModels([]);
57
+ }
58
+ finally {
59
+ setLoadingProviders(false);
60
+ }
61
+ }, [baseUrl, apiKeyId, loadingProviders]);
62
+ // Récupérer les modèles activés par l'utilisateur
63
+ const fetchUserModels = useCallback(async () => {
64
+ if (loadingUserModels)
65
+ return; // Éviter les appels multiples
66
+ setLoadingUserModels(true);
67
+ try {
68
+ const models = await getUserModels({ baseUrl, apiKey: apiKeyId });
69
+ setUserModels(models);
70
+ }
71
+ catch (error) {
72
+ console.error("[AiProvider] Error fetching user models:", error);
73
+ // En cas d'erreur 404, ne pas logger comme erreur critique
74
+ if (error instanceof Error && error.message.includes("404")) {
75
+ setUserModels([]);
76
+ }
77
+ }
78
+ finally {
79
+ setLoadingUserModels(false);
80
+ }
81
+ }, [baseUrl, apiKeyId, loadingUserModels]);
82
+ // Récupérer les données au montage du provider
83
+ useEffect(() => {
84
+ fetchProviders(); // Fetch providers + available models en même temps
85
+ fetchUserModels();
86
+ }, [fetchProviders, fetchUserModels]);
87
+ // Helpers pour filtrer les modèles par type
88
+ const getModelsByType = useCallback((type) => {
89
+ return allModels.filter((model) => model.type === type);
90
+ }, [allModels]);
91
+ const getTextModels = useCallback(() => {
92
+ return allModels.filter((model) => model.type === "text" || model.type === "language");
93
+ }, [allModels]);
94
+ const getImageModels = useCallback(() => {
95
+ return allModels.filter((model) => model.type === "image");
96
+ }, [allModels]);
6
97
  const value = {
7
98
  baseUrl,
8
99
  apiKeyId,
9
100
  uiMode,
101
+ providers,
102
+ allModels,
103
+ availableModels,
104
+ userModels,
105
+ loadingProviders,
106
+ loadingUserModels,
107
+ refetchProviders: fetchProviders,
108
+ refetchUserModels: fetchUserModels,
109
+ getModelsByType,
110
+ getTextModels,
111
+ getImageModels,
10
112
  };
11
113
  return _jsx(AiContext.Provider, { value: value, children: children });
12
114
  }
@@ -2,6 +2,7 @@ import type { ModelRef } from "@lastbrain/ai-ui-core";
2
2
  export interface UseAiModelsOptions {
3
3
  baseUrl?: string;
4
4
  apiKeyId?: string;
5
+ modelType?: "text" | "language" | "image" | "embed" | "text-or-language";
5
6
  }
6
7
  export interface UseAiModelsResult {
7
8
  models: ModelRef[] | null;
@@ -9,5 +10,9 @@ export interface UseAiModelsResult {
9
10
  error: Error | null;
10
11
  refetch: () => void;
11
12
  }
13
+ /**
14
+ * Hook pour récupérer les modèles disponibles
15
+ * Utilise les données du contexte pour éviter les appels API multiples
16
+ */
12
17
  export declare function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult;
13
18
  //# sourceMappingURL=useAiModels.d.ts.map
@@ -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;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA+B3E"}
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,GAAG,IAAI,CAAC;IAC1B,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,CAgD3E"}
@@ -1,32 +1,48 @@
1
1
  "use client";
2
- import { useState, useEffect, useCallback } from "react";
3
- import { useAiClient } from "./useAiClient";
2
+ import { useMemo, useCallback } from "react";
3
+ import { useAiContext } from "../context/AiProvider";
4
+ /**
5
+ * Hook pour récupérer les modèles disponibles
6
+ * Utilise les données du contexte pour éviter les appels API multiples
7
+ */
4
8
  export function useAiModels(options) {
5
- const client = useAiClient(options);
6
- const [models, setModels] = useState(null);
7
- const [loading, setLoading] = useState(false);
8
- const [error, setError] = useState(null);
9
- const fetchModels = useCallback(async () => {
10
- setLoading(true);
11
- setError(null);
12
- try {
13
- const result = await client.getModels();
14
- setModels(result);
9
+ const context = useAiContext();
10
+ // Si les options ne correspondent pas au contexte, on peut faire un appel direct
11
+ // Mais dans la plupart des cas, on utilise le contexte
12
+ const useContextData = (!options?.baseUrl || options.baseUrl === context.baseUrl) &&
13
+ (!options?.apiKeyId || options.apiKeyId === context.apiKeyId);
14
+ // Filtrer les modèles selon le type demandé
15
+ const filteredModels = useMemo(() => {
16
+ if (!useContextData || !context.allModels.length)
17
+ return null;
18
+ if (!options?.modelType) {
19
+ return context.allModels;
15
20
  }
16
- catch (err) {
17
- setError(err instanceof Error ? err : new Error("Failed to fetch models"));
21
+ // Cas spécial: text ou language
22
+ if (options.modelType === "text-or-language") {
23
+ return context.getTextModels();
18
24
  }
19
- finally {
20
- setLoading(false);
25
+ return context.getModelsByType(options.modelType);
26
+ }, [useContextData, context, options?.modelType]);
27
+ const refetch = useCallback(() => {
28
+ if (useContextData) {
29
+ context.refetchProviders();
21
30
  }
22
- }, [client]);
23
- useEffect(() => {
24
- fetchModels();
25
- }, [fetchModels]);
31
+ }, [useContextData, context]);
32
+ if (useContextData) {
33
+ return {
34
+ models: filteredModels,
35
+ loading: context.loadingProviders,
36
+ error: null,
37
+ refetch,
38
+ };
39
+ }
40
+ // Fallback: si les options ne correspondent pas, retourner des valeurs vides
41
+ // (cas rare, la plupart du temps on utilise le contexte)
26
42
  return {
27
- models,
28
- loading,
29
- error,
30
- refetch: fetchModels,
43
+ models: null,
44
+ loading: false,
45
+ error: new Error("useAiModels called with different baseUrl/apiKeyId than context"),
46
+ refetch: () => { },
31
47
  };
32
48
  }
@@ -1,14 +1,5 @@
1
+ import { type AIModel } from "../context/AiProvider";
1
2
  import { type ModelToggleOptions } from "../utils/modelManagement";
2
- export interface AIModel {
3
- id: string;
4
- name: string;
5
- description?: string;
6
- provider: string;
7
- category: "text" | "image" | "audio" | "video";
8
- isActive?: boolean;
9
- isPro?: boolean;
10
- costPer1M?: number;
11
- }
12
3
  export interface UseModelManagementOptions extends ModelToggleOptions {
13
4
  autoFetch?: boolean;
14
5
  category?: "text" | "image" | "audio" | "video";
@@ -27,6 +18,7 @@ export interface UseModelManagementReturn {
27
18
  }
28
19
  /**
29
20
  * Hook pour gérer les modèles IA d'un utilisateur
21
+ * Utilise les données du contexte pour éviter les appels API multiples
30
22
  */
31
23
  export declare function useModelManagement(options?: UseModelManagementOptions): UseModelManagementReturn;
32
24
  //# sourceMappingURL=useModelManagement.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModelManagement.d.ts","sourceRoot":"","sources":["../../src/hooks/useModelManagement.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,wBAAwB;IAEvC,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,eAAe,EAAE,MAAM,OAAO,EAAE,CAAC;IACjC,iBAAiB,EAAE,MAAM,OAAO,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAsO1B"}
1
+ {"version":3,"file":"useModelManagement.d.ts","sourceRoot":"","sources":["../../src/hooks/useModelManagement.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,wBAAwB;IAEvC,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,eAAe,EAAE,MAAM,OAAO,EAAE,CAAC;IACjC,iBAAiB,EAAE,MAAM,OAAO,EAAE,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CA4F1B"}
@@ -1,181 +1,73 @@
1
- import { useState, useCallback, useEffect, useMemo } from "react";
1
+ import { useState, useCallback, useMemo } from "react";
2
2
  import { useAiContext } from "../context/AiProvider";
3
- import { toggleUserModel, getAvailableModels, getUserModels, } from "../utils/modelManagement";
4
- import { getCached, setCache, isRateLimited, getRateLimitResetTime, } from "../utils/cache";
3
+ import { toggleUserModel, } from "../utils/modelManagement";
5
4
  /**
6
5
  * Hook pour gérer les modèles IA d'un utilisateur
6
+ * Utilise les données du contexte pour éviter les appels API multiples
7
7
  */
8
8
  export function useModelManagement(options = {}) {
9
- const { autoFetch = false, category, ...apiOptions } = options;
10
- const { baseUrl, apiKeyId } = useAiContext();
11
- const [availableModels, setAvailableModels] = useState([]);
12
- const [userModels, setUserModels] = useState([]);
9
+ const { category } = options;
10
+ const context = useAiContext();
13
11
  const [loading, setLoading] = useState(false);
14
12
  const [error, setError] = useState(null);
15
- const [apiUnavailable, setApiUnavailable] = useState(false);
16
- // Utiliser baseUrl et apiKey du contexte avec memoization
17
- const effectiveOptions = useMemo(() => ({
18
- baseUrl: options.baseUrl || baseUrl,
19
- apiKey: options.apiKey || apiKeyId,
20
- }), [options.baseUrl, options.apiKey, baseUrl, apiKeyId]);
13
+ // Utiliser les données du contexte si disponibles
14
+ const useContextData = (!options.baseUrl || options.baseUrl === context.baseUrl) &&
15
+ (!options.apiKey || options.apiKey === context.apiKeyId);
16
+ // Filtrer par catégorie si nécessaire
17
+ const filteredModels = useMemo(() => {
18
+ if (!useContextData)
19
+ return [];
20
+ return category
21
+ ? context.availableModels.filter((m) => m.category === category)
22
+ : context.availableModels;
23
+ }, [useContextData, category, context.availableModels]);
21
24
  const refreshModels = useCallback(async () => {
22
- try {
23
- setLoading(true);
24
- setError(null);
25
- // Check cache first (5 minutes TTL for models)
26
- const cacheKey = `models_${category || "all"}`;
27
- const cached = getCached(cacheKey, 300000);
28
- if (cached) {
29
- console.log("[useModelManagement] Using cached models");
30
- setAvailableModels(cached);
31
- setLoading(false);
32
- return;
33
- }
34
- // Check rate limit (max 5 calls per minute)
35
- if (isRateLimited("models_fetch", 5, 60000)) {
36
- const resetTime = getRateLimitResetTime("models_fetch");
37
- const resetSeconds = Math.ceil(resetTime / 1000);
38
- throw new Error(`Rate limit exceeded. Please wait ${resetSeconds}s before trying again.`);
39
- }
40
- console.log("[useModelManagement] Fetching available models with options:", {
41
- apiKey: effectiveOptions.apiKey
42
- ? effectiveOptions.apiKey.substring(0, 10) + "..."
43
- : "none",
44
- baseUrl: effectiveOptions.baseUrl || "default",
45
- });
46
- const models = await getAvailableModels(effectiveOptions);
47
- console.log("[useModelManagement] Got available models:", models.length);
48
- const filteredModels = category
49
- ? models.filter((m) => m.category === category)
50
- : models;
51
- setAvailableModels(filteredModels);
52
- // Cache the results
53
- setCache(cacheKey, filteredModels);
54
- console.log("[useModelManagement] Set filtered models:", filteredModels.length);
55
- }
56
- catch (err) {
57
- const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
58
- console.error("[useModelManagement] Error fetching available models:", errorMessage);
59
- setError(errorMessage);
60
- // En cas d'erreur 404 (API non disponible), utiliser un array vide
61
- if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
62
- setAvailableModels([]);
63
- }
25
+ if (useContextData) {
26
+ await context.refetchProviders();
64
27
  }
65
- finally {
66
- setLoading(false);
67
- }
68
- }, [category, effectiveOptions]);
28
+ }, [useContextData, context]);
69
29
  const refreshUserModels = useCallback(async () => {
70
- // Si l'API est marquée comme indisponible, ne pas faire l'appel
71
- if (apiUnavailable) {
72
- console.log("[useModelManagement] Skipping user models fetch - API marked as unavailable");
73
- return;
74
- }
75
- try {
76
- setLoading(true);
77
- setError(null);
78
- console.log("[useModelManagement] Fetching user models with options:", {
79
- apiKey: effectiveOptions.apiKey
80
- ? effectiveOptions.apiKey.substring(0, 10) + "..."
81
- : "none",
82
- baseUrl: effectiveOptions.baseUrl || "default",
83
- });
84
- const models = await getUserModels(effectiveOptions);
85
- console.log("[useModelManagement] Got user models:", models);
86
- setUserModels(models);
30
+ if (useContextData) {
31
+ await context.refetchUserModels();
87
32
  }
88
- catch (err) {
89
- const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
90
- console.error("[useModelManagement] Error fetching user models:", errorMessage);
91
- setError(errorMessage);
92
- // En cas d'erreur 404 (API non disponible), utiliser un array vide et ne pas retry
93
- if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
94
- setUserModels([]);
95
- setApiUnavailable(true);
96
- console.warn("[useModelManagement] User models API not available (404), disabling further requests");
97
- }
98
- }
99
- finally {
100
- setLoading(false);
101
- }
102
- }, [effectiveOptions, apiUnavailable]);
33
+ }, [useContextData, context]);
103
34
  const toggleModel = useCallback(async (modelId, isActive) => {
104
- const currentlyActive = userModels.includes(modelId);
35
+ const currentlyActive = context.userModels.includes(modelId);
105
36
  const targetState = isActive !== undefined ? isActive : !currentlyActive;
106
37
  try {
107
38
  setLoading(true);
108
39
  setError(null);
40
+ const effectiveOptions = {
41
+ baseUrl: options.baseUrl || context.baseUrl,
42
+ apiKey: options.apiKey || context.apiKeyId,
43
+ };
109
44
  await toggleUserModel(modelId, targetState, effectiveOptions);
110
- // Mise à jour optimiste
111
- if (targetState) {
112
- setUserModels((prev) => [
113
- ...prev.filter((id) => id !== modelId),
114
- modelId,
115
- ]);
116
- }
117
- else {
118
- setUserModels((prev) => prev.filter((id) => id !== modelId));
119
- }
45
+ // Rafraîchir les modèles utilisateur depuis le contexte
46
+ await context.refetchUserModels();
120
47
  }
121
48
  catch (err) {
122
- setError(err instanceof Error ? err.message : "Erreur inconnue");
49
+ const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
50
+ console.error("[useModelManagement] Error toggling model:", errorMessage);
51
+ setError(errorMessage);
123
52
  throw err;
124
53
  }
125
54
  finally {
126
55
  setLoading(false);
127
56
  }
128
- }, [userModels, effectiveOptions]);
129
- // Helpers
57
+ }, [context, options.baseUrl, options.apiKey]);
130
58
  const isModelActive = useCallback((modelId) => {
131
- return userModels.includes(modelId);
132
- }, [userModels]);
59
+ return context.userModels.includes(modelId);
60
+ }, [context.userModels]);
133
61
  const getActiveModels = useCallback(() => {
134
- return availableModels.filter((model) => userModels.includes(model.id));
135
- }, [availableModels, userModels]);
62
+ return filteredModels.filter((model) => context.userModels.includes(model.id));
63
+ }, [filteredModels, context.userModels]);
136
64
  const getInactiveModels = useCallback(() => {
137
- return availableModels.filter((model) => !userModels.includes(model.id));
138
- }, [availableModels, userModels]);
139
- // Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe) ET API disponible
140
- useEffect(() => {
141
- const isExternalProxy = effectiveOptions.baseUrl &&
142
- effectiveOptions.baseUrl.includes("/api/lastbrain");
143
- console.log("[useModelManagement] useEffect triggered:", {
144
- autoFetch,
145
- hasApiKey: !!effectiveOptions.apiKey,
146
- hasBaseUrl: !!effectiveOptions.baseUrl,
147
- isExternalProxy,
148
- apiUnavailable,
149
- apiKeyPreview: effectiveOptions.apiKey
150
- ? effectiveOptions.apiKey.substring(0, 10) + "..."
151
- : "none",
152
- baseUrl: effectiveOptions.baseUrl || "none",
153
- });
154
- // Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe ET API disponible
155
- if (autoFetch &&
156
- !apiUnavailable &&
157
- (effectiveOptions.apiKey || isExternalProxy)) {
158
- console.log("[useModelManagement] Starting auto-fetch...");
159
- Promise.all([refreshModels(), refreshUserModels()]);
160
- }
161
- else if (autoFetch && !effectiveOptions.apiKey && !isExternalProxy) {
162
- console.warn("[useModelManagement] autoFetch is enabled but no apiKey or baseUrl provided. Skipping automatic fetch.");
163
- }
164
- else if (autoFetch && apiUnavailable) {
165
- console.log("[useModelManagement] autoFetch skipped - API marked as unavailable");
166
- }
167
- }, [
168
- autoFetch,
169
- refreshModels,
170
- refreshUserModels,
171
- effectiveOptions.apiKey,
172
- effectiveOptions.baseUrl,
173
- apiUnavailable,
174
- ]);
65
+ return filteredModels.filter((model) => !context.userModels.includes(model.id));
66
+ }, [filteredModels, context.userModels]);
175
67
  return {
176
- availableModels,
177
- userModels,
178
- loading,
68
+ availableModels: filteredModels,
69
+ userModels: context.userModels,
70
+ loading: context.loadingProviders || context.loadingUserModels || loading,
179
71
  error,
180
72
  toggleModel,
181
73
  refreshModels,