Moteur OpenRouter (google/gemini-3.5-flash-lite), par defaut
Meme consigne, meme encodage, meme repli local que le moteur Gemini direct ; seuls l'adresse, l'authentification (Bearer) et le format de requete (input_audio, compatible OpenAI) changent. Cle : trousseau zonza-openrouter, puis OPENROUTER_API_KEY. Un 200 portant un objet error est traite comme un echec, pas comme une transcription vide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ def test_un_moteur_inconnu_est_refuse_clairement():
|
||||
|
||||
|
||||
def test_la_table_ne_contient_que_les_moteurs_reconnus():
|
||||
assert set(MODELES_PAR_MOTEUR) == {"mlx", "faster-cpu", "faster-gpu", "gemini"}
|
||||
assert set(MODELES_PAR_MOTEUR) == {"mlx", "faster-cpu", "faster-gpu", "gemini", "openrouter"}
|
||||
|
||||
|
||||
def test_le_reglage_manuel_prime_sur_la_table():
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Moteur OpenRouter : même modèle Gemini, facturé et routé par OpenRouter.
|
||||
|
||||
Aucun test ne touche le réseau : l'envoi HTTP et la lecture de la clé sont
|
||||
injectés. Le repli local, lui, est déjà éprouvé dans test_gemini.py.
|
||||
"""
|
||||
import base64
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from moteur import choisir_transcripteur
|
||||
from moteur.gemini import ErreurGemini, TranscripteurAvecRepli
|
||||
from moteur.openrouter import TranscripteurOpenRouter, lire_cle_openrouter
|
||||
|
||||
|
||||
def _reponse(texte):
|
||||
return {"choices": [{"message": {"role": "assistant", "content": texte}}]}
|
||||
|
||||
|
||||
def _transcripteur(envoyer, cle="cle-test"):
|
||||
return TranscripteurOpenRouter(
|
||||
"google/gemini-x", "fr", "Vocabulaire : Gitea, iCloud.",
|
||||
lire_cle=lambda: cle, envoyer=envoyer,
|
||||
)
|
||||
|
||||
|
||||
def test_la_requete_porte_l_audio_le_modele_et_le_vocabulaire():
|
||||
vu = {}
|
||||
|
||||
def envoyer(url, entetes, corps, delai_s):
|
||||
vu.update(url=url, entetes=entetes, corps=corps)
|
||||
return _reponse("Bonjour.")
|
||||
|
||||
_transcripteur(envoyer).transcrire(np.zeros(1600, dtype="float32"))
|
||||
|
||||
assert vu["url"] == "https://openrouter.ai/api/v1/chat/completions"
|
||||
assert vu["entetes"]["Authorization"] == "Bearer cle-test"
|
||||
assert vu["corps"]["model"] == "google/gemini-x"
|
||||
systeme, utilisateur = vu["corps"]["messages"]
|
||||
assert "Gitea" in systeme["content"] and "iCloud" in systeme["content"]
|
||||
audio = next(p["input_audio"] for p in utilisateur["content"] if p["type"] == "input_audio")
|
||||
assert audio["format"] == "wav"
|
||||
assert base64.b64decode(audio["data"])[:4] == b"RIFF"
|
||||
|
||||
|
||||
def test_la_cle_ne_part_jamais_dans_l_url():
|
||||
vu = {}
|
||||
_transcripteur(lambda url, e, c, d: vu.setdefault("url", url) and _reponse("x")) \
|
||||
.transcrire(np.zeros(1600, dtype="float32"))
|
||||
assert "cle-test" not in vu["url"]
|
||||
|
||||
|
||||
def test_le_texte_rendu_est_nettoye():
|
||||
t = _transcripteur(lambda *a: _reponse(" Je pousse sur Gitea. "))
|
||||
assert t.transcrire(np.zeros(1600, dtype="float32")) == "Je pousse sur Gitea."
|
||||
|
||||
|
||||
def test_une_erreur_rendue_dans_un_200_est_une_erreur_explicite():
|
||||
# OpenRouter peut répondre 200 avec un objet `error` (fournisseur en panne).
|
||||
t = _transcripteur(lambda *a: {"error": {"code": 502, "message": "fournisseur indisponible"}})
|
||||
with pytest.raises(ErreurGemini) as e:
|
||||
t.transcrire(np.zeros(1600, dtype="float32"))
|
||||
assert "fournisseur indisponible" in str(e.value)
|
||||
|
||||
|
||||
def test_une_reponse_sans_choix_est_une_erreur_explicite():
|
||||
t = _transcripteur(lambda *a: {"choices": []})
|
||||
with pytest.raises(ErreurGemini):
|
||||
t.transcrire(np.zeros(1600, dtype="float32"))
|
||||
|
||||
|
||||
def test_une_cle_absente_est_signalee_des_le_warmup_sans_appel():
|
||||
appels = []
|
||||
t = _transcripteur(lambda *a: appels.append(a), cle=None)
|
||||
with pytest.raises(ErreurGemini) as e:
|
||||
t.warmup()
|
||||
assert "zonza-openrouter" in str(e.value)
|
||||
assert appels == []
|
||||
|
||||
|
||||
def test_la_cle_se_lit_sous_son_propre_nom():
|
||||
assert lire_cle_openrouter(trousseau=lambda: None,
|
||||
environ={"OPENROUTER_API_KEY": "or", "GEMINI_API_KEY": "g"}) == "or"
|
||||
assert lire_cle_openrouter(trousseau=lambda: None, environ={"GEMINI_API_KEY": "g"}) is None
|
||||
|
||||
|
||||
def test_la_fabrique_rend_openrouter_enveloppe_d_un_repli():
|
||||
t = choisir_transcripteur(nom="openrouter", modele="google/gemini-x", langue="fr", amorce="")
|
||||
assert isinstance(t, TranscripteurAvecRepli)
|
||||
assert isinstance(t.principal, TranscripteurOpenRouter)
|
||||
Reference in New Issue
Block a user