fix(ui): dropdown reflete blown SUR LA CLE COURANTE (per-key, pas per-pool)
Browse filesL'utilisateur clarifie : il veut que le dropdown montre l'etat de la
cle ACTUELLEMENT utilisee, pas une vue agregee du pool. Quand un
modele autre que 3.1 hit PerDay sur la cle courante, il devient
'epuise sur cette cle' immediatement (et la prochaine invocation
prendra une autre cle, mais pas pour 3.5/2.5 sans switch).
Avant : is_model_fully_blown(model) = True ssi toutes les cles du
pool blown pour ce modele → trop conservateur, dropdown ne changeait
jamais en pratique.
Maintenant :
- _CURRENT_GEMINI_KEY : variable module-level qui track la cle active.
- set_current_gemini_key(key) : appelee a chaque init et chaque switch.
Bumpe le registry version pour rafraichir le dropdown.
- is_model_blown_on_current_key(model) : True si le modele est blown
sur la cle courante (PerDay aujourd'hui OU cle invalide).
- build_model_choices() utilise ce check au lieu de is_model_fully_blown.
- Label : '❌ X — epuise sur cette cle' (au lieu de 'sur tout le pool').
Sites de set_current_gemini_key :
- chat_with_agent : init + apres switch.
- run_jarvis_flow : init + apres switch invalide + apres switch PerDay 3.1.
Comportement final :
- Sur 3.1 : un PerDay declenche switch auto → dropdown se rafraichit
immediatement avec les statuts connus de la NOUVELLE cle (qui peut
encore avoir 3.5/2.5 dispo, ou pas).
- Sur 3.5/2.5 : un PerDay marque blown → dropdown affiche immediatement
'❌ epuise sur cette cle' (puisque c'est la verite pour la cle qu'on
utilise actuellement). L'utilisateur sait qu'il doit basculer
manuellement de modele.
183 tests verts.
|
@@ -407,17 +407,35 @@ def mark_gemini_key_blown(key: str, model: str) -> None:
|
|
| 407 |
_bump_registry_version()
|
| 408 |
|
| 409 |
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
return False
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
for k in keys
|
| 420 |
-
)
|
| 421 |
|
| 422 |
|
| 423 |
def _refresh_dropdown_wrap(fn):
|
|
@@ -447,19 +465,22 @@ def _refresh_dropdown_wrap(fn):
|
|
| 447 |
|
| 448 |
def build_model_choices() -> list[tuple[str, str]]:
|
| 449 |
"""Construit la liste de (label, value) du dropdown des modèles,
|
| 450 |
-
avec marquage visuel :
|
| 451 |
-
- ✅ pour les modèles disponibles
|
| 452 |
-
- ❌ pour les Gemini natifs
|
| 453 |
-
|
| 454 |
-
par `— épuisé
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
"""
|
| 456 |
import re as _re
|
| 457 |
out: list[tuple[str, str]] = []
|
| 458 |
for key, label in ALL_MODELS.items():
|
| 459 |
-
if key in GEMINI_NATIVE_REQUIRED and
|
| 460 |
-
# Strip un suffixe parenthésé en fin de label (« (500 req/jour) »)
|
| 461 |
base = _re.sub(r"\s*\(.*?\)\s*$", "", label).strip()
|
| 462 |
-
decorated = f"❌ {base} — épuisé
|
| 463 |
else:
|
| 464 |
decorated = f"✅ {label}"
|
| 465 |
out.append((decorated, key))
|
|
@@ -802,6 +823,9 @@ def chat_with_agent(message: str, history: list[dict], api_key: str, model: str,
|
|
| 802 |
current_gemini_key: Optional[str] = (
|
| 803 |
pick_unblown_gemini_key(model) if model in GEMINI_NATIVE_REQUIRED else None
|
| 804 |
)
|
|
|
|
|
|
|
|
|
|
| 805 |
try:
|
| 806 |
llm = _build_llm(model, api_key, use_thinking=use_thinking,
|
| 807 |
gemini_key_override=current_gemini_key)
|
|
@@ -950,6 +974,7 @@ def chat_with_agent(message: str, history: list[dict], api_key: str, model: str,
|
|
| 950 |
if next_key:
|
| 951 |
pool_n = gemini_pool_size()
|
| 952 |
current_gemini_key = next_key
|
|
|
|
| 953 |
llm = _build_llm(
|
| 954 |
model, api_key,
|
| 955 |
use_thinking=use_thinking,
|
|
@@ -1018,6 +1043,7 @@ def chat_with_agent(message: str, history: list[dict], api_key: str, model: str,
|
|
| 1018 |
if next_key:
|
| 1019 |
pool_n = gemini_pool_size()
|
| 1020 |
current_gemini_key = next_key
|
|
|
|
| 1021 |
llm = _build_llm(
|
| 1022 |
model, api_key,
|
| 1023 |
use_thinking=use_thinking,
|
|
|
|
| 407 |
_bump_registry_version()
|
| 408 |
|
| 409 |
|
| 410 |
+
# Clé Gemini ACTIVE dans la session courante (= celle dans laquelle
|
| 411 |
+
# tournent les requêtes en ce moment). Mise à jour quand on pick une
|
| 412 |
+
# nouvelle clé (init) ou quand on bascule (PerDay/invalid sur 3.1).
|
| 413 |
+
# Le dropdown affiche le status « épuisé » selon ce qui est blown
|
| 414 |
+
# POUR CETTE CLÉ — pas pour tout le pool.
|
| 415 |
+
_CURRENT_GEMINI_KEY: Optional[str] = None
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
def set_current_gemini_key(key: Optional[str]) -> None:
|
| 419 |
+
"""Déclare la clé Gemini active. Bumpe le registry version pour
|
| 420 |
+
que le dropdown se rafraîchisse au prochain yield (le label
|
| 421 |
+
« ✅/❌ épuisé sur cette clé » dépend de la clé courante)."""
|
| 422 |
+
global _CURRENT_GEMINI_KEY
|
| 423 |
+
if _CURRENT_GEMINI_KEY != key:
|
| 424 |
+
_CURRENT_GEMINI_KEY = key
|
| 425 |
+
_bump_registry_version()
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
def is_model_blown_on_current_key(model: str) -> bool:
|
| 429 |
+
"""True si le modèle est épuisé SUR LA CLÉ COURANTE (blown today
|
| 430 |
+
ou invalide). C'est ça qui détermine si le dropdown affiche
|
| 431 |
+
« ❌ épuisé ». Si on n'a pas encore de clé active, on retourne
|
| 432 |
+
False (état neutre)."""
|
| 433 |
+
key = _CURRENT_GEMINI_KEY
|
| 434 |
+
if not key:
|
| 435 |
return False
|
| 436 |
+
if key in _INVALID_KEYS:
|
| 437 |
+
return True
|
| 438 |
+
return _BLOWN_TODAY.get((key, model, _today_utc_str()), False)
|
|
|
|
|
|
|
| 439 |
|
| 440 |
|
| 441 |
def _refresh_dropdown_wrap(fn):
|
|
|
|
| 465 |
|
| 466 |
def build_model_choices() -> list[tuple[str, str]]:
|
| 467 |
"""Construit la liste de (label, value) du dropdown des modèles,
|
| 468 |
+
avec marquage visuel BASÉ SUR LA CLÉ COURANTE :
|
| 469 |
+
- ✅ pour les modèles disponibles sur la clé active
|
| 470 |
+
- ❌ pour les Gemini natifs blown sur la clé active (PerDay
|
| 471 |
+
sur cette clé OU clé invalide). On remplace le suffixe
|
| 472 |
+
`(X req/jour)` par `— épuisé sur cette clé`.
|
| 473 |
+
|
| 474 |
+
Quand on bascule de clé (3.1 hit PerDay → switch), set_current_
|
| 475 |
+
gemini_key est appelée et le dropdown se re-rendre avec les
|
| 476 |
+
statuts connus de la NOUVELLE clé (qui peuvent être différents).
|
| 477 |
"""
|
| 478 |
import re as _re
|
| 479 |
out: list[tuple[str, str]] = []
|
| 480 |
for key, label in ALL_MODELS.items():
|
| 481 |
+
if key in GEMINI_NATIVE_REQUIRED and is_model_blown_on_current_key(key):
|
|
|
|
| 482 |
base = _re.sub(r"\s*\(.*?\)\s*$", "", label).strip()
|
| 483 |
+
decorated = f"❌ {base} — épuisé sur cette clé"
|
| 484 |
else:
|
| 485 |
decorated = f"✅ {label}"
|
| 486 |
out.append((decorated, key))
|
|
|
|
| 823 |
current_gemini_key: Optional[str] = (
|
| 824 |
pick_unblown_gemini_key(model) if model in GEMINI_NATIVE_REQUIRED else None
|
| 825 |
)
|
| 826 |
+
# Annonce au registry quelle clé est active → le dropdown
|
| 827 |
+
# reflètera ce qui est blown SUR CETTE CLÉ.
|
| 828 |
+
set_current_gemini_key(current_gemini_key)
|
| 829 |
try:
|
| 830 |
llm = _build_llm(model, api_key, use_thinking=use_thinking,
|
| 831 |
gemini_key_override=current_gemini_key)
|
|
|
|
| 974 |
if next_key:
|
| 975 |
pool_n = gemini_pool_size()
|
| 976 |
current_gemini_key = next_key
|
| 977 |
+
set_current_gemini_key(current_gemini_key)
|
| 978 |
llm = _build_llm(
|
| 979 |
model, api_key,
|
| 980 |
use_thinking=use_thinking,
|
|
|
|
| 1043 |
if next_key:
|
| 1044 |
pool_n = gemini_pool_size()
|
| 1045 |
current_gemini_key = next_key
|
| 1046 |
+
set_current_gemini_key(current_gemini_key)
|
| 1047 |
llm = _build_llm(
|
| 1048 |
model, api_key,
|
| 1049 |
use_thinking=use_thinking,
|
|
@@ -1096,9 +1096,13 @@ def run_jarvis_flow(
|
|
| 1096 |
# Si modèle Gemini natif, on pick une clé du pool en explicite
|
| 1097 |
# pour pouvoir la marquer "blown" plus tard si nécessaire.
|
| 1098 |
try:
|
| 1099 |
-
from app import
|
|
|
|
|
|
|
|
|
|
| 1100 |
if model in GEMINI_NATIVE_REQUIRED:
|
| 1101 |
current_gemini_key = pick_unblown_gemini_key(model)
|
|
|
|
| 1102 |
except Exception:
|
| 1103 |
pass # app pas importable (test mode) → comportement standard
|
| 1104 |
llm = build_llm_fn(model, api_key, use_thinking=use_thinking,
|
|
@@ -1345,6 +1349,11 @@ def run_jarvis_flow(
|
|
| 1345 |
if next_key:
|
| 1346 |
pool_n = gemini_pool_size()
|
| 1347 |
current_gemini_key = next_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
llm = build_llm_fn(
|
| 1349 |
model, api_key,
|
| 1350 |
use_thinking=use_thinking,
|
|
@@ -1453,6 +1462,11 @@ def run_jarvis_flow(
|
|
| 1453 |
# langgraph reprend là où il en était.
|
| 1454 |
pool_n = gemini_pool_size()
|
| 1455 |
current_gemini_key = next_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1456 |
llm = build_llm_fn(
|
| 1457 |
model, api_key,
|
| 1458 |
use_thinking=use_thinking,
|
|
|
|
| 1096 |
# Si modèle Gemini natif, on pick une clé du pool en explicite
|
| 1097 |
# pour pouvoir la marquer "blown" plus tard si nécessaire.
|
| 1098 |
try:
|
| 1099 |
+
from app import (
|
| 1100 |
+
GEMINI_NATIVE_REQUIRED, pick_unblown_gemini_key,
|
| 1101 |
+
set_current_gemini_key as _set_current_key,
|
| 1102 |
+
)
|
| 1103 |
if model in GEMINI_NATIVE_REQUIRED:
|
| 1104 |
current_gemini_key = pick_unblown_gemini_key(model)
|
| 1105 |
+
_set_current_key(current_gemini_key)
|
| 1106 |
except Exception:
|
| 1107 |
pass # app pas importable (test mode) → comportement standard
|
| 1108 |
llm = build_llm_fn(model, api_key, use_thinking=use_thinking,
|
|
|
|
| 1349 |
if next_key:
|
| 1350 |
pool_n = gemini_pool_size()
|
| 1351 |
current_gemini_key = next_key
|
| 1352 |
+
try:
|
| 1353 |
+
from app import set_current_gemini_key as _set_cur
|
| 1354 |
+
_set_cur(current_gemini_key)
|
| 1355 |
+
except Exception:
|
| 1356 |
+
pass
|
| 1357 |
llm = build_llm_fn(
|
| 1358 |
model, api_key,
|
| 1359 |
use_thinking=use_thinking,
|
|
|
|
| 1462 |
# langgraph reprend là où il en était.
|
| 1463 |
pool_n = gemini_pool_size()
|
| 1464 |
current_gemini_key = next_key
|
| 1465 |
+
try:
|
| 1466 |
+
from app import set_current_gemini_key as _set_cur
|
| 1467 |
+
_set_cur(current_gemini_key)
|
| 1468 |
+
except Exception:
|
| 1469 |
+
pass
|
| 1470 |
llm = build_llm_fn(
|
| 1471 |
model, api_key,
|
| 1472 |
use_thinking=use_thinking,
|