Connect 9Router to AvalAI
9Router can register AvalAI as an OpenAI-compatible upstream, assign it a model prefix, and expose the resulting models through a local OpenAI-compatible gateway. This guide keeps the first connection simple, separates Chat Completions from Responses, and delays fallback and request transformations until the direct path is proven.
Validated: The public labels, environment contract, and container paths on this page were checked against official 9Router sources on 2026-08-06. Recheck the AvalAI model catalog and the upstream release before deployment.
What this integration is
The request flow is:
OpenAI-compatible client → local 9Router /v1 gateway → prefixed AvalAI provider node → AvalAI
The AvalAI key authenticates 9Router to AvalAI. A different key generated in the 9Router dashboard authenticates your downstream client to 9Router. Do not reuse or confuse these two credentials.
Use 9Router when you need a local gateway, prefixed model IDs, multiple upstream connections, priorities, or controlled fallback. Connect directly to AvalAI when you do not need another routing layer.
What you need
- Node.js 20+ and npm for the local evaluation path, or Docker with Compose for the durable path.
- A dedicated AvalAI API key.
- A current model ID and its supported endpoint.
- A strong dashboard password and independent random values for the 9Router signing secrets.
- Local access to
http://localhost:20128; do not expose the dashboard before changing its credentials.
The current pilot is gpt-5.4-mini. It supports both Chat Completions and Responses, but those are separate 9Router provider-node types.
Preflight AvalAI
Confirm these values first:
| Check | Required value |
|---|---|
| AvalAI base URL | https://api.avalai.ir/v1 |
| Chat route | /v1/chat/completions |
| Responses route | /v1/responses |
| Pilot model | gpt-5.4-mini |
| Gateway URL after setup | http://localhost:20128/v1 |
A successful Check proves only the entered upstream key, URL, API type, and optional model at that moment. It does not create the persistent API-key connection used for later requests.
AvalAI does not currently support the gpt-transcribe or gpt-live-transcribe identifiers. Speech connections must use a model currently listed for the matching /v1/audio/* route.
Install 9Router
For a local evaluation, use the official npm package:
npm install -g 9router
9routerOpen http://localhost:20128. For an operational instance, skip the global package and use the pinned Compose example below.
Connect AvalAI
Create the provider node
- Open Providers.
- Choose Add OpenAI Compatible.
- Set Name to
AvalAI. - Set Prefix to
avalai. The prefix becomes part of every downstream model ID. - Choose Chat Completions for the first connection. Create a separate node with Responses API only when the selected model supports
/v1/responses. - Set Base URL to
https://api.avalai.ir/v1. - Enter the AvalAI key in API Key (for Check).
- Optionally enter
gpt-5.4-miniin Model ID (optional). - Select Check, review the result, and create the node.
The validation key is intentionally transient. Continue to the next step.
Add the persistent upstream connection
Open the new AvalAI provider and its Connections card, then select Add Connection. Enter:
- Name: a descriptive name such as
AvalAI production; - API Key: the dedicated AvalAI key;
- Priority:
1for the first and only connection; - Proxy Pool:
Noneunless you have a separately reviewed outbound proxy requirement.
Select Validate, then Save. Confirm that the connection is active before adding another key, round-robin, or fallback.
Understand prefixed model IDs
With prefix avalai, the downstream gateway model is avalai/gpt-5.4-mini, not the bare AvalAI ID. Create a downstream 9Router API key in the dashboard, then inspect the local model list:
curl http://localhost:20128/v1/models \
-H "Authorization: Bearer replace-with-9router-key"The client sends the 9Router key to localhost; 9Router sends the saved AvalAI key upstream.
Verify the first flow
Keep one active AvalAI connection and disable fallback while verifying:
curl -N http://localhost:20128/v1/chat/completions \
-H "Authorization: Bearer replace-with-9router-key" \
-H "Content-Type: application/json" \
-H "X-9Router-Token-Saver: off" \
-d '{
"model": "avalai/gpt-5.4-mini",
"messages": [{"role": "user", "content": "Reply with exactly: AvalAI via 9Router"}],
"stream": true
}'Verify these layers separately:
- Provider-node Check succeeds.
- The persistent connection shows active status.
/v1/modelsincludes the prefixed model.- The streaming Chat Completions request returns the expected model output.
- Only then enable token savers, request rewrites, round-robin, proxy pools, or fallback.
- For Responses, repeat the process with a distinct Responses API node and
/v1/responsesrequest.
The header X-9Router-Token-Saver: off bypasses token savers for one diagnostic request. It helps distinguish an upstream protocol problem from a transformation problem.
Supported capabilities
The generic language-model node covers only the API type selected when that node was created.
| Status | Capability | What to expect |
|---|---|---|
| Direct | Model discovery | 9Router exposes its routed and prefixed catalog through local /v1/models. |
| Direct | Chat Completions | Choose Chat Completions and call local /v1/chat/completions. |
| Direct | Responses | Choose Responses API in a separate node; the AvalAI model must support the route. |
| Model/route dependent | Streaming | The selected model, upstream route, 9Router adapter, and downstream client must agree on stream framing. |
| Model/route dependent | Tools and structured output | Requires model support and compatible pass-through of tools, tool_choice, and schema fields. |
| Model/route dependent | Vision/image input | Requires an AvalAI vision model and a downstream client that sends the correct multimodal shape. |
| Separate configuration | Embeddings | Add a Self-hosted Embedding connection with base https://api.avalai.ir/v1, a saved key, and a current embeddings model such as text-embedding-v4. |
| Separate configuration | Speech-to-text | Add a Self-hosted STT connection with the full URL https://api.avalai.ir/v1/audio/transcriptions and a current transcription model. |
| Separate configuration | Text-to-speech | Add a Self-hosted TTS connection using server root https://api.avalai.ir so the adapter appends /v1/audio/speech; choose a current speech model. |
| Separate configuration | Web search | 9Router web-search providers and the model's own web-search route are different configurations. Verify which one the downstream client invokes. |
| Unsupported or unvalidated | Image generation, Realtime, and video through the generic node | No AvalAI mapping was validated merely from the language provider. Do not infer these routes from OpenAI compatibility. |
The labels Self-hosted STT, Self-hosted TTS, and Self-hosted Embedding are 9Router provider types. Their configurable URL and key fields expose the documented OpenAI-shaped route, but this guide did not run credentialed AvalAI media requests through them.
Run with Docker Compose
The official image is multi-platform. Pin a reviewed release, bind the dashboard to loopback, persist /app/data, and omit the optional Headroom sidecar from the baseline:
services:
9router:
image: decolua/9router:v0.5.35
container_name: 9router
restart: unless-stopped
ports:
- "127.0.0.1:20128:20128"
volumes:
- 9router-data:/app/data
env_file:
- .env
environment:
DATA_DIR: /app/data
PORT: "20128"
HOSTNAME: 0.0.0.0
NODE_ENV: production
ENABLE_REQUEST_LOGS: "false"
volumes:
9router-data:Create an ignored .env with mode 0600. Generate every secret independently; do not use the example text as a real value:
JWT_SECRET=replace-with-an-independent-random-value
INITIAL_PASSWORD=replace-with-a-strong-dashboard-password
API_KEY_SECRET=replace-with-an-independent-random-value
MACHINE_ID_SALT=replace-with-an-independent-random-value
DATA_DIR=/app/data
ENABLE_REQUEST_LOGS=false
AUTH_COOKIE_SECURE=false
REQUIRE_API_KEY=trueThe upstream fallback for an unset INITIAL_PASSWORD is 123456. Treat that as insecure: set the variable before the first start and never expose an instance using the fallback. Set AUTH_COOKIE_SECURE=true when the dashboard is served through HTTPS.
Start and inspect the pinned instance:
chmod 600 .env
docker compose config --quiet
docker compose up -d
docker compose logs --tail=100 9routerThis baseline does not start Headroom, an outbound proxy, or a reverse proxy. Add those only after the direct AvalAI path works and their security boundary is reviewed.
Operate safely
- Keep port
20128on loopback or a private network unless an authenticated TLS ingress is already present. - Keep the dashboard login, downstream gateway keys, and AvalAI upstream keys separate.
- Leave
ENABLE_REQUEST_LOGS=falsefor sensitive workloads. Debug logs can contain prompts, responses, headers, files, and personal data. - Use a dedicated AvalAI key per 9Router deployment and monitor provider-side usage. The dashboard cost figures are estimates for display and tracking, not AvalAI billing records.
- Disable token savers and rewrites when diagnosing protocol behavior, then re-enable them one at a time.
- Protect the volume because it contains SQLite state, backups, certificates, logs, runtime configuration, saved provider credentials, and generated downstream keys.
Before an upgrade, stop the service and copy the durable data out of the stopped container:
docker compose stop 9router
mkdir -p backup/9router-data
docker cp 9router:/app/data/. backup/9router-data/
docker compose start 9routerRecord the current image tag. To upgrade, change only the reviewed tag, run docker compose config --quiet, pull, and recreate the service with the same volume. To roll back, restore the previous image tag first. Restore SQLite data only while the service is stopped and only from a tested backup.
Troubleshooting
| Symptom | Check this layer first | Safe next action |
|---|---|---|
Provider Check returns 401/403 | AvalAI key in the transient validation field | Verify the dedicated upstream key without exposing it. |
Local /v1 returns 401/403 | Downstream 9Router key | Use the gateway key copied from 9Router, not the AvalAI key. |
| Check succeeds but requests fail | Missing or inactive persistent connection | Add the key under Connections, validate, save, and confirm active status. |
| Model not found | Missing avalai/ prefix or wrong API-type node | Inspect local /v1/models and use its exact model ID. |
404 upstream | Base URL or route mismatch | Keep https://api.avalai.ir/v1 and match Chat vs Responses. |
| Stream stalls or output changes | Token saver, rewrite, fallback, or proxy | Send one request with X-9Router-Token-Saver: off and disable other transformations. |
Embeddings return 404 | Base URL missing /v1 | For Self-hosted Embedding, use https://api.avalai.ir/v1. |
| STT/TTS route is doubled | Wrong full-URL vs server-root shape | STT takes the full transcription URL; TTS takes the server root. |
| State disappears after restart | Missing /app/data volume or wrong DATA_DIR | Confirm DATA_DIR=/app/data and the named volume mount. |
| Dashboard shows high cost | Estimate confused with billing | Check the AvalAI usage/pricing source; do not treat the 9Router estimate as an invoice. |
Related AvalAI guides and official sources
AvalAI:
- API introduction
- Model catalog
- Model selection
- Embeddings
- Speech to text
- Text to speech
- Rate limits
- Production best practices
9Router:
Validation boundary
This guide was source-reviewed on 2026-08-06. The provider labels, route shapes, release tag, Markdown parity, and Compose syntax can be checked without credentials. This work did not install 9Router, pull or start its image, create a provider node, save an AvalAI key, send a live request, test fallback, restore SQLite, or expose a production gateway. Revalidate the upstream release and current AvalAI routes before operational use.