@lastbrain/ai-ui-react 1.0.26 → 1.0.28
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/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +33 -3
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +21 -2
- package/dist/components/AiPromptPanel.d.ts +1 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +12 -4
- package/dist/hooks/useModelManagement.d.ts.map +1 -1
- package/dist/hooks/useModelManagement.js +24 -12
- package/package.json +1 -1
- package/src/components/AiContextButton.tsx +34 -2
- package/src/components/AiImageButton.tsx +22 -2
- package/src/components/AiPromptPanel.tsx +19 -5
- package/src/hooks/useModelManagement.ts +24 -12
|
@@ -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;AAQ5C,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,
|
|
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;AAQ5C,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,2CAshBtB"}
|
|
@@ -153,10 +153,40 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
catch (error) {
|
|
156
|
+
console.error("AiContextButton error:", error);
|
|
157
|
+
// Gestion spécifique des erreurs d'API
|
|
158
|
+
let errorMessage = "Erreur lors de l'analyse";
|
|
159
|
+
let errorCode;
|
|
160
|
+
if (error instanceof Error) {
|
|
161
|
+
try {
|
|
162
|
+
// Tenter de parser l'erreur JSON si c'est une erreur API
|
|
163
|
+
const errorData = JSON.parse(error.message);
|
|
164
|
+
if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
|
|
165
|
+
errorMessage =
|
|
166
|
+
"Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
|
|
167
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
168
|
+
}
|
|
169
|
+
else if (errorData.error?.message) {
|
|
170
|
+
errorMessage = errorData.error.message;
|
|
171
|
+
errorCode = errorData.error.code;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Si ce n'est pas du JSON, utiliser le message d'erreur direct
|
|
176
|
+
if (error.message.includes("INSUFFICIENT_TOKENS")) {
|
|
177
|
+
errorMessage =
|
|
178
|
+
"Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
|
|
179
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
errorMessage = error.message;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
156
186
|
onToast?.({
|
|
157
187
|
type: "error",
|
|
158
|
-
message:
|
|
159
|
-
code:
|
|
188
|
+
message: errorMessage,
|
|
189
|
+
code: errorCode,
|
|
160
190
|
});
|
|
161
191
|
}
|
|
162
192
|
finally {
|
|
@@ -217,7 +247,7 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
217
247
|
} }), _jsx("span", { style: { letterSpacing: "0.025em" }, children: "Analyse..." })] })) : (_jsx(_Fragment, { children: _jsx(Sparkle, { size: 18, style: {
|
|
218
248
|
color: "white",
|
|
219
249
|
filter: "drop-shadow(0 0 2px rgba(255,255,255,0.2))",
|
|
220
|
-
} }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models?.filter((m) => m.type === "language") || [], enableModelManagement: true }))] }), isResultOpen && analysisResult && (_jsx("div", { style: {
|
|
250
|
+
} }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models?.filter((m) => m.type === "language") || [], enableModelManagement: true, baseUrl: baseUrl, apiKey: apiKeyId }))] }), isResultOpen && analysisResult && (_jsx("div", { style: {
|
|
221
251
|
position: "fixed",
|
|
222
252
|
top: 0,
|
|
223
253
|
left: 0,
|
|
@@ -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;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,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,
|
|
1
|
+
{"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,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,2CA2ZpB"}
|
|
@@ -132,10 +132,29 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
catch (error) {
|
|
135
|
+
let errorMessage = "Erreur lors de la génération de l'image";
|
|
136
|
+
let errorCode;
|
|
137
|
+
// Parse JSON error response for INSUFFICIENT_TOKENS
|
|
138
|
+
if (error instanceof Error) {
|
|
139
|
+
try {
|
|
140
|
+
const errorData = JSON.parse(error.message);
|
|
141
|
+
if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
|
|
142
|
+
errorMessage = `Solde insuffisant. ${errorData.error.message || "Veuillez recharger votre compte."}`;
|
|
143
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// If parsing fails, check if error message contains the code
|
|
148
|
+
if (error.message.includes("INSUFFICIENT_TOKENS")) {
|
|
149
|
+
errorMessage = "Solde insuffisant. Veuillez recharger votre compte pour continuer.";
|
|
150
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
135
154
|
onToast?.({
|
|
136
155
|
type: "error",
|
|
137
|
-
message:
|
|
138
|
-
code: error instanceof Error ? error.message : undefined,
|
|
156
|
+
message: errorMessage,
|
|
157
|
+
code: errorCode || (error instanceof Error ? error.message : undefined),
|
|
139
158
|
});
|
|
140
159
|
}
|
|
141
160
|
finally {
|
|
@@ -33,5 +33,5 @@ export interface AiPromptPanelRenderProps {
|
|
|
33
33
|
setShowAllModels?: (show: boolean) => void;
|
|
34
34
|
onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
|
|
35
35
|
}
|
|
36
|
-
export declare function AiPromptPanel(
|
|
36
|
+
export declare function AiPromptPanel(props: AiPromptPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
37
37
|
//# sourceMappingURL=AiPromptPanel.d.ts.map
|
|
@@ -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;AAOvC,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,6BAA6B,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;AAOvC,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"}
|
|
@@ -5,7 +5,17 @@ import { BookOpen, Search, Sparkles, Star, Tag, Settings } from "lucide-react";
|
|
|
5
5
|
import { aiStyles } from "../styles/inline";
|
|
6
6
|
import { usePrompts, } from "../hooks/usePrompts";
|
|
7
7
|
import { useModelManagement } from "../hooks/useModelManagement";
|
|
8
|
-
|
|
8
|
+
import { AiProvider } from "../context/AiProvider";
|
|
9
|
+
export function AiPromptPanel(props) {
|
|
10
|
+
const { apiKey, baseUrl } = props;
|
|
11
|
+
// Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
|
|
12
|
+
if (apiKey || baseUrl) {
|
|
13
|
+
return (_jsx(AiProvider, { baseUrl: baseUrl || "", apiKeyId: apiKey || "", children: _jsx(AiPromptPanelInternal, { ...props }) }));
|
|
14
|
+
}
|
|
15
|
+
// Sinon, utiliser le contexte existant
|
|
16
|
+
return _jsx(AiPromptPanelInternal, { ...props });
|
|
17
|
+
}
|
|
18
|
+
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = false, availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, }) {
|
|
9
19
|
const [selectedModel, setSelectedModel] = useState("");
|
|
10
20
|
const [prompt, setPrompt] = useState("");
|
|
11
21
|
const [promptId, setPromptId] = useState(undefined);
|
|
@@ -31,9 +41,7 @@ export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode: _uiMode = "mo
|
|
|
31
41
|
apiKey,
|
|
32
42
|
baseUrl,
|
|
33
43
|
category: "text", // Par défaut pour AiPromptPanel
|
|
34
|
-
autoFetch: enableModelManagement &&
|
|
35
|
-
availableModels.length === 0 &&
|
|
36
|
-
(!!apiKey || !!baseUrl),
|
|
44
|
+
autoFetch: enableModelManagement && availableModels.length === 0,
|
|
37
45
|
});
|
|
38
46
|
// Utiliser soit les props externes soit la gestion automatique
|
|
39
47
|
const effectiveAvailableModels = availableModels.length > 0
|
|
@@ -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,
|
|
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,CAyN1B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, useEffect } from "react";
|
|
1
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import { useAiContext } from "../context/AiProvider";
|
|
3
3
|
import { toggleUserModel, getAvailableModels, getUserModels, } from "../utils/modelManagement";
|
|
4
4
|
import { getCached, setCache, isRateLimited, getRateLimitResetTime, } from "../utils/cache";
|
|
@@ -12,12 +12,12 @@ export function useModelManagement(options = {}) {
|
|
|
12
12
|
const [userModels, setUserModels] = useState([]);
|
|
13
13
|
const [loading, setLoading] = useState(false);
|
|
14
14
|
const [error, setError] = useState(null);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
baseUrl:
|
|
19
|
-
apiKey:
|
|
20
|
-
};
|
|
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]);
|
|
21
21
|
const refreshModels = useCallback(async () => {
|
|
22
22
|
try {
|
|
23
23
|
setLoading(true);
|
|
@@ -67,6 +67,11 @@ export function useModelManagement(options = {}) {
|
|
|
67
67
|
}
|
|
68
68
|
}, [category, effectiveOptions]);
|
|
69
69
|
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
|
+
}
|
|
70
75
|
try {
|
|
71
76
|
setLoading(true);
|
|
72
77
|
setError(null);
|
|
@@ -84,15 +89,17 @@ export function useModelManagement(options = {}) {
|
|
|
84
89
|
const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
|
|
85
90
|
console.error("[useModelManagement] Error fetching user models:", errorMessage);
|
|
86
91
|
setError(errorMessage);
|
|
87
|
-
// En cas d'erreur 404 (API non disponible), utiliser un array vide
|
|
92
|
+
// En cas d'erreur 404 (API non disponible), utiliser un array vide et ne pas retry
|
|
88
93
|
if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
|
|
89
94
|
setUserModels([]);
|
|
95
|
+
setApiUnavailable(true);
|
|
96
|
+
console.warn("[useModelManagement] User models API not available (404), disabling further requests");
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
finally {
|
|
93
100
|
setLoading(false);
|
|
94
101
|
}
|
|
95
|
-
}, [effectiveOptions]);
|
|
102
|
+
}, [effectiveOptions, apiUnavailable]);
|
|
96
103
|
const toggleModel = useCallback(async (modelId, isActive) => {
|
|
97
104
|
const currentlyActive = userModels.includes(modelId);
|
|
98
105
|
const targetState = isActive !== undefined ? isActive : !currentlyActive;
|
|
@@ -129,7 +136,7 @@ export function useModelManagement(options = {}) {
|
|
|
129
136
|
const getInactiveModels = useCallback(() => {
|
|
130
137
|
return availableModels.filter((model) => !userModels.includes(model.id));
|
|
131
138
|
}, [availableModels, userModels]);
|
|
132
|
-
// Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe)
|
|
139
|
+
// Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe) ET API disponible
|
|
133
140
|
useEffect(() => {
|
|
134
141
|
const isExternalProxy = effectiveOptions.baseUrl &&
|
|
135
142
|
effectiveOptions.baseUrl.includes("/api/lastbrain");
|
|
@@ -138,25 +145,30 @@ export function useModelManagement(options = {}) {
|
|
|
138
145
|
hasApiKey: !!effectiveOptions.apiKey,
|
|
139
146
|
hasBaseUrl: !!effectiveOptions.baseUrl,
|
|
140
147
|
isExternalProxy,
|
|
148
|
+
apiUnavailable,
|
|
141
149
|
apiKeyPreview: effectiveOptions.apiKey
|
|
142
150
|
? effectiveOptions.apiKey.substring(0, 10) + "..."
|
|
143
151
|
: "none",
|
|
144
152
|
baseUrl: effectiveOptions.baseUrl || "none",
|
|
145
153
|
});
|
|
146
|
-
// Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe
|
|
147
|
-
if (autoFetch && (effectiveOptions.apiKey || isExternalProxy)) {
|
|
154
|
+
// Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe ET API disponible
|
|
155
|
+
if (autoFetch && !apiUnavailable && (effectiveOptions.apiKey || isExternalProxy)) {
|
|
148
156
|
console.log("[useModelManagement] Starting auto-fetch...");
|
|
149
157
|
Promise.all([refreshModels(), refreshUserModels()]);
|
|
150
158
|
}
|
|
151
159
|
else if (autoFetch && !effectiveOptions.apiKey && !isExternalProxy) {
|
|
152
160
|
console.warn("[useModelManagement] autoFetch is enabled but no apiKey or baseUrl provided. Skipping automatic fetch.");
|
|
153
161
|
}
|
|
162
|
+
else if (autoFetch && apiUnavailable) {
|
|
163
|
+
console.log("[useModelManagement] autoFetch skipped - API marked as unavailable");
|
|
164
|
+
}
|
|
154
165
|
}, [
|
|
155
166
|
autoFetch,
|
|
156
167
|
refreshModels,
|
|
157
168
|
refreshUserModels,
|
|
158
169
|
effectiveOptions.apiKey,
|
|
159
170
|
effectiveOptions.baseUrl,
|
|
171
|
+
apiUnavailable,
|
|
160
172
|
]);
|
|
161
173
|
return {
|
|
162
174
|
availableModels,
|
package/package.json
CHANGED
|
@@ -221,10 +221,40 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
223
|
} catch (error) {
|
|
224
|
+
console.error("AiContextButton error:", error);
|
|
225
|
+
|
|
226
|
+
// Gestion spécifique des erreurs d'API
|
|
227
|
+
let errorMessage = "Erreur lors de l'analyse";
|
|
228
|
+
let errorCode: string | undefined;
|
|
229
|
+
|
|
230
|
+
if (error instanceof Error) {
|
|
231
|
+
try {
|
|
232
|
+
// Tenter de parser l'erreur JSON si c'est une erreur API
|
|
233
|
+
const errorData = JSON.parse(error.message);
|
|
234
|
+
if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
|
|
235
|
+
errorMessage =
|
|
236
|
+
"Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
|
|
237
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
238
|
+
} else if (errorData.error?.message) {
|
|
239
|
+
errorMessage = errorData.error.message;
|
|
240
|
+
errorCode = errorData.error.code;
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
// Si ce n'est pas du JSON, utiliser le message d'erreur direct
|
|
244
|
+
if (error.message.includes("INSUFFICIENT_TOKENS")) {
|
|
245
|
+
errorMessage =
|
|
246
|
+
"Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
|
|
247
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
248
|
+
} else {
|
|
249
|
+
errorMessage = error.message;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
224
254
|
onToast?.({
|
|
225
255
|
type: "error",
|
|
226
|
-
message:
|
|
227
|
-
code:
|
|
256
|
+
message: errorMessage,
|
|
257
|
+
code: errorCode,
|
|
228
258
|
});
|
|
229
259
|
} finally {
|
|
230
260
|
setIsOpen(false);
|
|
@@ -328,6 +358,8 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
328
358
|
uiMode={uiMode}
|
|
329
359
|
models={models?.filter((m) => m.type === "language") || []}
|
|
330
360
|
enableModelManagement={true}
|
|
361
|
+
baseUrl={baseUrl}
|
|
362
|
+
apiKey={apiKeyId}
|
|
331
363
|
/>
|
|
332
364
|
)}
|
|
333
365
|
</div>
|
|
@@ -193,10 +193,30 @@ export function AiImageButton({
|
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
195
|
} catch (error) {
|
|
196
|
+
let errorMessage = "Erreur lors de la génération de l'image";
|
|
197
|
+
let errorCode: string | undefined;
|
|
198
|
+
|
|
199
|
+
// Parse JSON error response for INSUFFICIENT_TOKENS
|
|
200
|
+
if (error instanceof Error) {
|
|
201
|
+
try {
|
|
202
|
+
const errorData = JSON.parse(error.message);
|
|
203
|
+
if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
|
|
204
|
+
errorMessage = `Solde insuffisant. ${errorData.error.message || "Veuillez recharger votre compte."}`;
|
|
205
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
// If parsing fails, check if error message contains the code
|
|
209
|
+
if (error.message.includes("INSUFFICIENT_TOKENS")) {
|
|
210
|
+
errorMessage = "Solde insuffisant. Veuillez recharger votre compte pour continuer.";
|
|
211
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
onToast?.({
|
|
197
217
|
type: "error",
|
|
198
|
-
message:
|
|
199
|
-
code: error instanceof Error ? error.message : undefined,
|
|
218
|
+
message: errorMessage,
|
|
219
|
+
code: errorCode || (error instanceof Error ? error.message : undefined),
|
|
200
220
|
});
|
|
201
221
|
} finally {
|
|
202
222
|
setIsOpen(false);
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type PublicPrompt,
|
|
18
18
|
} from "../hooks/usePrompts";
|
|
19
19
|
import { useModelManagement, type AIModel } from "../hooks/useModelManagement";
|
|
20
|
+
import { AiProvider } from "../context/AiProvider";
|
|
20
21
|
|
|
21
22
|
export interface AiPromptPanelProps {
|
|
22
23
|
isOpen: boolean;
|
|
@@ -53,7 +54,23 @@ export interface AiPromptPanelRenderProps {
|
|
|
53
54
|
onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
export function AiPromptPanel({
|
|
57
|
+
export function AiPromptPanel(props: AiPromptPanelProps) {
|
|
58
|
+
const { apiKey, baseUrl } = props;
|
|
59
|
+
|
|
60
|
+
// Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
|
|
61
|
+
if (apiKey || baseUrl) {
|
|
62
|
+
return (
|
|
63
|
+
<AiProvider baseUrl={baseUrl || ""} apiKeyId={apiKey || ""}>
|
|
64
|
+
<AiPromptPanelInternal {...props} />
|
|
65
|
+
</AiProvider>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Sinon, utiliser le contexte existant
|
|
70
|
+
return <AiPromptPanelInternal {...props} />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function AiPromptPanelInternal({
|
|
57
74
|
isOpen,
|
|
58
75
|
onClose,
|
|
59
76
|
onSubmit,
|
|
@@ -101,10 +118,7 @@ export function AiPromptPanel({
|
|
|
101
118
|
apiKey,
|
|
102
119
|
baseUrl,
|
|
103
120
|
category: "text", // Par défaut pour AiPromptPanel
|
|
104
|
-
autoFetch:
|
|
105
|
-
enableModelManagement &&
|
|
106
|
-
availableModels.length === 0 &&
|
|
107
|
-
(!!apiKey || !!baseUrl),
|
|
121
|
+
autoFetch: enableModelManagement && availableModels.length === 0,
|
|
108
122
|
});
|
|
109
123
|
|
|
110
124
|
// Utiliser soit les props externes soit la gestion automatique
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, useEffect } from "react";
|
|
1
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import { useAiContext } from "../context/AiProvider";
|
|
3
3
|
import {
|
|
4
4
|
toggleUserModel,
|
|
@@ -60,13 +60,13 @@ export function useModelManagement(
|
|
|
60
60
|
const [userModels, setUserModels] = useState<string[]>([]);
|
|
61
61
|
const [loading, setLoading] = useState(false);
|
|
62
62
|
const [error, setError] = useState<string | null>(null);
|
|
63
|
+
const [apiUnavailable, setApiUnavailable] = useState(false);
|
|
63
64
|
|
|
64
|
-
// Utiliser baseUrl et apiKey du contexte
|
|
65
|
-
const effectiveOptions = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
65
|
+
// Utiliser baseUrl et apiKey du contexte avec memoization
|
|
66
|
+
const effectiveOptions = useMemo(() => ({
|
|
67
|
+
baseUrl: options.baseUrl || baseUrl,
|
|
68
|
+
apiKey: options.apiKey || apiKeyId,
|
|
69
|
+
}), [options.baseUrl, options.apiKey, baseUrl, apiKeyId]);
|
|
70
70
|
|
|
71
71
|
const refreshModels = useCallback(async () => {
|
|
72
72
|
try {
|
|
@@ -134,6 +134,12 @@ export function useModelManagement(
|
|
|
134
134
|
}, [category, effectiveOptions]);
|
|
135
135
|
|
|
136
136
|
const refreshUserModels = useCallback(async () => {
|
|
137
|
+
// Si l'API est marquée comme indisponible, ne pas faire l'appel
|
|
138
|
+
if (apiUnavailable) {
|
|
139
|
+
console.log("[useModelManagement] Skipping user models fetch - API marked as unavailable");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
137
143
|
try {
|
|
138
144
|
setLoading(true);
|
|
139
145
|
setError(null);
|
|
@@ -156,14 +162,16 @@ export function useModelManagement(
|
|
|
156
162
|
errorMessage
|
|
157
163
|
);
|
|
158
164
|
setError(errorMessage);
|
|
159
|
-
// En cas d'erreur 404 (API non disponible), utiliser un array vide
|
|
165
|
+
// En cas d'erreur 404 (API non disponible), utiliser un array vide et ne pas retry
|
|
160
166
|
if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
|
|
161
167
|
setUserModels([]);
|
|
168
|
+
setApiUnavailable(true);
|
|
169
|
+
console.warn("[useModelManagement] User models API not available (404), disabling further requests");
|
|
162
170
|
}
|
|
163
171
|
} finally {
|
|
164
172
|
setLoading(false);
|
|
165
173
|
}
|
|
166
|
-
}, [effectiveOptions]);
|
|
174
|
+
}, [effectiveOptions, apiUnavailable]);
|
|
167
175
|
|
|
168
176
|
const toggleModel = useCallback(
|
|
169
177
|
async (modelId: string, isActive?: boolean) => {
|
|
@@ -211,7 +219,7 @@ export function useModelManagement(
|
|
|
211
219
|
return availableModels.filter((model) => !userModels.includes(model.id));
|
|
212
220
|
}, [availableModels, userModels]);
|
|
213
221
|
|
|
214
|
-
// Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe)
|
|
222
|
+
// Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe) ET API disponible
|
|
215
223
|
useEffect(() => {
|
|
216
224
|
const isExternalProxy =
|
|
217
225
|
effectiveOptions.baseUrl &&
|
|
@@ -222,20 +230,23 @@ export function useModelManagement(
|
|
|
222
230
|
hasApiKey: !!effectiveOptions.apiKey,
|
|
223
231
|
hasBaseUrl: !!effectiveOptions.baseUrl,
|
|
224
232
|
isExternalProxy,
|
|
233
|
+
apiUnavailable,
|
|
225
234
|
apiKeyPreview: effectiveOptions.apiKey
|
|
226
235
|
? effectiveOptions.apiKey.substring(0, 10) + "..."
|
|
227
236
|
: "none",
|
|
228
237
|
baseUrl: effectiveOptions.baseUrl || "none",
|
|
229
238
|
});
|
|
230
239
|
|
|
231
|
-
// Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe
|
|
232
|
-
if (autoFetch && (effectiveOptions.apiKey || isExternalProxy)) {
|
|
240
|
+
// Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe ET API disponible
|
|
241
|
+
if (autoFetch && !apiUnavailable && (effectiveOptions.apiKey || isExternalProxy)) {
|
|
233
242
|
console.log("[useModelManagement] Starting auto-fetch...");
|
|
234
243
|
Promise.all([refreshModels(), refreshUserModels()]);
|
|
235
244
|
} else if (autoFetch && !effectiveOptions.apiKey && !isExternalProxy) {
|
|
236
245
|
console.warn(
|
|
237
246
|
"[useModelManagement] autoFetch is enabled but no apiKey or baseUrl provided. Skipping automatic fetch."
|
|
238
247
|
);
|
|
248
|
+
} else if (autoFetch && apiUnavailable) {
|
|
249
|
+
console.log("[useModelManagement] autoFetch skipped - API marked as unavailable");
|
|
239
250
|
}
|
|
240
251
|
}, [
|
|
241
252
|
autoFetch,
|
|
@@ -243,6 +254,7 @@ export function useModelManagement(
|
|
|
243
254
|
refreshUserModels,
|
|
244
255
|
effectiveOptions.apiKey,
|
|
245
256
|
effectiveOptions.baseUrl,
|
|
257
|
+
apiUnavailable,
|
|
246
258
|
]);
|
|
247
259
|
|
|
248
260
|
return {
|