@lastbrain/ai-ui-react 1.0.63 → 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.
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +1 -1
- package/dist/components/AiInput.d.ts +1 -1
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +16 -2
- package/dist/components/AiPromptPanel.d.ts +1 -0
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +39 -9
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +81 -12
- package/dist/components/AiTextarea.d.ts +1 -1
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +16 -2
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +143 -182
- package/dist/hooks/usePrompts.d.ts.map +1 -1
- package/dist/hooks/usePrompts.js +12 -4
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +23 -8
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +2 -0
- package/src/components/AiInput.tsx +18 -2
- package/src/components/AiPromptPanel.tsx +50 -9
- package/src/components/AiStatusButton.tsx +127 -13
- package/src/components/AiTextarea.tsx +18 -2
- package/src/context/LBAuthProvider.tsx +151 -208
- package/src/hooks/usePrompts.ts +11 -4
- package/src/utils/modelManagement.ts +25 -8
|
@@ -4,7 +4,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
4
4
|
* Provider d'authentification LastBrain
|
|
5
5
|
* Gère l'état de connexion, la session et les appels IA
|
|
6
6
|
*/
|
|
7
|
-
import { createContext, useContext, useEffect, useCallback, useState, } from "react";
|
|
7
|
+
import { createContext, useContext, useEffect, useCallback, useMemo, useState, } from "react";
|
|
8
|
+
import { createLBClient } from "@lastbrain/ai-ui-core";
|
|
8
9
|
const LBContext = createContext(undefined);
|
|
9
10
|
export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, onAuthChange, }) {
|
|
10
11
|
const [state, setState] = useState({
|
|
@@ -18,61 +19,70 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
18
19
|
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
19
20
|
const [isLoadingStorage, setIsLoadingStorage] = useState(false);
|
|
20
21
|
const [storageLastFetch, setStorageLastFetch] = useState(0);
|
|
22
|
+
const lbClient = useMemo(() => createLBClient({
|
|
23
|
+
baseUrl: proxyUrl,
|
|
24
|
+
mode: process.env.LB_API_KEY ? "env-key" : "auto",
|
|
25
|
+
selectedApiKeyId: state.selectedKey?.id,
|
|
26
|
+
}), [proxyUrl, state.selectedKey?.id]);
|
|
21
27
|
/**
|
|
22
28
|
* Vérifie si une session existe au chargement
|
|
23
29
|
*/
|
|
24
30
|
const checkSession = useCallback(async () => {
|
|
25
31
|
try {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const statusData = await statusResponse.json();
|
|
38
|
-
setState({
|
|
39
|
-
status: "ready",
|
|
40
|
-
session,
|
|
41
|
-
user: {
|
|
42
|
-
id: session.userId,
|
|
43
|
-
email: statusData.user?.email || "",
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
// Fallback sans email
|
|
49
|
-
setState({
|
|
50
|
-
status: "ready",
|
|
51
|
-
session,
|
|
52
|
-
user: {
|
|
53
|
-
id: session.userId,
|
|
54
|
-
email: "",
|
|
55
|
-
},
|
|
56
|
-
});
|
|
32
|
+
const session = await lbClient.verifySession();
|
|
33
|
+
if (session) {
|
|
34
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
35
|
+
const activeKey = userData?.apiKeyActive
|
|
36
|
+
? {
|
|
37
|
+
id: userData.apiKeyActive.id,
|
|
38
|
+
name: userData.apiKeyActive.name,
|
|
39
|
+
keyPrefix: userData.apiKeyActive.prefix,
|
|
40
|
+
scopes: userData.apiKeyActive.scopes || [],
|
|
41
|
+
isActive: true,
|
|
42
|
+
createdAt: "",
|
|
57
43
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
: undefined;
|
|
45
|
+
setApiKeys(userData?.apiKeys || []);
|
|
46
|
+
setState({
|
|
47
|
+
status: "ready",
|
|
48
|
+
session,
|
|
49
|
+
user: {
|
|
50
|
+
id: session.userId,
|
|
51
|
+
email: userData?.user?.email || "",
|
|
52
|
+
},
|
|
53
|
+
selectedKey: activeKey,
|
|
54
|
+
});
|
|
55
|
+
onStatusChange?.("ready");
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Supabase session mode (no lb_session cookie): try user endpoint directly
|
|
59
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
60
|
+
if (userData?.user?.id) {
|
|
61
|
+
const activeKey = userData?.apiKeyActive
|
|
62
|
+
? {
|
|
63
|
+
id: userData.apiKeyActive.id,
|
|
64
|
+
name: userData.apiKeyActive.name,
|
|
65
|
+
keyPrefix: userData.apiKeyActive.prefix,
|
|
66
|
+
scopes: userData.apiKeyActive.scopes || [],
|
|
67
|
+
isActive: true,
|
|
68
|
+
createdAt: "",
|
|
69
|
+
}
|
|
70
|
+
: undefined;
|
|
71
|
+
setApiKeys(userData.apiKeys || []);
|
|
62
72
|
setState({
|
|
63
73
|
status: "ready",
|
|
64
|
-
session,
|
|
65
74
|
user: {
|
|
66
|
-
id:
|
|
67
|
-
email: "",
|
|
75
|
+
id: userData.user.id,
|
|
76
|
+
email: userData.user.email || "",
|
|
68
77
|
},
|
|
78
|
+
selectedKey: activeKey,
|
|
69
79
|
});
|
|
80
|
+
onStatusChange?.("ready");
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
setState({ status: "needs_auth" });
|
|
84
|
+
onStatusChange?.("needs_auth");
|
|
70
85
|
}
|
|
71
|
-
onStatusChange?.("ready");
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
setState({ status: "needs_auth" });
|
|
75
|
-
onStatusChange?.("needs_auth");
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
catch (error) {
|
|
@@ -80,7 +90,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
80
90
|
setState({ status: "needs_auth" });
|
|
81
91
|
onStatusChange?.("needs_auth");
|
|
82
92
|
}
|
|
83
|
-
}, [
|
|
93
|
+
}, [lbClient, onStatusChange]);
|
|
84
94
|
useEffect(() => {
|
|
85
95
|
checkSession();
|
|
86
96
|
}, [checkSession]);
|
|
@@ -89,23 +99,8 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
89
99
|
*/
|
|
90
100
|
const fetchApiKeys = useCallback(async (token) => {
|
|
91
101
|
try {
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
headers: {
|
|
95
|
-
Authorization: `Bearer ${token}`,
|
|
96
|
-
"Content-Type": "application/json",
|
|
97
|
-
},
|
|
98
|
-
credentials: "include",
|
|
99
|
-
});
|
|
100
|
-
console.log("[LBProvider] API keys response status:", response.status);
|
|
101
|
-
if (!response.ok) {
|
|
102
|
-
const errorData = await response.json().catch(() => ({}));
|
|
103
|
-
console.error("[LBProvider] Failed to fetch API keys:", errorData);
|
|
104
|
-
throw new Error(errorData.message || "Failed to fetch API keys");
|
|
105
|
-
}
|
|
106
|
-
const data = await response.json();
|
|
107
|
-
console.log("[LBProvider] API keys received:", data);
|
|
108
|
-
const keys = data.apiKeys || data;
|
|
102
|
+
const data = await lbClient.getUser(token);
|
|
103
|
+
const keys = data.apiKeys || [];
|
|
109
104
|
setApiKeys(keys);
|
|
110
105
|
return keys;
|
|
111
106
|
}
|
|
@@ -113,7 +108,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
113
108
|
console.error("[LBProvider] Failed to fetch API keys:", error);
|
|
114
109
|
throw error;
|
|
115
110
|
}
|
|
116
|
-
}, [
|
|
111
|
+
}, [lbClient]);
|
|
117
112
|
/**
|
|
118
113
|
* Sélectionne une clé API et crée une session
|
|
119
114
|
*/
|
|
@@ -121,22 +116,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
121
116
|
try {
|
|
122
117
|
console.log("[LBProvider] Selecting API key:", apiKeyId);
|
|
123
118
|
setState((prev) => ({ ...prev, status: "loading" }));
|
|
124
|
-
const
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers: {
|
|
127
|
-
"Content-Type": "application/json",
|
|
128
|
-
Authorization: `Bearer ${token}`,
|
|
129
|
-
},
|
|
130
|
-
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
131
|
-
credentials: "include",
|
|
132
|
-
});
|
|
133
|
-
console.log("[LBProvider] Session response status:", response.status);
|
|
134
|
-
if (!response.ok) {
|
|
135
|
-
const errorData = await response.json().catch(() => ({}));
|
|
136
|
-
console.error("[LBProvider] Failed to create session:", errorData);
|
|
137
|
-
throw new Error(errorData.message || "Failed to create session");
|
|
138
|
-
}
|
|
139
|
-
const sessionResult = await response.json();
|
|
119
|
+
const sessionResult = await lbClient.selectApiKey(apiKeyId, token);
|
|
140
120
|
console.log("[LBProvider] Session created successfully:", sessionResult);
|
|
141
121
|
setState({
|
|
142
122
|
status: "ready",
|
|
@@ -162,7 +142,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
162
142
|
});
|
|
163
143
|
throw error;
|
|
164
144
|
}
|
|
165
|
-
}, [
|
|
145
|
+
}, [lbClient, state.user, onStatusChange, onAuthChange]);
|
|
166
146
|
/**
|
|
167
147
|
* Connexion utilisateur (étape 1 : login)
|
|
168
148
|
* Retourne le token et les clés API sans créer de session
|
|
@@ -171,24 +151,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
171
151
|
try {
|
|
172
152
|
console.log("[LBProvider] Login attempt:", email);
|
|
173
153
|
setState((prev) => ({ ...prev, status: "loading" }));
|
|
174
|
-
const
|
|
175
|
-
method: "POST",
|
|
176
|
-
headers: { "Content-Type": "application/json" },
|
|
177
|
-
body: JSON.stringify({ email, password }),
|
|
178
|
-
credentials: "include",
|
|
179
|
-
});
|
|
180
|
-
console.log("[LBProvider] Login response status:", response.status);
|
|
181
|
-
if (!response.ok) {
|
|
182
|
-
const error = await response.json();
|
|
183
|
-
const errorMessage = error.message || "Login failed";
|
|
184
|
-
console.error("[LBProvider] Login failed:", errorMessage);
|
|
185
|
-
setState({
|
|
186
|
-
status: "needs_auth",
|
|
187
|
-
error: errorMessage,
|
|
188
|
-
});
|
|
189
|
-
return { success: false, error: errorMessage };
|
|
190
|
-
}
|
|
191
|
-
const result = await response.json();
|
|
154
|
+
const result = await lbClient.login(email, password);
|
|
192
155
|
console.log("[LBProvider] Login successful:", result.user?.email);
|
|
193
156
|
console.log("[LBProvider] Access token received:", result.accessToken ? "YES" : "NO");
|
|
194
157
|
console.log("[LBProvider] Token length:", result.accessToken?.length || 0);
|
|
@@ -259,7 +222,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
259
222
|
});
|
|
260
223
|
return { success: false, error: message };
|
|
261
224
|
}
|
|
262
|
-
}, [
|
|
225
|
+
}, [lbClient, fetchApiKeys, selectApiKey]);
|
|
263
226
|
/**
|
|
264
227
|
* Récupère le status API basique (balance, API key info) - RAPIDE
|
|
265
228
|
*/
|
|
@@ -271,22 +234,43 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
271
234
|
}
|
|
272
235
|
setIsLoadingStatus(true);
|
|
273
236
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (response.ok) {
|
|
278
|
-
const data = await response.json();
|
|
279
|
-
setBasicStatus(data);
|
|
280
|
-
// Combiner avec le storage existant si disponible
|
|
281
|
-
const combinedStatus = {
|
|
282
|
-
...data,
|
|
283
|
-
storage: storageStatus?.storage || data.storage,
|
|
284
|
-
};
|
|
285
|
-
setApiStatus(combinedStatus);
|
|
237
|
+
let data;
|
|
238
|
+
try {
|
|
239
|
+
data = await lbClient.getStatus();
|
|
286
240
|
}
|
|
287
|
-
|
|
288
|
-
|
|
241
|
+
catch {
|
|
242
|
+
// Backward compatibility: older backends may not expose /auth/status
|
|
243
|
+
const userData = await lbClient.getUser();
|
|
244
|
+
data = {
|
|
245
|
+
authType: userData.authType,
|
|
246
|
+
user: userData.user,
|
|
247
|
+
apiKey: userData.apiKeyActive,
|
|
248
|
+
api_key: userData.apiKeyActive,
|
|
249
|
+
balance: {
|
|
250
|
+
used: 0,
|
|
251
|
+
total: userData.balance?.sellValueUsd || 0,
|
|
252
|
+
percentage: 0,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
289
255
|
}
|
|
256
|
+
const normalizedStatus = {
|
|
257
|
+
...data,
|
|
258
|
+
authType: data?.authType,
|
|
259
|
+
user: data?.user,
|
|
260
|
+
apiKey: data?.apiKey || data?.api_key,
|
|
261
|
+
api_key: data?.api_key || data?.apiKey,
|
|
262
|
+
balance: data?.balance || {
|
|
263
|
+
used: 0,
|
|
264
|
+
total: 0,
|
|
265
|
+
percentage: 0,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
setBasicStatus(normalizedStatus);
|
|
269
|
+
const combinedStatus = {
|
|
270
|
+
...normalizedStatus,
|
|
271
|
+
storage: storageStatus?.storage,
|
|
272
|
+
};
|
|
273
|
+
setApiStatus(combinedStatus);
|
|
290
274
|
}
|
|
291
275
|
catch (error) {
|
|
292
276
|
console.error("[LBProvider] Failed to fetch basic status:", error);
|
|
@@ -295,7 +279,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
295
279
|
finally {
|
|
296
280
|
setIsLoadingStatus(false);
|
|
297
281
|
}
|
|
298
|
-
}, [
|
|
282
|
+
}, [lbClient, state.status, storageStatus]);
|
|
299
283
|
/**
|
|
300
284
|
* Récupère le storage - LENT avec cache (5 minutes)
|
|
301
285
|
*/
|
|
@@ -315,35 +299,16 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
315
299
|
}
|
|
316
300
|
setIsLoadingStorage(true);
|
|
317
301
|
try {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
if (response.ok) {
|
|
330
|
-
const data = await response.json();
|
|
331
|
-
// Si c'est la réponse complète (fallback), extraire juste le storage
|
|
332
|
-
const storageData = data.storage ? { storage: data.storage } : data;
|
|
333
|
-
setStorageStatus(storageData);
|
|
334
|
-
setStorageLastFetch(now);
|
|
335
|
-
// Combiner avec le basic status
|
|
336
|
-
const combinedStatus = {
|
|
337
|
-
...basicStatus,
|
|
338
|
-
storage: storageData.storage,
|
|
339
|
-
};
|
|
340
|
-
setApiStatus(combinedStatus);
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
console.warn("[LBProvider] Failed to fetch storage status:", response.status);
|
|
344
|
-
// Arrêter les tentatives répétées si échec persistant
|
|
345
|
-
setStorageLastFetch(now); // Marquer comme essayé pour éviter la boucle
|
|
346
|
-
}
|
|
302
|
+
const data = await lbClient.getStorageStatus();
|
|
303
|
+
const storageData = data?.storage ? { storage: data.storage } : data;
|
|
304
|
+
setStorageStatus(storageData);
|
|
305
|
+
setStorageLastFetch(now);
|
|
306
|
+
// Combiner avec le basic status
|
|
307
|
+
const combinedStatus = {
|
|
308
|
+
...basicStatus,
|
|
309
|
+
storage: storageData?.storage,
|
|
310
|
+
};
|
|
311
|
+
setApiStatus(combinedStatus);
|
|
347
312
|
}
|
|
348
313
|
catch (error) {
|
|
349
314
|
console.error("[LBProvider] Failed to fetch storage status:", error);
|
|
@@ -353,7 +318,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
353
318
|
finally {
|
|
354
319
|
setIsLoadingStorage(false);
|
|
355
320
|
}
|
|
356
|
-
}, [
|
|
321
|
+
}, [lbClient, state.status, basicStatus, storageLastFetch]);
|
|
357
322
|
/**
|
|
358
323
|
* Sélectionne une clé API avec le token déjà stocké (après login)
|
|
359
324
|
*/
|
|
@@ -367,35 +332,40 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
367
332
|
* Change l'API key active - utilise la bonne méthode selon le contexte
|
|
368
333
|
*/
|
|
369
334
|
const switchApiKey = useCallback(async (apiKeyId) => {
|
|
370
|
-
if (state.status === "ready") {
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
335
|
+
if (state.status === "ready" || 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
|
+
}
|
|
350
|
+
const selectedKey = apiKeys.find((key) => key.id === apiKeyId);
|
|
351
|
+
if (selectedKey) {
|
|
352
|
+
setState((prev) => ({
|
|
353
|
+
...prev,
|
|
354
|
+
selectedKey,
|
|
355
|
+
}));
|
|
381
356
|
}
|
|
382
|
-
// Refresh le status après le changement
|
|
383
357
|
await refreshBasicStatus();
|
|
384
|
-
// Refresh storage en arrière-plan
|
|
385
358
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
386
359
|
}
|
|
387
|
-
else if (accessToken) {
|
|
388
|
-
// Utiliser la méthode avec access token
|
|
389
|
-
await selectApiKey(accessToken, apiKeyId);
|
|
390
|
-
}
|
|
391
360
|
else {
|
|
392
361
|
throw new Error("No valid authentication method available");
|
|
393
362
|
}
|
|
394
363
|
}, [
|
|
395
364
|
state.status,
|
|
396
|
-
|
|
365
|
+
state.session?.sessionToken,
|
|
397
366
|
accessToken,
|
|
398
|
-
|
|
367
|
+
apiKeys,
|
|
368
|
+
lbClient,
|
|
399
369
|
refreshBasicStatus,
|
|
400
370
|
refreshStorageStatus,
|
|
401
371
|
]);
|
|
@@ -404,10 +374,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
404
374
|
*/
|
|
405
375
|
const logout = useCallback(async () => {
|
|
406
376
|
try {
|
|
407
|
-
await
|
|
408
|
-
method: "POST",
|
|
409
|
-
credentials: "include",
|
|
410
|
-
});
|
|
377
|
+
await lbClient.logout();
|
|
411
378
|
}
|
|
412
379
|
catch (error) {
|
|
413
380
|
console.error("[LBProvider] Logout failed:", error);
|
|
@@ -416,10 +383,15 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
416
383
|
setState({ status: "needs_auth" });
|
|
417
384
|
setApiKeys([]);
|
|
418
385
|
setAccessToken(undefined);
|
|
386
|
+
// Reset tous les statuts après logout
|
|
387
|
+
setApiStatus(null);
|
|
388
|
+
setBasicStatus(null);
|
|
389
|
+
setStorageStatus(null);
|
|
390
|
+
setStorageLastFetch(0);
|
|
419
391
|
onStatusChange?.("needs_auth");
|
|
420
392
|
onAuthChange?.(); // Refresh provider after logout
|
|
421
393
|
}
|
|
422
|
-
}, [
|
|
394
|
+
}, [lbClient, onStatusChange, onAuthChange]);
|
|
423
395
|
/**
|
|
424
396
|
* Recharge la session
|
|
425
397
|
*/
|
|
@@ -435,25 +407,14 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
435
407
|
return;
|
|
436
408
|
}
|
|
437
409
|
try {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
credentials: "include",
|
|
441
|
-
});
|
|
442
|
-
if (response.ok) {
|
|
443
|
-
const data = await response.json();
|
|
444
|
-
console.log("[LBProvider] API keys received:", data);
|
|
445
|
-
setApiKeys(data.apiKeys || []);
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
console.warn("[LBProvider] Failed to fetch API keys:", response.status);
|
|
449
|
-
setApiKeys([]);
|
|
450
|
-
}
|
|
410
|
+
const data = await lbClient.getUser();
|
|
411
|
+
setApiKeys(data.apiKeys || []);
|
|
451
412
|
}
|
|
452
413
|
catch (error) {
|
|
453
414
|
console.error("[LBProvider] Failed to fetch API keys:", error);
|
|
454
415
|
setApiKeys([]);
|
|
455
416
|
}
|
|
456
|
-
}, [
|
|
417
|
+
}, [lbClient, state.status]);
|
|
457
418
|
// Refresh status quand la session devient ready
|
|
458
419
|
useEffect(() => {
|
|
459
420
|
if (state.status === "ready") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePrompts.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrompts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,YAAY,EAAE,CACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"usePrompts.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrompts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,YAAY,EAAE,CACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,CAuS7C"}
|
package/dist/hooks/usePrompts.js
CHANGED
|
@@ -7,6 +7,14 @@ export function usePrompts() {
|
|
|
7
7
|
const [prompts, setPrompts] = useState([]);
|
|
8
8
|
const [loading, setLoading] = useState(false);
|
|
9
9
|
const [error, setError] = useState(null);
|
|
10
|
+
const isAuthTokenCandidate = (value) => {
|
|
11
|
+
if (!value)
|
|
12
|
+
return false;
|
|
13
|
+
const token = value.trim();
|
|
14
|
+
if (!token)
|
|
15
|
+
return false;
|
|
16
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
17
|
+
};
|
|
10
18
|
const fetchPrompts = useCallback(async (options) => {
|
|
11
19
|
try {
|
|
12
20
|
setLoading(true);
|
|
@@ -71,7 +79,7 @@ export function usePrompts() {
|
|
|
71
79
|
console.log("[usePrompts] Fetching prompts from:", endpoint);
|
|
72
80
|
const headers = {};
|
|
73
81
|
// Ajouter l'API key pour les appels publics directs (pas de proxy externe ni auth interne)
|
|
74
|
-
if (isPublicApi && apiKeyId) {
|
|
82
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
75
83
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
76
84
|
}
|
|
77
85
|
const response = await fetch(endpoint, {
|
|
@@ -110,7 +118,7 @@ export function usePrompts() {
|
|
|
110
118
|
? `${baseUrl}/auth/prompts` // Proxy ajoutera /api/ai/
|
|
111
119
|
: "/api/ai/auth/prompts";
|
|
112
120
|
const headers = { "Content-Type": "application/json" };
|
|
113
|
-
if (!isExternalProxy && apiKeyId) {
|
|
121
|
+
if (!isExternalProxy && isAuthTokenCandidate(apiKeyId)) {
|
|
114
122
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
115
123
|
}
|
|
116
124
|
const response = await fetch(endpoint, {
|
|
@@ -148,7 +156,7 @@ export function usePrompts() {
|
|
|
148
156
|
? `${baseUrl}/auth/prompts`
|
|
149
157
|
: "/api/ai/auth/prompts";
|
|
150
158
|
const headers = { "Content-Type": "application/json" };
|
|
151
|
-
if (isPublicApi && apiKeyId) {
|
|
159
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
152
160
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
153
161
|
}
|
|
154
162
|
const response = await fetch(endpoint, {
|
|
@@ -186,7 +194,7 @@ export function usePrompts() {
|
|
|
186
194
|
? `${baseUrl}/auth/prompts?id=${id}`
|
|
187
195
|
: `/api/ai/auth/prompts?id=${id}`;
|
|
188
196
|
const headers = {};
|
|
189
|
-
if (isPublicApi && apiKeyId) {
|
|
197
|
+
if (isPublicApi && isAuthTokenCandidate(apiKeyId)) {
|
|
190
198
|
headers["Authorization"] = `Bearer ${apiKeyId}`;
|
|
191
199
|
}
|
|
192
200
|
const response = await fetch(endpoint, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CACR,KAAK,CAAC;IACJ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CACH,CAsDA;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CAkDnB"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilitaires pour la gestion des modèles IA
|
|
3
3
|
*/
|
|
4
|
+
function isAuthTokenCandidate(value) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return false;
|
|
7
|
+
const token = value.trim();
|
|
8
|
+
if (!token)
|
|
9
|
+
return false;
|
|
10
|
+
// Raw API key or JWT access token
|
|
11
|
+
return token.startsWith("lb_") || token.split(".").length === 3;
|
|
12
|
+
}
|
|
4
13
|
/**
|
|
5
14
|
* Active ou désactive un modèle pour l'utilisateur courant
|
|
6
15
|
*/
|
|
@@ -10,7 +19,7 @@ export async function toggleUserModel(modelId, isActive, options = {}) {
|
|
|
10
19
|
"Content-Type": "application/json",
|
|
11
20
|
};
|
|
12
21
|
// Ajouter la clé API si fournie
|
|
13
|
-
if (apiKey) {
|
|
22
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
14
23
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
15
24
|
}
|
|
16
25
|
const isPublicApi = baseUrl && baseUrl.includes("/api/public/v1");
|
|
@@ -45,16 +54,16 @@ export async function getAvailableModels(options = {}) {
|
|
|
45
54
|
const isExternalProxy = baseUrl && baseUrl.includes("/api/lastbrain");
|
|
46
55
|
const isPublicApi = baseUrl && baseUrl.includes("/api/public/v1");
|
|
47
56
|
const endpoint = isExternalProxy
|
|
48
|
-
? `${baseUrl}/
|
|
57
|
+
? `${baseUrl}/auth/models` // Proxy routes to /api/ai/auth/models
|
|
49
58
|
: isPublicApi
|
|
50
|
-
? `${baseUrl}/
|
|
59
|
+
? `${baseUrl}/gateway-models` // → /api/public/v1/gateway-models
|
|
51
60
|
: baseUrl
|
|
52
|
-
? `${baseUrl}/auth/
|
|
53
|
-
: `/api/ai/auth/
|
|
61
|
+
? `${baseUrl}/auth/models` // → /api/ai/auth/models
|
|
62
|
+
: `/api/ai/auth/models`; // fallback
|
|
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, {
|
|
@@ -66,7 +75,13 @@ export async function getAvailableModels(options = {}) {
|
|
|
66
75
|
throw new Error(`Erreur lors de la récupération des modèles: ${response.status}`);
|
|
67
76
|
}
|
|
68
77
|
const data = await response.json();
|
|
69
|
-
|
|
78
|
+
if (Array.isArray(data?.models)) {
|
|
79
|
+
return data.models;
|
|
80
|
+
}
|
|
81
|
+
if (Array.isArray(data?.providers)) {
|
|
82
|
+
return data.providers.flatMap((provider) => Array.isArray(provider.models) ? provider.models : []);
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
70
85
|
}
|
|
71
86
|
/**
|
|
72
87
|
* Récupère les modèles activés par l'utilisateur avec API key
|
|
@@ -86,7 +101,7 @@ export async function getUserModels(options = {}) {
|
|
|
86
101
|
console.log("[getUserModels] isExternalProxy:", isExternalProxy, "isPublicApi:", isPublicApi, "endpoint:", endpoint);
|
|
87
102
|
const headers = {};
|
|
88
103
|
// Ajouter la clé API pour tous les types d'appels si disponible
|
|
89
|
-
if (apiKey) {
|
|
104
|
+
if (isAuthTokenCandidate(apiKey)) {
|
|
90
105
|
if (isPublicApi) {
|
|
91
106
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
92
107
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/ai-ui-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.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.
|
|
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 */}
|