pierreguillou commited on
Commit
d8df87a
·
verified ·
1 Parent(s): 151a04c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +836 -0
app.py ADDED
@@ -0,0 +1,836 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import requests
4
+ import tempfile
5
+ from datetime import datetime, time
6
+ import os
7
+ import google.generativeai as genai
8
+ from typing import Optional, Tuple
9
+
10
+ # Configuration mise à jour avec les derniers modèles (juin 2025)
11
+ UPDATED_MODELS = {
12
+ "OpenAI": [
13
+ # Série GPT-4.1 (2025)
14
+ "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano",
15
+ # Série o (reasoning models)
16
+ "o3-pro", "o3", "o3-mini", "o4-mini", "o1", "o1-preview", "o1-pro", "o1-mini",
17
+ # GPT-4.5 et autres
18
+ "gpt-4.5-preview", "gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo",
19
+ # Anciens modèles encore disponibles
20
+ "gpt-4", "gpt-4-vision-preview", "gpt-3.5-turbo-16k"
21
+ ],
22
+ "Anthropic": [
23
+ # Claude 4 (mai 2025)
24
+ "claude-opus-4-20250514", "claude-sonnet-4-20250514",
25
+ # Claude 3.7 (février 2025)
26
+ "claude-3-7-sonnet-20250219", "claude-3-7-sonnet-latest",
27
+ # Claude 3.5
28
+ "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-20240620", "claude-3-5-haiku-20241022",
29
+ # Claude 3
30
+ "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"
31
+ ],
32
+ "Google AI": [
33
+ # Gemini 2.5 (2025) - GRATUIT via Google AI Studio
34
+ "gemini-2.5-pro 🆓", "gemini-2.5-flash 🆓", "gemini-2.5-flash-lite 🆓",
35
+ "gemini-2.5-flash-preview-native-audio-dialog", "gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts",
36
+ # Gemini 2.0 - GRATUIT via Google AI Studio
37
+ "gemini-2.0-flash 🆓", "gemini-2.0-flash-preview-image-generation", "gemini-2.0-flash-lite 🆓", "gemini-2.0-pro-experimental",
38
+ # Gemini 1.5
39
+ "gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.5-flash-8b",
40
+ # Anciens modèles
41
+ "gemini-pro", "gemini-pro-vision"
42
+ ],
43
+ "Cohere": [
44
+ "command-r", "command-r-plus", "command-r-08-2024", "command-r-plus-08-2024",
45
+ "command", "command-light", "command-nightly", "aya-23-35b", "aya-23-8b"
46
+ ],
47
+ "Mistral AI": [
48
+ "mistral-medium-3", "mistral-small-3", "mistral-tiny-3", "magistral-medium", "magistral-small",
49
+ "ministral-3b 🆓", "ministral-8b 🆓", "mistral-large", "mistral-medium", "mistral-small", "mistral-tiny",
50
+ "mixtral-8x7b-instruct", "mixtral-8x22b-instruct"
51
+ ],
52
+ "Together AI": [
53
+ "meta-llama/Llama-4-Scout 🆓", "meta-llama/Llama-4-Maverick 🆓",
54
+ "meta-llama/Llama-3.3-70B-Instruct 🆓", "meta-llama/Llama-3.3-Nemotron-Super-49B",
55
+ "meta-llama/Llama-3.1-405B-Instruct", "meta-llama/Llama-3.1-70B-Instruct", "meta-llama/Llama-3.1-8B-Instruct 🆓",
56
+ "Qwen/Qwen3-235B", "Qwen/Qwen3-32B 🆓", "Qwen/Qwen3-14B 🆓", "Qwen/Qwen3-8B 🆓", "Qwen/Qwen3-4B",
57
+ "Qwen/QwQ-32B-Preview 🆓", "Qwen/Qwen2.5-Max",
58
+ "deepseek-ai/DeepSeek-V3 🆓", "deepseek-ai/DeepSeek-R1 🆓", "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B 🆓"
59
+ ],
60
+ "Groq": [
61
+ "llama-3.3-70b-versatile 🆓", "llama-3.1-405b-reasoning", "llama-3.1-70b-versatile 🆓", "llama-3.1-8b-instant 🆓",
62
+ "mixtral-8x7b-32768 🆓", "mixtral-8x22b-instruct", "gemma2-9b-it 🆓", "gemma-7b-it 🆓"
63
+ ],
64
+ "xAI": [
65
+ "grok-3", "grok-3-mini", "grok-3-reasoning-beta", "grok-2", "grok-2-mini", "grok-2-vision"
66
+ ],
67
+ "DeepSeek": [
68
+ "deepseek-r1 🆓", "deepseek-r1-distill-qwen-32b 🆓", "deepseek-r1-distill-qwen-14b 🆓", "deepseek-r1-distill-llama-70b 🆓",
69
+ "deepseek-v3 🆓", "deepseek-v2.5", "deepseek-v2", "deepseek-coder-v2", "deepseek-coder-33b"
70
+ ],
71
+ "Perplexity AI": [
72
+ "sonar-reasoning", "sonar", "sonar-pro", "pplx-7b-online", "pplx-70b-online", "pplx-7b-chat", "pplx-70b-chat"
73
+ ],
74
+ "Amazon": ["nova-premier", "nova-pro", "nova-lite", "nova-micro"],
75
+ "Reka AI": ["reka-flash-3", "reka-core", "reka-edge"]
76
+ }
77
+
78
+ # Informations détaillées sur les modèles gratuits
79
+ FREE_MODELS_INFO = {
80
+ "gemini-2.5-flash 🆓": {
81
+ "provider": "Google AI Studio",
82
+ "performance": "⭐⭐⭐⭐⭐ (Excellent, multimodal, très rapide)",
83
+ "specialties": "Texte + images, vitesse, conversations longues",
84
+ "limits": "1M tokens/min, 1500 req/jour",
85
+ "conditions": "Compte Google gratuit. Données utilisées pour entraînement (hors UE/UK)",
86
+ "signup": "https://ai.google.dev",
87
+ "recommended": True,
88
+ "default_available": True
89
+ },
90
+ "deepseek-r1 🆓": {
91
+ "provider": "DeepSeek + OpenRouter",
92
+ "performance": "⭐⭐⭐⭐⭐ (Rivalise avec GPT-4o)",
93
+ "specialties": "Raisonnement, mathématiques, programmation",
94
+ "limits": "OpenRouter: 50 req/jour (1000 avec $10 crédit) | DeepSeek direct: Limites généreuses",
95
+ "conditions": "Compte gratuit requis. Sur OpenRouter: données utilisées pour entraînement",
96
+ "signup": "DeepSeek: https://platform.deepseek.com | OpenRouter: https://openrouter.ai",
97
+ "recommended": True
98
+ }
99
+ }
100
+
101
+ # Variables globales pour le quota
102
+ DEFAULT_API_QUOTA = 1500
103
+ current_quota = DEFAULT_API_QUOTA
104
+ quota_reset_time = None
105
+ default_api_available = True
106
+
107
+ def load_updated_models():
108
+ """Charge les modèles depuis le fichier JSON mis à jour ou utilise la configuration actualisée"""
109
+ try:
110
+ if os.path.exists('models_config.json'):
111
+ with open('models_config.json', 'r', encoding='utf-8') as f:
112
+ data = json.load(f)
113
+ return data.get('models', UPDATED_MODELS), data.get('last_updated', 'Inconnu')
114
+
115
+ config_url = "https://raw.githubusercontent.com/votre-username/votre-repo/main/models_config.json"
116
+ response = requests.get(config_url, timeout=5)
117
+ if response.status_code == 200:
118
+ data = response.json()
119
+ return data.get('models', UPDATED_MODELS), data.get('last_updated', 'Inconnu')
120
+
121
+ except Exception as e:
122
+ print(f"Erreur lors du chargement des modèles mis à jour: {e}")
123
+
124
+ return UPDATED_MODELS, "Configuration mise à jour - Juin 2025"
125
+
126
+ def get_default_api_key() -> Optional[str]:
127
+ """Récupère la clé API par défaut depuis les secrets Hugging Face"""
128
+ return os.getenv("GOOGLE_API_KEY")
129
+
130
+ def check_quota_status() -> Tuple[bool, int, str]:
131
+ """Vérifie le statut du quota de l'API par défaut"""
132
+ global current_quota, quota_reset_time, default_api_available
133
+
134
+ now = datetime.now()
135
+
136
+ if quota_reset_time is None or now.date() > quota_reset_time.date():
137
+ current_quota = DEFAULT_API_QUOTA
138
+ quota_reset_time = datetime.combine(now.date().replace(day=now.day + 1), time.min)
139
+ default_api_available = True
140
+
141
+ if current_quota <= 0:
142
+ default_api_available = False
143
+ hours_until_reset = (quota_reset_time - now).total_seconds() / 3600
144
+ status_msg = f"🚫 API par défaut épuisée. Réinitialisation dans {hours_until_reset:.1f}h"
145
+ else:
146
+ default_api_available = True
147
+ status_msg = f"✅ API par défaut disponible"
148
+
149
+ return default_api_available, current_quota, status_msg
150
+
151
+ def update_models(company):
152
+ """Met à jour la liste des modèles selon l'entreprise sélectionnée"""
153
+ global default_api_available
154
+
155
+ if company in CURRENT_MODELS:
156
+ models = CURRENT_MODELS[company]
157
+ default_value = None
158
+ if default_api_available and company == "Google AI":
159
+ default_value = "gemini-2.5-flash 🆓"
160
+ elif models:
161
+ default_value = models[0] if not default_api_available else None
162
+
163
+ return gr.Dropdown(
164
+ choices=models,
165
+ value=default_value,
166
+ label="🧠 Modèle LLM",
167
+ interactive=True
168
+ )
169
+ return gr.Dropdown(choices=[], label="🧠 Modèle LLM", interactive=True)
170
+
171
+ def update_model_info(model):
172
+ """Met à jour les informations du modèle sélectionné"""
173
+ if not model:
174
+ return "Sélectionnez un modèle pour voir ses informations."
175
+
176
+ if model in FREE_MODELS_INFO:
177
+ info = FREE_MODELS_INFO[model]
178
+ default_api_info = ""
179
+
180
+ if model == "gemini-2.5-flash 🆓" and default_api_available:
181
+ default_api_info = f"""
182
+ ### 🎁 **API PAR DÉFAUT DISPONIBLE**
183
+ Cette app fournit une clé API Google gratuite pour ce modèle !
184
+ - **Quota partagé:** {current_quota}/{DEFAULT_API_QUOTA} requêtes restantes aujourd'hui
185
+ - **Réinitialisation:** Chaque jour à minuit
186
+ - **Alternative:** Vous pouvez utiliser votre propre clé API Google
187
+
188
+ """
189
+
190
+ return f"""
191
+ {default_api_info}
192
+ ### 🆓 **{model}** - MODÈLE GRATUIT
193
+
194
+ **🏢 Provider:** {info['provider']}
195
+ **📊 Performance:** {info['performance']}
196
+ **🎯 Spécialités:** {info['specialties']}
197
+ **⏱️ Limites d'usage:** {info['limits']}
198
+ **📋 Conditions:** {info['conditions']}
199
+ **🔗 Inscription:** {info['signup']}
200
+
201
+ {'🌟 **RECOMMANDÉ** - Excellent rapport qualité/gratuité' if info.get('recommended') else ''}
202
+ """
203
+ elif "🆓" in model:
204
+ return f"""
205
+ ### 🆓 **{model}** - MODÈLE GRATUIT
206
+
207
+ Ce modèle est disponible gratuitement sous certaines conditions.
208
+ Consultez la documentation du provider pour les détails spécifiques.
209
+
210
+ **⚠️ Important:** Les modèles gratuits nécessitent généralement :
211
+ - Un compte gratuit chez le provider
212
+ - Acceptation que vos données puissent être utilisées pour l'entraînement
213
+ - Respect des limites d'usage quotidiennes/mensuelles
214
+ """
215
+ else:
216
+ return f"""
217
+ ### 💰 **{model}** - MODÈLE PAYANT
218
+
219
+ Ce modèle nécessite un abonnement ou un paiement à l'usage.
220
+ Consultez la tarification du provider pour les coûts exacts.
221
+
222
+ **💡 Conseil:** Vérifiez si le provider offre des crédits gratuits pour tester le modèle.
223
+ """
224
+
225
+ def generate_selection_file(company, model, user_api_key, use_default_api):
226
+ """Génère un fichier avec les choix effectués - SÉCURISÉ"""
227
+ if not company or not model:
228
+ return "Veuillez sélectionner une entreprise et un modèle", None, get_quota_display()
229
+
230
+ try:
231
+ is_free = "🆓" in model
232
+ model_clean = model.replace(" 🆓", "")
233
+
234
+ # SÉCURITÉ : Gestion des clés API
235
+ api_config_section = ""
236
+ api_status = ""
237
+
238
+ if model == "gemini-2.5-flash 🆓" and use_default_api and default_api_available:
239
+ # Utilisation de l'API par défaut - PAS DE CLÉ DANS LE FICHIER
240
+ api_status = "🎁 API par défaut utilisée"
241
+ api_config_section = """
242
+ 🔐 CONFIGURATION API - SÉCURISÉE
243
+ ===============================
244
+ Type d'API utilisée: API par défaut de l'application
245
+ Clé API: [FOURNIE PAR L'APPLICATION - NON AFFICHÉE POUR SÉCURITÉ]
246
+
247
+ ⚠️ POURQUOI LA CLÉ N'EST PAS AFFICHÉE ?
248
+ - La clé API par défaut est stockée de manière sécurisée
249
+ - Elle ne doit jamais être exposée aux utilisateurs
250
+ - Cela protège le quota partagé contre les abus
251
+
252
+ 🔑 POUR VOIR UNE CLÉ API DANS CETTE CONFIGURATION :
253
+ 1. Créez votre compte gratuit sur https://ai.google.dev
254
+ 2. Générez votre clé API personnelle
255
+ 3. Saisissez-la dans le champ "Votre clé API" de l'interface
256
+ 4. Régénérez cette configuration
257
+
258
+ AVANTAGES DE VOTRE PROPRE CLÉ API :
259
+ ✅ Quota personnel complet (1500 req/jour)
260
+ ✅ Clé visible dans la configuration générée
261
+ ✅ Contrôle total sur vos données
262
+ ✅ Pas de partage avec d'autres utilisateurs
263
+
264
+ """
265
+ elif user_api_key:
266
+ # Utilisateur a fourni sa propre clé
267
+ api_status = "🔑 Votre clé API sera utilisée"
268
+ # Masquer partiellement la clé pour sécurité
269
+ masked_key = user_api_key[:8] + "..." + user_api_key[-4:] if len(user_api_key) > 12 else "***"
270
+ api_config_section = f"""
271
+ 🔐 CONFIGURATION API - VOTRE CLÉ PERSONNELLE
272
+ ==========================================
273
+ Type d'API: Clé personnelle fournie
274
+ Clé API: {user_api_key}
275
+ Clé masquée: {masked_key}
276
+
277
+ ✅ AVANTAGES DE VOTRE CLÉ PERSONNELLE :
278
+ - Quota personnel complet
279
+ - Contrôle total sur vos données
280
+ - Pas de partage avec d'autres utilisateurs
281
+ - Clé visible dans cette configuration
282
+
283
+ """
284
+ else:
285
+ api_status = "⚠️ Aucune clé API configurée"
286
+ api_config_section = """
287
+ ⚠️ AUCUNE CLÉ API CONFIGURÉE
288
+ ============================
289
+ Vous devez configurer une clé API pour utiliser ce modèle.
290
+
291
+ OPTIONS DISPONIBLES :
292
+ 1. Utiliser l'API par défaut (Gemini 2.5 Flash uniquement)
293
+ 2. Créer votre compte et utiliser votre propre clé API
294
+
295
+ POUR OBTENIR VOTRE CLÉ API :
296
+ - Google AI: https://ai.google.dev
297
+ - OpenAI: https://platform.openai.com
298
+ - Anthropic: https://console.anthropic.com
299
+ - DeepSeek: https://platform.deepseek.com
300
+
301
+ """
302
+
303
+ # Création du contenu du fichier
304
+ content = f"""Configuration LLM sélectionnée
305
+ =====================================
306
+
307
+ Entreprise: {company}
308
+ Modèle: {model_clean}
309
+ Type: {'GRATUIT 🆓' if is_free else 'PAYANT 💰'}
310
+ Statut API: {api_status}
311
+ Date de génération: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
312
+ Dernière mise à jour des modèles: {LAST_UPDATE}
313
+
314
+ {api_config_section}
315
+ """
316
+
317
+ # Ajout d'informations sur l'API par défaut si applicable
318
+ if model == "gemini-2.5-flash 🆓":
319
+ content += f"""
320
+ 🎁 INFORMATION API PAR DÉFAUT
321
+ ============================
322
+ Cette application fournit une clé API Google gratuite pour Gemini 2.5 Flash.
323
+ - Quota quotidien: {DEFAULT_API_QUOTA} requêtes (partagé)
324
+ - Quota restant: {current_quota} requêtes
325
+ - Réinitialisation: Chaque jour à minuit
326
+
327
+ ⚠️ IMPORTANT - CONDITIONS D'USAGE :
328
+ - API partagée entre tous les utilisateurs de l'app
329
+ - Vos conversations peuvent être utilisées pour l'entraînement Google
330
+ - Pour un usage privé, utilisez votre propre clé API Google
331
+ - Quota limité - peut être épuisé rapidement
332
+
333
+ 🔄 PASSER À VOTRE PROPRE API :
334
+ 1. Aller sur https://ai.google.dev
335
+ 2. Créer un compte Google gratuit
336
+ 3. Générer votre clé API personnelle
337
+ 4. Utiliser votre clé dans l'interface de l'app
338
+ 5. Profiter de votre quota personnel de 1500 req/jour
339
+
340
+ """
341
+
342
+ # Ajout d'exemples de code sécurisés
343
+ if company == "Google AI":
344
+ if use_default_api and not user_api_key:
345
+ content += f"""
346
+ Exemple d'utilisation (avec votre propre clé API):
347
+ ------------------------------------------------
348
+ import google.generativeai as genai
349
+
350
+ # ⚠️ REMPLACEZ PAR VOTRE VRAIE CLÉ API
351
+ genai.configure(api_key="VOTRE_CLE_API_GOOGLE_ICI")
352
+ model = genai.GenerativeModel('{model_clean}')
353
+
354
+ response = model.generate_content("Votre prompt ici")
355
+ print(response.text)
356
+
357
+ # Avec LangChain
358
+ from langchain_google_genai import ChatGoogleGenerativeAI
359
+ llm = ChatGoogleGenerativeAI(
360
+ model="{model_clean}",
361
+ google_api_key="VOTRE_CLE_API_GOOGLE_ICI"
362
+ )
363
+
364
+ 🔑 POUR OBTENIR VOTRE CLÉ API GOOGLE :
365
+ 1. Aller sur https://ai.google.dev
366
+ 2. Se connecter avec un compte Google
367
+ 3. Cliquer sur "Get API Key"
368
+ 4. Créer un nouveau projet si nécessaire
369
+ 5. Copier la clé générée
370
+ """
371
+ else:
372
+ content += f"""
373
+ Exemple d'utilisation avec votre clé API:
374
+ ---------------------------------------
375
+ import google.generativeai as genai
376
+
377
+ # Votre clé API configurée
378
+ genai.configure(api_key="{user_api_key if user_api_key else 'VOTRE_CLE_API_GOOGLE'}")
379
+ model = genai.GenerativeModel('{model_clean}')
380
+
381
+ response = model.generate_content("Votre prompt ici")
382
+ print(response.text)
383
+ """
384
+
385
+ # Création du fichier temporaire
386
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as f:
387
+ f.write(content)
388
+ temp_file_path = f.name
389
+
390
+ return content, temp_file_path, get_quota_display()
391
+
392
+ except Exception as e:
393
+ error_msg = f"Erreur lors de la génération du fichier: {str(e)}"
394
+ return error_msg, None, get_quota_display()
395
+
396
+ def get_quota_display():
397
+ """Retourne l'affichage du quota actuel"""
398
+ available, quota, status = check_quota_status()
399
+ return f"📊 {status} | Quota: {quota}/{DEFAULT_API_QUOTA}"
400
+
401
+ def refresh_models():
402
+ """Rafraîchit la liste des modèles"""
403
+ global CURRENT_MODELS, LAST_UPDATE
404
+ CURRENT_MODELS, LAST_UPDATE = load_updated_models()
405
+ return f"Modèles rafraîchis - Dernière mise à jour: {LAST_UPDATE}"
406
+
407
+ # Chargement des modèles au démarrage
408
+ CURRENT_MODELS, LAST_UPDATE = load_updated_models()
409
+
410
+ # CSS personnalisé pour un design moderne et classe
411
+ custom_css = """
412
+ /* Design moderne et élégant */
413
+ .gradio-container {
414
+ max-width: 1200px !important;
415
+ margin: 0 auto !important;
416
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
417
+ }
418
+
419
+ /* Titre centré avec style moderne */
420
+ .main-title {
421
+ text-align: center !important;
422
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
423
+ -webkit-background-clip: text !important;
424
+ -webkit-text-fill-color: transparent !important;
425
+ background-clip: text !important;
426
+ font-size: 2.5rem !important;
427
+ font-weight: 700 !important;
428
+ margin: 1rem 0 2rem 0 !important;
429
+ letter-spacing: -0.02em !important;
430
+ }
431
+
432
+ /* Onglets modernes */
433
+ .tab-nav {
434
+ border-bottom: 2px solid #e5e7eb !important;
435
+ margin-bottom: 1.5rem !important;
436
+ }
437
+
438
+ .tab-nav button {
439
+ background: none !important;
440
+ border: none !important;
441
+ padding: 0.75rem 1.5rem !important;
442
+ font-weight: 600 !important;
443
+ color: #6b7280 !important;
444
+ border-bottom: 3px solid transparent !important;
445
+ transition: all 0.3s ease !important;
446
+ }
447
+
448
+ .tab-nav button.selected {
449
+ color: #4f46e5 !important;
450
+ border-bottom-color: #4f46e5 !important;
451
+ }
452
+
453
+ .tab-nav button:hover {
454
+ color: #4f46e5 !important;
455
+ background: rgba(79, 70, 229, 0.05) !important;
456
+ }
457
+
458
+ /* Contenu des onglets */
459
+ .tab-content {
460
+ width: 100% !important;
461
+ padding: 1.5rem !important;
462
+ background: #ffffff !important;
463
+ border-radius: 12px !important;
464
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
465
+ border: 1px solid #e5e7eb !important;
466
+ }
467
+
468
+ /* Cartes d'information */
469
+ .info-card {
470
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;
471
+ border: 1px solid #e2e8f0 !important;
472
+ border-radius: 12px !important;
473
+ padding: 1.5rem !important;
474
+ margin: 1rem 0 !important;
475
+ }
476
+
477
+ /* Statut du quota */
478
+ .quota-status {
479
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
480
+ color: white !important;
481
+ padding: 0.75rem 1.5rem !important;
482
+ border-radius: 8px !important;
483
+ text-align: center !important;
484
+ font-weight: 600 !important;
485
+ margin-bottom: 1rem !important;
486
+ }
487
+
488
+ .quota-status.exhausted {
489
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
490
+ }
491
+
492
+ /* Boutons modernes */
493
+ .modern-button {
494
+ background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%) !important;
495
+ border: none !important;
496
+ color: white !important;
497
+ padding: 0.75rem 2rem !important;
498
+ border-radius: 8px !important;
499
+ font-weight: 600 !important;
500
+ transition: all 0.3s ease !important;
501
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
502
+ }
503
+
504
+ .modern-button:hover {
505
+ transform: translateY(-2px) !important;
506
+ box-shadow: 0 8px 15px -3px rgba(0, 0, 0, 0.2) !important;
507
+ }
508
+
509
+ /* Dropdowns et inputs */
510
+ .gradio-dropdown, .gradio-textbox {
511
+ border-radius: 8px !important;
512
+ border: 2px solid #e5e7eb !important;
513
+ transition: border-color 0.3s ease !important;
514
+ }
515
+
516
+ .gradio-dropdown:focus, .gradio-textbox:focus {
517
+ border-color: #4f46e5 !important;
518
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1) !important;
519
+ }
520
+
521
+ /* Badges gratuits */
522
+ .free-badge {
523
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
524
+ color: white !important;
525
+ padding: 0.25rem 0.5rem !important;
526
+ border-radius: 4px !important;
527
+ font-size: 0.75rem !important;
528
+ font-weight: 600 !important;
529
+ }
530
+
531
+ /* Accordéons */
532
+ .gradio-accordion {
533
+ border: 1px solid #e5e7eb !important;
534
+ border-radius: 12px !important;
535
+ overflow: hidden !important;
536
+ margin: 1rem 0 !important;
537
+ }
538
+
539
+ .gradio-accordion summary {
540
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;
541
+ padding: 1rem 1.5rem !important;
542
+ font-weight: 600 !important;
543
+ color: #374151 !important;
544
+ }
545
+
546
+ /* Responsive */
547
+ @media (max-width: 768px) {
548
+ .gradio-container {
549
+ max-width: 100% !important;
550
+ padding: 0 1rem !important;
551
+ }
552
+
553
+ .main-title {
554
+ font-size: 2rem !important;
555
+ }
556
+
557
+ .tab-content {
558
+ padding: 1rem !important;
559
+ }
560
+ }
561
+ """
562
+
563
+ # Interface Gradio avec design moderne
564
+ with gr.Blocks(
565
+ title="🤖 Sélecteur de LLM Premium - 2025",
566
+ theme=gr.themes.Soft(
567
+ primary_hue="indigo",
568
+ secondary_hue="blue",
569
+ neutral_hue="slate",
570
+ font=gr.themes.GoogleFont("Inter")
571
+ ),
572
+ css=custom_css
573
+ ) as app:
574
+
575
+ # Titre centré moderne
576
+ gr.HTML("""
577
+ <div class="main-title">
578
+ 🤖 Sélecteur de LLM Premium - 2025
579
+ </div>
580
+ """)
581
+
582
+ # Statut de l'API par défaut en haut
583
+ with gr.Row():
584
+ quota_display = gr.HTML(
585
+ value=f'<div class="quota-status">{get_quota_display()}</div>',
586
+ elem_id="quota-display"
587
+ )
588
+ refresh_quota_btn = gr.Button("🔄 Actualiser", size="sm", elem_classes=["modern-button"])
589
+
590
+ # Interface avec onglets modernes
591
+ with gr.Tabs() as tabs:
592
+
593
+ # ONGLET 1: Configuration du modèle
594
+ with gr.Tab("🎯 Configuration du modèle", elem_classes=["tab-content"]):
595
+ gr.Markdown("""
596
+ ### 🚀 Sélectionnez votre modèle LLM
597
+
598
+ Choisissez l'entreprise et le modèle qui correspondent à vos besoins.
599
+ **Gemini 2.5 Flash** est pré-sélectionné avec notre API gratuite par défaut.
600
+ """)
601
+
602
+ with gr.Row():
603
+ with gr.Column(scale=1):
604
+ # Déterminer les valeurs par défaut
605
+ default_company = "Google AI" if default_api_available else None
606
+ default_model = "gemini-2.5-flash 🆓" if default_api_available else None
607
+
608
+ company_dropdown = gr.Dropdown(
609
+ choices=list(CURRENT_MODELS.keys()),
610
+ label="🏢 Entreprise / Provider",
611
+ value=default_company,
612
+ info="Google AI sélectionné par défaut (API gratuite fournie)",
613
+ elem_classes=["gradio-dropdown"]
614
+ )
615
+
616
+ model_dropdown = gr.Dropdown(
617
+ choices=CURRENT_MODELS.get("Google AI", []) if default_api_available else [],
618
+ value=default_model,
619
+ label="🧠 Modèle LLM",
620
+ info="🆓 = Gratuit sous conditions | 💰 = Payant",
621
+ elem_classes=["gradio-dropdown"]
622
+ )
623
+
624
+ with gr.Column(scale=1):
625
+ # Informations sur le modèle sélectionné
626
+ model_info = gr.Markdown(
627
+ value=update_model_info(default_model) if default_model else "Sélectionnez un modèle pour voir ses informations.",
628
+ elem_classes=["info-card"]
629
+ )
630
+
631
+ # ONGLET 2: Configuration API
632
+ with gr.Tab("🔑 Configuration API", elem_classes=["tab-content"]):
633
+ gr.Markdown("""
634
+ ### 🔐 Configurez votre accès API
635
+
636
+ **Option 1** : Utilisez notre API gratuite par défaut (Gemini 2.5 Flash uniquement)
637
+ **Option 2** : Utilisez votre propre clé API pour plus de contrôle et de quota
638
+ """)
639
+
640
+ with gr.Column():
641
+ use_default_api = gr.Checkbox(
642
+ label="🎁 Utiliser l'API par défaut (Gemini 2.5 Flash uniquement)",
643
+ value=default_api_available and default_model == "gemini-2.5-flash 🆓",
644
+ interactive=default_api_available,
645
+ info="API Google gratuite fournie par l'app - Quota partagé"
646
+ )
647
+
648
+ user_api_key = gr.Textbox(
649
+ label="🔑 Votre clé API personnelle (optionnel si API par défaut utilisée)",
650
+ type="password",
651
+ placeholder="Collez votre clé API ici pour un quota personnel...",
652
+ info="Votre clé API personnelle pour ce provider",
653
+ elem_classes=["gradio-textbox"]
654
+ )
655
+
656
+ gr.Markdown("""
657
+ #### 🎯 Pourquoi utiliser votre propre clé API ?
658
+
659
+ - **🎯 Quota personnel complet** : 1500 req/jour pour Google (vs quota partagé)
660
+ - **🎯 Disponibilité garantie** : Pas de risque d'épuisement par d'autres utilisateurs
661
+ - **��� Accès à tous les modèles** : Pas limité à Gemini 2.5 Flash
662
+ - **🎯 Contrôle des données** : Vos conversations selon vos conditions
663
+ - **🎯 Évolutivité** : Possibilité d'upgrade vers des plans payants
664
+
665
+ #### 🔗 Liens pour obtenir vos clés API :
666
+ - **Google AI Studio** : [ai.google.dev](https://ai.google.dev) (gratuit)
667
+ - **OpenAI** : [platform.openai.com](https://platform.openai.com)
668
+ - **Anthropic** : [console.anthropic.com](https://console.anthropic.com)
669
+ - **DeepSeek** : [platform.deepseek.com](https://platform.deepseek.com) (gratuit)
670
+ """)
671
+
672
+ # ONGLET 3: Génération et résultats
673
+ with gr.Tab("📄 Génération de configuration", elem_classes=["tab-content"]):
674
+ gr.Markdown("""
675
+ ### 📋 Générez votre fichier de configuration
676
+
677
+ Cliquez sur le bouton pour générer un fichier de configuration complet avec :
678
+ - Informations sur le modèle sélectionné
679
+ - Configuration API sécurisée
680
+ - Exemples de code prêts à utiliser
681
+ - Instructions d'intégration
682
+ """)
683
+
684
+ generate_btn = gr.Button(
685
+ "📄 Générer la configuration complète",
686
+ variant="primary",
687
+ size="lg",
688
+ elem_classes=["modern-button"]
689
+ )
690
+
691
+ with gr.Row():
692
+ with gr.Column():
693
+ output_text = gr.Textbox(
694
+ label="📋 Configuration générée",
695
+ lines=25,
696
+ interactive=False,
697
+ show_copy_button=True,
698
+ elem_classes=["gradio-textbox"]
699
+ )
700
+
701
+ download_file = gr.File(
702
+ label="📥 Fichier de configuration téléchargeable (.txt)",
703
+ interactive=False
704
+ )
705
+
706
+ # ONGLET 4: Guide d'utilisation
707
+ with gr.Tab("📖 Guide d'utilisation", elem_classes=["tab-content"]):
708
+ gr.Markdown(f"""
709
+ ## 🎁 **API PAR DÉFAUT GRATUITE !**
710
+
711
+ Cette application fournit **gratuitement** une clé API Google pour utiliser **Gemini 2.5 Flash** !
712
+
713
+ ### ✨ Avantages de l'API par défaut :
714
+ - **🆓 Totalement gratuit** : Aucune inscription requise de votre part
715
+ - **📊 Quota généreux** : {DEFAULT_API_QUOTA} requêtes par jour
716
+ - **🔄 Réinitialisation quotidienne** : Chaque jour à minuit
717
+ - **⚡ Très rapide** : Gemini 2.5 Flash optimisé pour la vitesse
718
+ - **🖼️ Multimodal** : Traite texte et images
719
+
720
+ ### 🚀 Comment utiliser l'app :
721
+
722
+ 1. **Vérifiez le quota** en haut de la page
723
+ 2. **Onglet "Configuration du modèle"** : Sélectionnez provider et modèle
724
+ 3. **Onglet "Configuration API"** : Choisissez API par défaut ou votre clé
725
+ 4. **Onglet "Génération"** : Créez et téléchargez votre configuration
726
+
727
+ ### ⚠️ **Quand l'API par défaut est épuisée** :
728
+ - L'app vous informe que le quota est atteint
729
+ - L'API par défaut n'est plus proposée jusqu'à minuit
730
+ - **Solution simple** : Utilisez votre propre clé API Google
731
+ - Ou choisissez un autre provider avec votre clé API
732
+
733
+ ### 🔐 **Sécurité et confidentialité** :
734
+ - **API par défaut** : Clé stockée de manière sécurisée (jamais exposée)
735
+ - **Vos données** : Peuvent être utilisées par Google pour l'entraînement
736
+ - **Conseil** : Ne jamais envoyer de données sensibles ou confidentielles
737
+ - **Usage professionnel** : Utilisez votre propre clé API
738
+ """)
739
+
740
+ # ONGLET 5: Modèles disponibles
741
+ with gr.Tab("📊 Modèles disponibles", elem_classes=["tab-content"]):
742
+ gr.Markdown(f"""
743
+ ## 📈 **Statistiques des modèles**
744
+
745
+ - **Total des providers** : {len(CURRENT_MODELS)}
746
+ - **Total des modèles** : {sum(len(models) for models in CURRENT_MODELS.values())}
747
+ - **Modèles gratuits identifiés** : {sum(1 for models in CURRENT_MODELS.values() for model in models if '🆓' in model)}
748
+ - **Dernière mise à jour** : {LAST_UPDATE}
749
+
750
+ ## 🆕 **Nouveautés 2025**
751
+
752
+ ### 🔥 **Modèles gratuits recommandés** :
753
+ - **Gemini 2.5 Flash** 🆓 : Multimodal, très rapide (API par défaut)
754
+ - **DeepSeek R1** 🆓 : Excellent en raisonnement et programmation
755
+ - **Llama 4 Scout/Maverick** 🆓 : Contexte ultra-long (10M tokens)
756
+ - **QwQ-32B** 🆓 : Spécialisé en mathématiques et logique
757
+
758
+ ### 🚀 **Nouveaux modèles 2025** :
759
+ - **OpenAI** : Série GPT-4.1, modèles o3/o4 (reasoning)
760
+ - **Anthropic** : Claude 4 Opus/Sonnet, Claude 3.7
761
+ - **Google** : Gemini 2.5 Pro/Flash avec thinking
762
+ - **xAI** : Grok 3 avec reasoning
763
+ - **DeepSeek** : R1 series (gratuits et performants)
764
+
765
+ ## 🏢 **Providers supportés**
766
+
767
+ {chr(10).join([f"### **{provider}**" + chr(10) + chr(10).join([f"- {model}" for model in models[:5]]) + (f"- ... et {len(models)-5} autres" if len(models) > 5 else "") for provider, models in CURRENT_MODELS.items()])}
768
+
769
+ ## 🔧 **Sources et références**
770
+
771
+ Cette application se base sur :
772
+ - **GitHub** : [free-llm-api-resources](https://github.com/cheahjs/free-llm-api-resources)
773
+ - **Hugging Face** : [Guide LLM gratuits 2025](https://huggingface.co/blog/lynn-mikami/llm-free)
774
+ - **Documentation officielle** des providers
775
+
776
+ Les informations sont mises à jour régulièrement pour refléter les dernières offres.
777
+ """)
778
+
779
+ refresh_btn = gr.Button("🔄 Rafraîchir la liste des modèles", elem_classes=["modern-button"])
780
+ refresh_status = gr.Textbox(label="Statut de la mise à jour", visible=False)
781
+
782
+ # Événements
783
+ company_dropdown.change(
784
+ fn=update_models,
785
+ inputs=[company_dropdown],
786
+ outputs=[model_dropdown]
787
+ )
788
+
789
+ model_dropdown.change(
790
+ fn=update_model_info,
791
+ inputs=[model_dropdown],
792
+ outputs=[model_info]
793
+ )
794
+
795
+ # Mise à jour de la checkbox selon le modèle sélectionné
796
+ def update_default_api_checkbox(model):
797
+ if model == "gemini-2.5-flash 🆓" and default_api_available:
798
+ return gr.Checkbox(value=True, interactive=True)
799
+ else:
800
+ return gr.Checkbox(value=False, interactive=False)
801
+
802
+ model_dropdown.change(
803
+ fn=update_default_api_checkbox,
804
+ inputs=[model_dropdown],
805
+ outputs=[use_default_api]
806
+ )
807
+
808
+ generate_btn.click(
809
+ fn=generate_selection_file,
810
+ inputs=[company_dropdown, model_dropdown, user_api_key, use_default_api],
811
+ outputs=[output_text, download_file, quota_display]
812
+ )
813
+
814
+ refresh_btn.click(
815
+ fn=refresh_models,
816
+ outputs=[refresh_status]
817
+ ).then(
818
+ fn=lambda: gr.Dropdown(choices=list(CURRENT_MODELS.keys())),
819
+ outputs=[company_dropdown]
820
+ )
821
+
822
+ refresh_quota_btn.click(
823
+ fn=get_quota_display,
824
+ outputs=[quota_display]
825
+ )
826
+
827
+ # Lancement de l'app
828
+ if __name__ == "__main__":
829
+ app.launch(share=True)
830
+
831
+ print("🎉 App Gradio Premium avec design moderne créée avec succès!")
832
+ print(f"📊 Modèles chargés: {sum(len(models) for models in CURRENT_MODELS.values())} modèles de {len(CURRENT_MODELS)} providers")
833
+ print(f"🎁 API par défaut: Gemini 2.5 Flash (quota: {current_quota}/{DEFAULT_API_QUOTA})")
834
+ print(f"🆓 Modèles gratuits identifiés: {sum(1 for models in CURRENT_MODELS.values() for model in models if '🆓' in model)}")
835
+ print("🎨 Design moderne avec onglets et CSS personnalisé appliqué")
836
+ print("🔐 Sécurité renforcée - Clé API par défaut jamais exposée")