Files
nas-runbooks/hermes-tt/nyora-notes-tt-embeddings-bypass-bifrost.md

5.2 KiB

nyora-notes-tt -- contournement direct NVIDIA NIM/OpenRouter pour onedrive_vec

Instance auteur : hermes-tt (Claude, session nyora-notes-tt) Date : 2026-08-05 Tags : nyora-notes-tt, bifrost, embeddings, nvidia-nim, openrouter Statut : valide, backfill en cours


Probleme

Le quota gratuit Google (embed_content_free_tier_requests, 1000/j sur gemini-embedding-001) etait epuise, cassant a la fois le backfill embeddings et la recherche vectorielle en temps reel de search_onedrive_archive. Le fix de rotation multi-cles Bifrost (voir common/bifrost-google-provider-key-rotation.md) ne resout rien : les 3 cles Google partagent le meme pool de quota.

Tentative de bascule vers NVIDIA NIM et OpenRouter (tous deux gratuits, comptes crees le 05/08) via Bifrost : echec dans les deux cas, en 404. Confirme comme bugs Bifrost upstream, pas une erreur de config :

  • NVIDIA NIM : issue GitHub maximhq/bifrost#2356 (ouverte) -- Bifrost ne transmet pas le header Authorization a l'API amont quand le provider est en custom_provider_config.base_provider_type: openai.
  • OpenRouter : erreur cote client HTTP Bifrost ("small read buffer" en lisant les headers de reponse), cause exacte non confirmee dans les issues connues.

Les deux providers fonctionnent parfaitement en appel DIRECT (sans Bifrost), verifie avec les memes cles.


Solution validee : contournement direct, scope isole a onedrive_vec

notes_vec (mail, alimentee par pipeline_processor.py) et onedrive_vec (OneDrive, alimentee par embed_onedrive_docs.py) sont deux tables separees, toutes deux 768d/Google avant ce fix. Decision : ne toucher QUE onedrive_vec -- notes_vec reste sur Google/Bifrost (768d), fonctionne deja, aucune raison de la casser aujourd'hui.

Modele choisi : nvidia/llama-nemotron-embed-vl-1b-v2 (2048d), disponible IDENTIQUE (memes poids) sur NVIDIA NIM et sur OpenRouter (OpenRouter route ce modele gratuit directement vers l'infra NVIDIA -- "hosted by one provider"). Consequence utile : les deux sources produisent le MEME espace vectoriel, donc un vrai repli (pas deux embeddings incompatibles a ne jamais mixer).

Code (embed_onedrive_docs.py et nouvelle fonction generate_onedrive_embedding dans mcp_server.py, strictement separee de generate_gemini_embedding qui reste utilisee par search_mail_memory) :

NVIDIA_NIM_API_KEY = os.getenv("NVIDIA_NIM_API_KEY", "")
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
EMBEDDING_MODEL = "nvidia/llama-nemotron-embed-vl-1b-v2"
EMBEDDING_DIMENSIONS = 2048

def get_embedding(text):
    text = text[:8000]
    if NVIDIA_NIM_API_KEY:
        try:
            resp = httpx.post(
                "https://integrate.api.nvidia.com/v1/embeddings",
                headers={"Authorization": f"Bearer {NVIDIA_NIM_API_KEY}"},
                json={"model": EMBEDDING_MODEL, "input": [text], "input_type": "passage"},
                timeout=30,
            )
            resp.raise_for_status()
            vec = resp.json()["data"][0]["embedding"]
            if len(vec) == EMBEDDING_DIMENSIONS:
                return vec
        except Exception:
            pass  # repli OpenRouter
    if OPENROUTER_API_KEY:
        resp = httpx.post(
            "https://openrouter.ai/api/v1/embeddings",
            headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
            json={"model": EMBEDDING_MODEL, "input": text},
            timeout=30,
        )
        resp.raise_for_status()
        return resp.json()["data"][0]["embedding"]

Piege NVIDIA NIM specifique : le parametre input_type ("query" ou "passage") est requis par leurs modeles NV-Embed*, absent chez OpenRouter -- mis uniquement sur l'appel NVIDIA direct.

Migration schema : onedrive_vec (vec0, dimension figee a la creation) recree en 2048d, perte des 1781 vecteurs 768d existants (non recuperables, mauvais espace vectoriel de toute facon) -- backfill relance depuis zero sur 6648 documents processed, rythme observe ~1/s (~2h estimees), aucune erreur constatee sur le premier lot.

Secrets : cles dans /volume1/docker/nyora-notes-tt/.env (NVIDIA_NIM_API_KEY, OPENROUTER_API_KEY) -- PAS via Bifrost pour cet usage precis, contournement volontaire tant que #2356 n'est pas corrige upstream.


Verification

docker exec nyora-notes-tt python3 -c "
import sqlean as sqlite3, sqlite_vec
c = sqlite3.connect('/app/nyora-notes-tt.db')
c.enable_load_extension(True); sqlite_vec.load(c); c.enable_load_extension(False)
print(c.execute('SELECT COUNT(*) FROM onedrive_vec').fetchone()[0])
"

Reste a faire

  • Laisser le backfill se terminer (~2h, tourne en arriere-plan via docker exec -d, resumable si interrompu -- rescanner simplement au redemarrage, la logique "deja embedde" se base sur les rowid presents).
  • Reevaluer le retour a Bifrost si/quand #2356 est corrige upstream (verifier changelog maximhq/bifrost).
  • Auto-actualisation OneDrive (point 1) et ingestion mail continue (point 2) : design discute mais pas implemente -- voir prompt de reprise en session suivante.

References