@lastbrain/ai-ui-react 1.0.69 → 1.0.71
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 +1 -0
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +4 -1
- package/dist/components/AiContextButton.d.ts +6 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +11 -4
- package/dist/components/AiImageButton.d.ts +1 -0
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +1 -0
- package/dist/components/AiInput.d.ts +1 -0
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +1 -0
- package/dist/components/AiModelSelect.d.ts +1 -0
- package/dist/components/AiModelSelect.d.ts.map +1 -1
- package/dist/components/AiModelSelect.js +1 -0
- package/dist/components/AiPromptPanel.d.ts +1 -0
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +13 -6
- package/dist/components/AiSelect.d.ts +1 -0
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +1 -0
- package/dist/components/AiSettingsButton.d.ts +1 -0
- package/dist/components/AiSettingsButton.d.ts.map +1 -1
- package/dist/components/AiSettingsButton.js +1 -0
- package/dist/components/AiStatusButton.d.ts +1 -0
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +21 -13
- package/dist/components/AiTextarea.d.ts +1 -0
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +1 -0
- package/dist/components/ErrorToast.d.ts +1 -0
- package/dist/components/ErrorToast.d.ts.map +1 -1
- package/dist/components/ErrorToast.js +1 -0
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBConnectButton.d.ts +1 -0
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +2 -1
- package/dist/components/LBKeyPicker.d.ts +1 -0
- package/dist/components/LBKeyPicker.d.ts.map +1 -1
- package/dist/components/LBKeyPicker.js +1 -0
- package/dist/components/LBSigninModal.d.ts +1 -0
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +2 -1
- package/dist/components/UsageToast.d.ts +1 -0
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/components/UsageToast.js +1 -0
- package/dist/context/LBAuthProvider.d.ts +35 -3
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +2 -0
- package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -1
- package/dist/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useModelManagement.d.ts.map +1 -1
- package/dist/styles/register.d.ts +3 -0
- package/dist/styles/register.d.ts.map +1 -0
- package/dist/styles/register.js +1 -0
- package/dist/utils/errorHandler.d.ts +2 -2
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/dist/utils/errorHandler.js +8 -1
- package/dist/utils/modelManagement.d.ts +13 -10
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +19 -2
- package/package.json +5 -2
- package/src/components/AiChipLabel.tsx +6 -1
- package/src/components/AiContextButton.tsx +51 -23
- package/src/components/AiImageButton.tsx +1 -0
- package/src/components/AiInput.tsx +1 -0
- package/src/components/AiModelSelect.tsx +1 -0
- package/src/components/AiPromptPanel.tsx +20 -15
- package/src/components/AiSelect.tsx +1 -0
- package/src/components/AiSettingsButton.tsx +1 -0
- package/src/components/AiStatusButton.tsx +33 -19
- package/src/components/AiTextarea.tsx +4 -1
- package/src/components/ErrorToast.tsx +1 -0
- package/src/components/LBApiKeySelector.tsx +13 -3
- package/src/components/LBConnectButton.tsx +4 -12
- package/src/components/LBKeyPicker.tsx +1 -0
- package/src/components/LBSigninModal.tsx +21 -10
- package/src/components/UsageToast.tsx +1 -0
- package/src/context/LBAuthProvider.tsx +47 -7
- package/src/examples/AiUiPremiumShowcase.tsx +4 -1
- package/src/hooks/useAiModels.ts +2 -1
- package/src/hooks/useModelManagement.ts +2 -1
- package/src/styles/register.ts +3 -0
- package/src/utils/errorHandler.ts +16 -3
- package/src/utils/modelManagement.ts +53 -15
|
@@ -45,7 +45,10 @@ export function LBApiKeySelector({
|
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
47
|
<div className="ai-signin-overlay" onClick={onCancel}>
|
|
48
|
-
<div
|
|
48
|
+
<div
|
|
49
|
+
className="ai-signin-panel ai-key-modal-panel"
|
|
50
|
+
onClick={(e) => e.stopPropagation()}
|
|
51
|
+
>
|
|
49
52
|
<div className="ai-signin-header">
|
|
50
53
|
<div className="ai-center mb-3">
|
|
51
54
|
<span className="ai-icon-badge">
|
|
@@ -87,7 +90,9 @@ export function LBApiKeySelector({
|
|
|
87
90
|
<div>
|
|
88
91
|
<div className="ai-model-item-title">{key.name}</div>
|
|
89
92
|
<div className="ai-model-item-meta">
|
|
90
|
-
<span>
|
|
93
|
+
<span>
|
|
94
|
+
{key.keyPrefix || key.id.substring(0, 12) + "..."}
|
|
95
|
+
</span>
|
|
91
96
|
</div>
|
|
92
97
|
</div>
|
|
93
98
|
</div>
|
|
@@ -115,7 +120,12 @@ export function LBApiKeySelector({
|
|
|
115
120
|
) : null}
|
|
116
121
|
|
|
117
122
|
<div className="ai-signin-actions">
|
|
118
|
-
<button
|
|
123
|
+
<button
|
|
124
|
+
type="button"
|
|
125
|
+
onClick={onCancel}
|
|
126
|
+
disabled={loading}
|
|
127
|
+
className="ai-btn ai-btn--ghost"
|
|
128
|
+
>
|
|
119
129
|
Annuler
|
|
120
130
|
</button>
|
|
121
131
|
<button
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import "../styles/register";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { Loader2, LogIn, LogOut } from "lucide-react";
|
|
5
6
|
import { useLB } from "../context/LBAuthProvider";
|
|
@@ -38,8 +39,7 @@ export function LBConnectButton({
|
|
|
38
39
|
onOpenModal?.();
|
|
39
40
|
};
|
|
40
41
|
|
|
41
|
-
const buttonLabel =
|
|
42
|
-
status === "ready" && user ? "Déconnexion" : label;
|
|
42
|
+
const buttonLabel = status === "ready" && user ? "Déconnexion" : label;
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
45
|
<>
|
|
@@ -67,10 +67,7 @@ export function LBConnectButton({
|
|
|
67
67
|
)}
|
|
68
68
|
</button>
|
|
69
69
|
|
|
70
|
-
<LBSigninModal
|
|
71
|
-
isOpen={showModal}
|
|
72
|
-
onClose={() => setShowModal(false)}
|
|
73
|
-
/>
|
|
70
|
+
<LBSigninModal isOpen={showModal} onClose={() => setShowModal(false)} />
|
|
74
71
|
</>
|
|
75
72
|
);
|
|
76
73
|
}
|
|
@@ -84,10 +81,5 @@ interface LBAuthModalProps {
|
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
export function LBAuthModal({ onClose }: LBAuthModalProps) {
|
|
87
|
-
return (
|
|
88
|
-
<LBSigninModal
|
|
89
|
-
isOpen
|
|
90
|
-
onClose={() => onClose(false)}
|
|
91
|
-
/>
|
|
92
|
-
);
|
|
84
|
+
return <LBSigninModal isOpen onClose={() => onClose(false)} />;
|
|
93
85
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import "../styles/register";
|
|
3
4
|
import { useEffect, useMemo, useState } from "react";
|
|
4
5
|
import { createPortal } from "react-dom";
|
|
5
6
|
import { AlertCircle, Loader2, Lock, Mail, Sparkles, X } from "lucide-react";
|
|
@@ -22,11 +23,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
22
23
|
const [showKeySelector, setShowKeySelector] = useState(false);
|
|
23
24
|
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]);
|
|
24
25
|
|
|
25
|
-
const {
|
|
26
|
-
login,
|
|
27
|
-
selectApiKeyWithToken,
|
|
28
|
-
fetchApiKeys,
|
|
29
|
-
} = lbContext || {};
|
|
26
|
+
const { login, selectApiKeyWithToken, fetchApiKeys } = lbContext || {};
|
|
30
27
|
|
|
31
28
|
const canRender = Boolean(isOpen && lbContext && login);
|
|
32
29
|
|
|
@@ -75,7 +72,9 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
75
72
|
setError("Erreur lors de la récupération des clés API");
|
|
76
73
|
}
|
|
77
74
|
} catch (err) {
|
|
78
|
-
setError(
|
|
75
|
+
setError(
|
|
76
|
+
err instanceof Error ? err.message : "Une erreur s'est produite"
|
|
77
|
+
);
|
|
79
78
|
} finally {
|
|
80
79
|
setLoading(false);
|
|
81
80
|
}
|
|
@@ -91,7 +90,9 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
91
90
|
setShowKeySelector(false);
|
|
92
91
|
onClose();
|
|
93
92
|
} catch (err) {
|
|
94
|
-
setError(
|
|
93
|
+
setError(
|
|
94
|
+
err instanceof Error ? err.message : "Erreur lors de la sélection"
|
|
95
|
+
);
|
|
95
96
|
setShowKeySelector(false);
|
|
96
97
|
}
|
|
97
98
|
};
|
|
@@ -151,7 +152,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
151
152
|
<div className="ai-signin-content">
|
|
152
153
|
<form onSubmit={handleSubmit}>
|
|
153
154
|
<div className="ai-input-row">
|
|
154
|
-
<label
|
|
155
|
+
<label
|
|
156
|
+
htmlFor="lb-signin-email"
|
|
157
|
+
className="ai-input-label ai-row"
|
|
158
|
+
>
|
|
155
159
|
<Mail size={14} className="ai-inline-icon" />
|
|
156
160
|
Email
|
|
157
161
|
</label>
|
|
@@ -173,7 +177,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
173
177
|
</div>
|
|
174
178
|
|
|
175
179
|
<div className="ai-input-row">
|
|
176
|
-
<label
|
|
180
|
+
<label
|
|
181
|
+
htmlFor="lb-signin-password"
|
|
182
|
+
className="ai-input-label ai-row"
|
|
183
|
+
>
|
|
177
184
|
<Lock size={14} className="ai-inline-icon" />
|
|
178
185
|
Mot de passe
|
|
179
186
|
</label>
|
|
@@ -201,7 +208,11 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
201
208
|
) : null}
|
|
202
209
|
|
|
203
210
|
<div className="ai-signin-actions">
|
|
204
|
-
<button
|
|
211
|
+
<button
|
|
212
|
+
type="submit"
|
|
213
|
+
className="ai-btn ai-btn--auth"
|
|
214
|
+
disabled={loading}
|
|
215
|
+
>
|
|
205
216
|
{loading ? (
|
|
206
217
|
<>
|
|
207
218
|
<Loader2 size={16} className="ai-spinner" />
|
|
@@ -17,13 +17,45 @@ import {
|
|
|
17
17
|
import type {
|
|
18
18
|
LBAuthState,
|
|
19
19
|
LBApiKey,
|
|
20
|
-
LBSession,
|
|
21
20
|
LBLoginResult,
|
|
22
21
|
LBSessionResult,
|
|
23
22
|
AiStatus,
|
|
24
23
|
} from "@lastbrain/ai-ui-core";
|
|
25
24
|
import { createLBClient } from "@lastbrain/ai-ui-core";
|
|
26
25
|
|
|
26
|
+
export interface ApiKeyUser {
|
|
27
|
+
id?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
prefix?: string;
|
|
30
|
+
env?: string;
|
|
31
|
+
rate_limit_rpm?: number;
|
|
32
|
+
scopes?: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface BasicStatus {
|
|
36
|
+
authType?: string;
|
|
37
|
+
user?: {
|
|
38
|
+
id?: string;
|
|
39
|
+
email?: string;
|
|
40
|
+
} | null;
|
|
41
|
+
apiKey?: ApiKeyUser | null;
|
|
42
|
+
api_key?: ApiKeyUser | null;
|
|
43
|
+
balance?: {
|
|
44
|
+
used?: number;
|
|
45
|
+
total?: number;
|
|
46
|
+
percentage?: number;
|
|
47
|
+
};
|
|
48
|
+
storage?: StorageStatus["storage"];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StorageStatus {
|
|
52
|
+
storage?: {
|
|
53
|
+
used_mb?: number;
|
|
54
|
+
allocated_mb?: number;
|
|
55
|
+
percentage?: number;
|
|
56
|
+
} | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
27
59
|
interface LBProviderProps {
|
|
28
60
|
children: ReactNode;
|
|
29
61
|
/** URL de l'API LastBrain (ex: https://api.lastbrain.io) */
|
|
@@ -66,9 +98,9 @@ interface LBContextValue extends LBAuthState {
|
|
|
66
98
|
/** Status API (balance, storage, API key info) */
|
|
67
99
|
apiStatus: AiStatus | null;
|
|
68
100
|
/** Status basique (rapide) - balance, API key info sans storage */
|
|
69
|
-
basicStatus:
|
|
101
|
+
basicStatus: BasicStatus | null;
|
|
70
102
|
/** Status storage (lent) avec cache */
|
|
71
|
-
storageStatus:
|
|
103
|
+
storageStatus: StorageStatus | null;
|
|
72
104
|
/** Fonction pour rafraîchir le status rapide */
|
|
73
105
|
refreshBasicStatus: () => Promise<void>;
|
|
74
106
|
/** Fonction pour rafraîchir le storage (avec cache optionnel) */
|
|
@@ -83,6 +115,9 @@ interface LBContextValue extends LBAuthState {
|
|
|
83
115
|
|
|
84
116
|
const LBContext = createContext<LBContextValue | undefined>(undefined);
|
|
85
117
|
|
|
118
|
+
// Export pour usage dans d'autres composants
|
|
119
|
+
export { LBContext };
|
|
120
|
+
|
|
86
121
|
export function LBProvider({
|
|
87
122
|
children,
|
|
88
123
|
baseUrl: _baseUrl = "/api/lastbrain",
|
|
@@ -96,8 +131,10 @@ export function LBProvider({
|
|
|
96
131
|
const [apiKeys, setApiKeys] = useState<LBApiKey[]>([]);
|
|
97
132
|
const [accessToken, setAccessToken] = useState<string>();
|
|
98
133
|
const [apiStatus, setApiStatus] = useState<AiStatus | null>(null);
|
|
99
|
-
const [basicStatus, setBasicStatus] = useState<
|
|
100
|
-
const [storageStatus, setStorageStatus] = useState<
|
|
134
|
+
const [basicStatus, setBasicStatus] = useState<BasicStatus | null>(null);
|
|
135
|
+
const [storageStatus, setStorageStatus] = useState<StorageStatus | null>(
|
|
136
|
+
null
|
|
137
|
+
);
|
|
101
138
|
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
102
139
|
const [isLoadingStorage, setIsLoadingStorage] = useState(false);
|
|
103
140
|
const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
|
|
@@ -373,7 +410,7 @@ export function LBProvider({
|
|
|
373
410
|
|
|
374
411
|
setIsLoadingStatus(true);
|
|
375
412
|
try {
|
|
376
|
-
let data:
|
|
413
|
+
let data: BasicStatus;
|
|
377
414
|
try {
|
|
378
415
|
data = await lbClient.getStatus();
|
|
379
416
|
} catch {
|
|
@@ -452,7 +489,7 @@ export function LBProvider({
|
|
|
452
489
|
...basicStatus,
|
|
453
490
|
storage: storageData?.storage,
|
|
454
491
|
};
|
|
455
|
-
setApiStatus(combinedStatus);
|
|
492
|
+
setApiStatus(combinedStatus as AiStatus);
|
|
456
493
|
} catch (error) {
|
|
457
494
|
console.error("[LBProvider] Failed to fetch storage status:", error);
|
|
458
495
|
// Arrêter les tentatives répétées si erreur persistante
|
|
@@ -619,3 +656,6 @@ export function useLB(): LBContextValue {
|
|
|
619
656
|
}
|
|
620
657
|
return context;
|
|
621
658
|
}
|
|
659
|
+
|
|
660
|
+
// Re-export des types pour usage externe
|
|
661
|
+
export type { LBApiKey };
|
|
@@ -56,7 +56,10 @@ function ShowcasePanel({
|
|
|
56
56
|
<Sparkles size={14} className="text-[var(--ai-primary)]" />
|
|
57
57
|
</div>
|
|
58
58
|
<div className="mt-3 space-y-2">
|
|
59
|
-
<input
|
|
59
|
+
<input
|
|
60
|
+
className="ai-control ai-control-input"
|
|
61
|
+
placeholder="Default state"
|
|
62
|
+
/>
|
|
60
63
|
<input
|
|
61
64
|
className="ai-control ai-control-input ai-control--focused"
|
|
62
65
|
placeholder="Focused style"
|
package/src/hooks/useAiModels.ts
CHANGED
|
@@ -26,7 +26,8 @@ export function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult {
|
|
|
26
26
|
|
|
27
27
|
// Utiliser le contexte dès que la base URL correspond.
|
|
28
28
|
// L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
|
|
29
|
-
const useContextData =
|
|
29
|
+
const useContextData =
|
|
30
|
+
!options?.baseUrl || options.baseUrl === context.baseUrl;
|
|
30
31
|
|
|
31
32
|
// Filtrer les modèles selon le type demandé
|
|
32
33
|
const filteredModels = useMemo(() => {
|
|
@@ -44,7 +44,8 @@ export function useModelManagement(
|
|
|
44
44
|
// Utiliser les données du contexte si la base URL correspond.
|
|
45
45
|
// L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
|
|
46
46
|
// devoir casser le cache/provider de modèles déjà présent.
|
|
47
|
-
const useContextData =
|
|
47
|
+
const useContextData =
|
|
48
|
+
!options.baseUrl || options.baseUrl === context.baseUrl;
|
|
48
49
|
|
|
49
50
|
// Filtrer par catégorie si nécessaire
|
|
50
51
|
const filteredModels = useMemo(() => {
|
|
@@ -8,12 +8,25 @@ export interface ParsedError {
|
|
|
8
8
|
isUserFriendly: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
type NormalizedErrorLike = {
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function isNormalizedErrorLike(value: unknown): value is NormalizedErrorLike {
|
|
17
|
+
if (!value || typeof value !== "object") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const v = value as Record<string, unknown>;
|
|
21
|
+
return typeof v.code === "string" && typeof v.message === "string";
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
/**
|
|
12
25
|
* Parse et uniformise la gestion des erreurs des composants AI
|
|
13
26
|
*/
|
|
14
|
-
export function parseAIError(error:
|
|
27
|
+
export function parseAIError(error: unknown): ParsedError {
|
|
15
28
|
// Si l'erreur est déjà un objet normalisé
|
|
16
|
-
if (error
|
|
29
|
+
if (isNormalizedErrorLike(error)) {
|
|
17
30
|
return {
|
|
18
31
|
message: getUserFriendlyMessage(error.code, error.message),
|
|
19
32
|
code: error.code,
|
|
@@ -160,7 +173,7 @@ export interface ErrorToastCallback {
|
|
|
160
173
|
* @param showInternalToast Callback interne optionnelle pour afficher un toast dans le composant
|
|
161
174
|
*/
|
|
162
175
|
export function handleAIError(
|
|
163
|
-
error:
|
|
176
|
+
error: unknown,
|
|
164
177
|
onToast?: ErrorToastCallback,
|
|
165
178
|
showInternalToast?: (error: { message: string; code?: string }) => void
|
|
166
179
|
): void {
|
|
@@ -7,6 +7,51 @@ export interface ModelToggleOptions {
|
|
|
7
7
|
baseUrl?: string; // URL de base de l'API
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
interface Provider {
|
|
11
|
+
models?: ModelInfo[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ProvidersResponse {
|
|
15
|
+
providers?: Provider[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ModelsResponse {
|
|
19
|
+
models?: ModelInfo[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type ModelCategory = "text" | "image" | "audio" | "video";
|
|
23
|
+
|
|
24
|
+
interface ModelInfo {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
provider: string;
|
|
29
|
+
category: ModelCategory;
|
|
30
|
+
isActive?: boolean;
|
|
31
|
+
isPro?: boolean;
|
|
32
|
+
costPer1M?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isModelCategory(value: unknown): value is ModelCategory {
|
|
36
|
+
return (
|
|
37
|
+
value === "text" ||
|
|
38
|
+
value === "image" ||
|
|
39
|
+
value === "audio" ||
|
|
40
|
+
value === "video"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isModelInfo(value: unknown): value is ModelInfo {
|
|
45
|
+
if (!value || typeof value !== "object") return false;
|
|
46
|
+
const v = value as Record<string, unknown>;
|
|
47
|
+
return (
|
|
48
|
+
typeof v.id === "string" &&
|
|
49
|
+
typeof v.name === "string" &&
|
|
50
|
+
typeof v.provider === "string" &&
|
|
51
|
+
isModelCategory(v.category)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
10
55
|
function isAuthTokenCandidate(value?: string): boolean {
|
|
11
56
|
if (!value) return false;
|
|
12
57
|
const token = value.trim();
|
|
@@ -66,18 +111,7 @@ export async function toggleUserModel(
|
|
|
66
111
|
*/
|
|
67
112
|
export async function getAvailableModels(
|
|
68
113
|
options: ModelToggleOptions = {}
|
|
69
|
-
): Promise<
|
|
70
|
-
Array<{
|
|
71
|
-
id: string;
|
|
72
|
-
name: string;
|
|
73
|
-
description?: string;
|
|
74
|
-
provider: string;
|
|
75
|
-
category: "text" | "image" | "audio" | "video";
|
|
76
|
-
isActive?: boolean;
|
|
77
|
-
isPro?: boolean;
|
|
78
|
-
costPer1M?: number;
|
|
79
|
-
}>
|
|
80
|
-
> {
|
|
114
|
+
): Promise<ModelInfo[]> {
|
|
81
115
|
const { apiKey, baseUrl = "" } = options;
|
|
82
116
|
|
|
83
117
|
// Déterminer le contexte
|
|
@@ -123,11 +157,15 @@ export async function getAvailableModels(
|
|
|
123
157
|
const data = await response.json();
|
|
124
158
|
|
|
125
159
|
if (Array.isArray(data?.models)) {
|
|
126
|
-
return data.models;
|
|
160
|
+
return data.models.filter(isModelInfo);
|
|
127
161
|
}
|
|
128
162
|
if (Array.isArray(data?.providers)) {
|
|
129
|
-
return
|
|
130
|
-
|
|
163
|
+
return (
|
|
164
|
+
(data as ProvidersResponse).providers?.flatMap((provider: Provider) =>
|
|
165
|
+
Array.isArray(provider.models)
|
|
166
|
+
? provider.models.filter(isModelInfo)
|
|
167
|
+
: []
|
|
168
|
+
) || []
|
|
131
169
|
);
|
|
132
170
|
}
|
|
133
171
|
return [];
|