feat(providers): self-hosted OpenAI-compatible STT, TTS and embedding providers

Add Self-hosted STT/TTS/Embedding providers that read baseUrl per connection
instead of a fixed registry endpoint, so 9Router can point at whisper.cpp,
faster-whisper, Kokoro-FastAPI, llama-server, vLLM, Infinity, and similar
OpenAI-compatible local servers.

Self-hosted Embedding refuses to run without a baseUrl rather than falling
back to api.openai.com like openaiCompatNode does, since that fallback would
silently send input text and the API key to OpenAI under a provider named
"Self-hosted". Also fixes embeddingsCore to catch adapter build errors as a
400 instead of letting them escape uncaught, and bounds the upstream fetch
with FETCH_CONNECT_TIMEOUT_MS to avoid hanging forever on a dead endpoint.

Self-hosted TTS treats a bare model value as the model rather than the voice,
since the generic OpenAI TTS convention (bare = voice) is backwards for a
provider where the model is the variable part.
This commit is contained in:
omar-nahhas
2026-08-05 13:33:28 +07:00
committed by decolua
parent b480892952
commit fe547f4dc0
12 changed files with 387 additions and 9 deletions

View File

@@ -1,3 +1,24 @@
# Unreleased
## Features
- **Providers**: add Self-hosted STT / TTS / Embedding — point 9Router at your own
OpenAI-compatible speech and embedding servers (whisper.cpp, faster-whisper,
Kokoro-FastAPI, llama-server, vLLM, Infinity). Unlike the named cloud providers
these read `baseUrl` per connection, so one provider can front several machines
## Fixes
- **TTS**: a bare self-hosted model name is the MODEL, not the voice — `kokoro`
was parsed as a voice against a default model, 404ing or synthesising with the
wrong one
- **Embeddings**: self-hosted embeddings no longer fall back to `api.openai.com`
when a connection has no `baseUrl` — that silently sent the input text and API
key to OpenAI under a provider named "Self-hosted"
- **Embeddings**: an adapter that rejects a misconfigured connection now returns
400 with the reason instead of escaping the handler uncaught
- **Embeddings**: bound the upstream fetch with `FETCH_CONNECT_TIMEOUT_MS` — an
endpoint that drops packets never returns headers, so the request previously
hung indefinitely
# v0.5.45 (2026-07-30)
## Features