fix: OpenRouter-STT auf JSON/base64 statt multipart (passend zur OpenRouter-API)
Live-Test gegen OpenRouter ergab: /audio/transcriptions erwartet JSON mit
input_audio:{data(base64),format} - NICHT multipart/form-data. Die fruehere
Umstellung auf multipart (OpenAI-Standard) war fuer OpenRouter falsch.
- STT-Provider zurueck auf JSON/base64 (mit robuster Fehlerbehandlung)
- verifiziert per TTS->WAV->STT Round-Trip: Text korrekt zurueck transkribiert
- LLM, TTS und STT funktionieren jetzt alle live gegen OpenRouter
- Doku-Referenz aktualisiert (multipart -> JSON/base64)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1eb79c1f09
commit
1d338810b1
2 changed files with 18 additions and 9 deletions
|
|
@ -182,7 +182,7 @@ Endpunkt-/Provider-Auswahl ist über **Request-Body** (pro Aufruf), **Session**
|
||||||
## 8. Stand der Implementierung
|
## 8. Stand der Implementierung
|
||||||
|
|
||||||
**Umgesetzt:** FastAPI-Gateway, alle o. g. REST-Endpunkte; OpenRouter-Adapter für
|
**Umgesetzt:** FastAPI-Gateway, alle o. g. REST-Endpunkte; OpenRouter-Adapter für
|
||||||
STT (multipart), LLM und TTS; lokaler OpenAI-kompatibler LLM-Adapter; regelbasierte
|
STT (JSON/base64), LLM und TTS; lokaler OpenAI-kompatibler LLM-Adapter; regelbasierte
|
||||||
Pipeline; geschichtete Config + Profile; Registry + einheitliche Route-Auflösung;
|
Pipeline; geschichtete Config + Profile; Registry + einheitliche Route-Auflösung;
|
||||||
Device Router (strikt, Singleton); Output-Lifecycle; **Authentifizierung
|
Device Router (strikt, Singleton); Output-Lifecycle; **Authentifizierung
|
||||||
(Bearer-Token) + persistenter SQLite-Store für Nutzer/Sessions + Mandanten-Trennung
|
(Bearer-Token) + persistenter SQLite-Store für Nutzer/Sessions + Mandanten-Trennung
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from app.providers.stt.base import STTProvider
|
from app.providers.stt.base import STTProvider
|
||||||
|
|
@ -16,12 +18,17 @@ class OpenRouterSTTProvider(STTProvider):
|
||||||
if not audio_bytes:
|
if not audio_bytes:
|
||||||
raise ValueError("STT input audio is empty")
|
raise ValueError("STT input audio is empty")
|
||||||
|
|
||||||
# OpenAI-kompatibler /audio/transcriptions-Endpunkt erwartet multipart/form-data
|
# OpenRouter /audio/transcriptions erwartet JSON mit base64-Audio
|
||||||
# mit binärem file-Feld, nicht JSON mit base64.
|
# (NICHT multipart/form-data). Quelle: OpenRouter-Doku (STT).
|
||||||
files = {"file": (f"audio.{fmt}", audio_bytes, f"audio/{fmt}")}
|
payload = {
|
||||||
data: dict[str, str] = {"model": self.model}
|
"model": self.model,
|
||||||
|
"input_audio": {
|
||||||
|
"data": base64.b64encode(audio_bytes).decode("utf-8"),
|
||||||
|
"format": fmt,
|
||||||
|
},
|
||||||
|
}
|
||||||
if language:
|
if language:
|
||||||
data["language"] = language
|
payload["language"] = language
|
||||||
|
|
||||||
timeout = httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=10.0)
|
timeout = httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=10.0)
|
||||||
|
|
||||||
|
|
@ -29,9 +36,11 @@ class OpenRouterSTTProvider(STTProvider):
|
||||||
try:
|
try:
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
"https://openrouter.ai/api/v1/audio/transcriptions",
|
"https://openrouter.ai/api/v1/audio/transcriptions",
|
||||||
headers={"Authorization": f"Bearer {self.api_key}"},
|
headers={
|
||||||
files=files,
|
"Authorization": f"Bearer {self.api_key}",
|
||||||
data=data,
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
json=payload,
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except httpx.HTTPStatusError as exc:
|
except httpx.HTTPStatusError as exc:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue