Files
nas-runbooks/common/bifrost-google-provider-key-rotation.md

5.1 KiB

Bifrost -- rotation entre cles provider bloquee par max_retries=0

Instance auteur : hermes-tt (diagnostic Claude, session nyora-notes-tt) Date : 2026-08-05 Tags : bifrost, llm-gateway, rate-limit, google Statut : valide (fix applique, limite connue documentee)


Probleme

search_onedrive_archive et le backfill embeddings de nyora-notes-tt echouaient en continu avec 429 Too Many Requests sur POST /v1/embeddings. Message Google exact : Quota exceeded for metric: generativelanguage.googleapis.com/embed_content_free_tier_requests, limit: 1000, model: gemini-embedding-1.0.

3 cles Google etaient pourtant configurees dans Bifrost (google-key1/2/3), toutes actives, poids egal, allow_all_keys=1 sur la VK consommatrice -- en theorie de quoi absorber 3x le quota.


Contexte et contraintes

Bifrost (config.db, table config_providers) stocke network_config_json par provider, incluant max_retries. Table governance_virtual_key_provider_configs confirme les permissions cote VK (allow_all_keys), mais la rotation reelle entre cles au sein d'un meme provider est geree par la couche retry, pas par les permissions VK seules.


Ce qui NE fonctionne PAS

Tentative Erreur obtenue Raison de l'echec
Configurer 3 cles + allow_all_keys=1 sur la VK 429 systematique, une seule cle tentee allow_all_keys autorise l'usage des cles mais ne force pas d'essai multiple -- ca depend de max_retries
Attendre / relancer la requete Meme 429, meme cle a chaque fois Sans retry, Bifrost ne re-tire jamais une autre cle du pool

Verification en base (logs.db, colonne attempt_trail) : avant fix, [{"attempt":0,"key_id":"...","fail_reason":"rate_limit_error","triggered_rotation":false}] -- un seul essai, triggered_rotation a false.


Solution validee

max_retries etait a 0 dans network_config_json du provider google (config_providers.id=46) -- valeur par defaut sur TOUS les providers de cette instance Bifrost (opencode, openrouter, google, nvidia_nim, groq), probablement pour eviter de multiplier latence/cout ailleurs. Mis a 2 specifiquement pour google (pas de changement global), avec retry_backoff_initial=1 et retry_backoff_max=10.

# 1. Backup obligatoire avant toute modification
cp /volume1/docker/bifrost/data/config.json /volume1/docker/bifrost/data/config.json.bak-$(date +%Y%m%d-%H%M%S)
cp /volume1/docker/bifrost/data/config.db /volume1/docker/bifrost/data/config.db.bak-$(date +%Y%m%d-%H%M%S)

# 2. config.json (fichier lu au BOOT -- piege connu, sans ca le fix ne survit pas au restart)
python3 -c "
import json
path = '/volume1/docker/bifrost/data/config.json'
d = json.load(open(path))
d['providers']['google']['network_config']['max_retries'] = 2
d['providers']['google']['network_config']['retry_backoff_initial'] = 1
d['providers']['google']['network_config']['retry_backoff_max'] = 10
json.dump(d, open(path, 'w'), indent=2, ensure_ascii=False)
"

# 3. config.db (etat vivant lu par le process en cours)
python3 -c "
import sqlite3, json
c = sqlite3.connect('/volume1/docker/bifrost/data/config.db')
cur = c.cursor()
cur.execute('SELECT network_config_json FROM config_providers WHERE id=46')
nc = json.loads(cur.fetchone()[0])
nc.update({'max_retries': 2, 'retry_backoff_initial': 1, 'retry_backoff_max': 10})
cur.execute('UPDATE config_providers SET network_config_json=? WHERE id=46', [json.dumps(nc)])
c.commit()
"

# 4. Redemarrage (config.json + config.db doivent etre coherents AVANT le restart)
docker restart bifrost

Verification

# Apres une requete embeddings ayant echoue, verifier attempt_trail dans logs.db
python3 -c "
import sqlite3, json
c = sqlite3.connect('/volume1/docker/bifrost/data/logs.db')
cur = c.cursor()
cur.execute(\"SELECT attempt_trail FROM logs WHERE provider='google' ORDER BY timestamp DESC LIMIT 1\")
print(cur.fetchone()[0])
"
# Resultat attendu : plusieurs entrees "attempt":0,1,2... avec des key_id DIFFERENTS
# et "triggered_rotation":true sur les echecs intermediaires.

Pieges specifiques DSM / NAS

  • Ne pas se fier a allow_all_keys seul -- ce flag autorise, il ne force pas d'essai. La rotation reelle depend de max_retries > 0 sur le provider.
  • config.json ET config.db doivent etre synchronises -- piege deja documente ailleurs (rechargement des cles au restart), s'applique aussi a max_retries.
  • Limite residuelle non resolue par ce fix : si les N cles configurees partagent le meme quota (meme projet / meme compte Google Cloud, ce qui semble etre le cas ici -- les 3 cles ont echoue ensemble apres le fix), la rotation ne cree AUCUNE capacite supplementaire. Rotation != pool de quota independant. A verifier cle par cle si le probleme se reproduit apres ce fix (voir attempt_trail : si les 3 key_id echouent systematiquement ensemble, c'est un quota partage, pas un bug de rotation).

References