expAge commited on
Commit
4531ded
·
1 Parent(s): 6bf6fcf

fix(ui): dropdown reflete blown SUR LA CLE COURANTE (per-key, pas per-pool)

Browse files

L'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.

Files changed (2) hide show
  1. app.py +44 -18
  2. jarvis.py +15 -1
app.py CHANGED
@@ -407,17 +407,35 @@ def mark_gemini_key_blown(key: str, model: str) -> None:
407
  _bump_registry_version()
408
 
409
 
410
- def is_model_fully_blown(model: str) -> bool:
411
- """True si TOUTES les clés du pool sont blown ou invalides pour ce
412
- modèle aujourd'hui. Utilisé pour griser dans le dropdown UI."""
413
- keys = _parse_google_keys()
414
- if not keys:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  return False
416
- today = _today_utc_str()
417
- return all(
418
- (k in _INVALID_KEYS) or _BLOWN_TODAY.get((k, model, today), False)
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 (label complet conservé)
452
- - ❌ pour les Gemini natifs dont TOUTES les clés du pool sont
453
- blown aujourd'hui : on remplace le suffixe `(X req/jour)`
454
- par `— épuisé aujourd'hui`.
 
 
 
 
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 is_model_fully_blown(key):
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é aujourd'hui"
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,
jarvis.py CHANGED
@@ -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 GEMINI_NATIVE_REQUIRED, pick_unblown_gemini_key
 
 
 
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,