Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2506,15 +2506,16 @@ APP_LOG_TO_FILE=true
# DARIO_HOST=127.0.0.1
# DARIO_PORT=3456

# ── Dario embedded service ──
# Override the host/port the embedded Dario (Claude Code subscription proxy)
# daemon binds to and is reached at. Always bound to 127.0.0.1 — never
# configurable to 0.0.0.0. Rarely needed — defaults to 127.0.0.1:3456.
# Used by: src/lib/services/installers/dario.ts, src/lib/services/bootstrap.ts,
# src/app/api/services/dario/_lib.ts, src/app/api/services/dario/admin/_lib.ts,
# open-sse/executors/dario.ts
# DARIO_HOST=127.0.0.1
# DARIO_PORT=3456
# ── LLMLingua embedded service ──
# Override the port and base URL for the embedded LLMLingua-2 prompt compression
# sidecar daemon. Always bound to 127.0.0.1 — never configurable to 0.0.0.0.
# Defaults to port 20135 and http://127.0.0.1:20135.
# Used by: src/lib/services/bootstrap.ts, src/lib/services/installers/llmlingua.ts,
# open-sse/services/compression/engines/llmlingua/index.ts
# LLMLINGUA_PORT=20135
# LLMLINGUA_BASE_URL=http://127.0.0.1:20135
# Hugging Face model id the sidecar loads (LLMLingua-2 JS port).
# LLMLINGUA_MODEL=atjsh/llmlingua-2-js-tinybert-meetingbank

# ── Local hostnames (Docker networking) ──
# Comma-separated additional hostnames treated as "local" for provider routing.
Expand Down
1 change: 1 addition & 0 deletions changelog.d/features/12967-llmlingua-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **feat(services):** Added an LLMLingua embedded-service supervisor + HTTP sidecar wrapping the real `@atjsh/llmlingua-2` prompt-compression package (ONNX TinyBERT model, downloaded from Hugging Face on first use). The compression engine dispatches `/compress` calls to the sidecar over HTTP, falling back to the existing in-process worker-thread backend when the sidecar is down.
5 changes: 5 additions & 0 deletions config/quality/gate-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,11 @@
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/security-fase01.test.ts",
"disposition": "separately-invoked"
},
{
"name": "test:services:int",
"command": "cross-env RUN_SERVICES_INT=1 DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/integration/services/*.test.ts\"",
"disposition": "separately-invoked"
},
{
"name": "test:system",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/e2e/system-failover.test.ts",
Expand Down
5 changes: 1 addition & 4 deletions config/quality/test-discovery-baseline.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
{
"_comment": "Catraca de test-discovery (check-test-discovery.mjs). Cada entrada e um arquivo de teste que NENHUM runner coleta (ele nunca roda) — divida congelada na auditoria 6A.1 (2026-06-09; 195 originais, 135 religados no node runner em 6A.1c). So pode DIMINUIR: religue o teste (ajustando o glob do runner ou movendo o arquivo) e remova a entrada via --update. NAO adicione novos orfaos — corrija o runner.",
"_remaining_10": "10 orfaos restantes: 4 golden-set + 1 benchmark + 1 teste live + 1 stress (deliberadamente manuais — decidir runner/gating); 3 integration/services (gated RUN_SERVICES_INT=1, sem runner CI).",
"_remaining_6": "6 orfaos restantes: 4 golden-set + 1 benchmark + 1 teste live (deliberadamente manuais — decidir runner/gating). Os 3 integration/services foram religados por test:services:int (#12967).",
"orphans": [
"tests/benchmarks/pipeline-accuracy.test.ts",
"tests/golden-set/compression-caveman-v2.test.ts",
"tests/golden-set/compression-quality.test.ts",
"tests/golden-set/compression-savings.test.ts",
"tests/golden-set/compression-upstream-parity.test.ts",
"tests/integration/services/cliproxy-coexistence.test.ts",
"tests/integration/services/full-lifecycle.int.test.ts",
"tests/integration/services/route-guard-services.int.test.ts",
"tests/live/deepseek-web-live.test.ts"
]
}
46 changes: 40 additions & 6 deletions docs/frameworks/EMBEDDED-SERVICES.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: "Embedded Services"
description: "Reference for 9Router, CLIProxyAPI, Mux, Bifrost, and open-wa"
description: "Reference for 9Router, CLIProxyAPI, Mux, Bifrost, open-wa, and LLMLingua"
---

# Embedded Services

> **Version:** v3.8.44
> **Last updated:** 2026-09-09
> **Audience:** Engineers adding, maintaining, or debugging embedded services (9Router, CLIProxyAPI, Mux, Bifrost, open-wa).
> **Last updated:** 2026-09-16
> **Audience:** Engineers adding, maintaining, or debugging embedded services (9Router, CLIProxyAPI, Mux, Bifrost, open-wa, LLMLingua).

Embedded services are locally-installed process sidecar tools that OmniRoute installs, supervises, and
exposes as first-class routing targets. Unlike external providers (which are reached over the internet
Expand All @@ -32,7 +32,7 @@ via API keys), embedded services run on the same machine as OmniRoute and commun

### Why embedded services?

Six services are embedded:
Seven services are embedded:

| Service | npm package | Default port | Purpose |
| --------------- | ---------------------------------- | :----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -42,8 +42,9 @@ Six services are embedded:
| **Bifrost** | `@maximhq/bifrost` | 8080 | Go AI-gateway relay backend. When running, auto-selected by the relay route (`/v1/relay/`) |
| **Dario** | `@askalf/dario` | 3456 | Claude-subscription proxy — alternative/failover to CLIProxyAPI for Claude-Code-shaped traffic; the injected key becomes `DARIO_ADMIN_TOKEN` gating its `/admin/*` OAuth control plane |
| **open-wa** | `@open-wa/wa-automate` | 8323 | WhatsApp Web automation (headless Chromium via Puppeteer). Lifecycle-managed only — not a routing target. |
| **LLMLingua** | `@atjsh/llmlingua-2` | 20135 | Prompt-compression sidecar — real LLMLingua-2 ONNX model (JS/TS port of Microsoft's algorithm). `open-sse/services/compression/engines/llmlingua/index.ts` dispatches `/compress` over HTTP to it, falling back to the in-process worker-thread backend when the sidecar is down. Lifecycle-managed only — not a routing target. |

All six follow the same supervisory model:
All seven follow the same supervisory model:

- OmniRoute installs them under `DATA_DIR/services/{name}/` (isolated from OmniRoute's own `package.json`)
- OmniRoute spawns and monitors them as child processes
Expand Down Expand Up @@ -153,6 +154,7 @@ All six follow the same supervisory model:
| `src/lib/services/installers/cliproxy.ts` | npm install/update/uninstall for CLIProxyAPI |
| `src/lib/services/installers/mux.ts` | npm install/update/uninstall for Mux |
| `src/lib/services/installers/openwa.ts` | npm install/update/uninstall for open-wa |
| `src/lib/services/installers/llmlingua.ts` | npm install/update/uninstall for LLMLingua |
| `src/app/api/services/9router/_lib.ts` | `getOrInitSupervisor()` helper |
| `src/app/api/services/[name]/logs/route.ts` | Shared SSE logs endpoint |
| `open-sse/executors/ninerouter.ts` | Provider executor (Layer 4) |
Expand Down Expand Up @@ -540,7 +542,39 @@ in this integration yet.

---

### 4.7 Reverse proxy (9Router dashboard embed)
### 4.7 LLMLingua endpoints (8 routes)

LLMLingua is a prompt-compression sidecar wrapping `@atjsh/llmlingua-2` (real
ONNX token-classification model, downloaded from Hugging Face on first
`/compress` call). It uses the same endpoint shape as Bifrost (no API key —
`needsApiKey: false`, it never handles credentials).

| Method | Path | Description |
| ------ | ---------------------------------------------- | ------------------------------------------------------------------------- |
| `POST` | `/api/services/llmlingua/install` | npm-install `@atjsh/llmlingua-2` + peers, write the sidecar server script |
| `POST` | `/api/services/llmlingua/start` | Start the sidecar on port 20135 (default) |
| `POST` | `/api/services/llmlingua/stop` | Stop the sidecar |
| `POST` | `/api/services/llmlingua/restart` | Restart the sidecar |
| `POST` | `/api/services/llmlingua/update` | Update to the newer package version |
| `GET` | `/api/services/llmlingua/status` | Live + DB status |
| `POST` | `/api/services/llmlingua/auto-start` | Toggle auto-start |
| `POST` | `/api/services/llmlingua/auto-restart-adopted` | Toggle auto-restart of an adopted (pre-existing) instance |
| `GET` | `/api/services/llmlingua/logs` | SSE log tail (via shared `[name]/logs` dynamic route) |

**Sidecar contract:** the server script exposes `GET /health` (instant — does
not wait on the model) and `POST /compress` (`{ text, rate }` →
`{ text, compressed, ratio }`). The model loads lazily on the first
`/compress` call.

**Compression wiring:** `open-sse/services/compression/engines/llmlingua/index.ts`'s
`httpSidecarBackend` calls `LLMLINGUA_BASE_URL` (default
`http://127.0.0.1:20135`) and only accepts the sidecar's response when it is
strictly shorter than the input; any failure (not running, timeout, no-op
response) falls back to the in-process worker-thread backend (`./worker.ts`).

---

### 4.8 Reverse proxy (9Router dashboard embed)

The dashboard embeds the 9Router web UI inside an iframe via an internal reverse
proxy at:
Expand Down
141 changes: 140 additions & 1 deletion docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5930,6 +5930,145 @@ paths:
"500":
description: Import failed

/api/services/llmlingua/install:
post:
tags: [Embedded Services]
summary: Install LLMLingua
description: >-
Installs `@atjsh/llmlingua-2` (real LLMLingua-2 ONNX prompt-compression
package) plus its `@huggingface/transformers`/`js-tiktoken` peers under
DATA_DIR/services/llmlingua/, and writes the sidecar server script.
Accepts an optional `version` field (semver or `latest`).
**LOCAL_ONLY** — loopback only.
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
version:
type: string
default: latest
responses:
"200":
description: Installation result
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
installedVersion:
type: string
installPath:
type: string
durationMs:
type: number

/api/services/llmlingua/start:
post:
tags: [Embedded Services]
summary: Start LLMLingua
description: Starts the supervised LLMLingua sidecar process. **LOCAL_ONLY** — loopback only.
responses:
"200":
description: Service status after start
"409":
description: LLMLingua is not installed

/api/services/llmlingua/stop:
post:
tags: [Embedded Services]
summary: Stop LLMLingua
description: Stops the supervised LLMLingua sidecar process. **LOCAL_ONLY** — loopback only.
responses:
"200":
description: Service status after stop

/api/services/llmlingua/restart:
post:
tags: [Embedded Services]
summary: Restart LLMLingua
description: Restarts the supervised LLMLingua sidecar process. **LOCAL_ONLY** — loopback only.
responses:
"200":
description: Service status after restart
"409":
description: LLMLingua is not installed

/api/services/llmlingua/update:
post:
tags: [Embedded Services]
summary: Update LLMLingua
description: >-
Updates LLMLingua to the latest npm version. Stops the running process,
installs the new version, and restarts if it was previously running.
**LOCAL_ONLY** — loopback only.
responses:
"200":
description: Update result

/api/services/llmlingua/status:
get:
tags: [Embedded Services]
summary: Get LLMLingua status
description: Returns live and DB status for the supervised LLMLingua service. **LOCAL_ONLY** — loopback only.
responses:
"200":
description: LLMLingua service status

/api/services/llmlingua/auto-start:
post:
tags: [Embedded Services]
summary: Toggle LLMLingua auto-start
description: >-
When enabled, LLMLingua starts automatically on the next OmniRoute boot.
**LOCAL_ONLY** — loopback only.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [enabled]
properties:
enabled:
type: boolean
responses:
"204":
description: Auto-start flag updated
"400":
description: Invalid request body

/api/services/llmlingua/auto-restart-adopted:
post:
tags: [Embedded Services]
summary: Toggle LLMLingua auto-restart-when-adopted
description: >-
When enabled, an externally-adopted (not OmniRoute-spawned) LLMLingua
process is restarted under OmniRoute's own supervisor on the next
health-check cycle instead of being left as adopted-only.
**LOCAL_ONLY** — loopback only.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [enabled]
properties:
enabled:
type: boolean
responses:
"204":
description: Flag updated
"400":
description: Invalid request body
"500":
description: Update failed

/api/services/{name}/logs:
get:
tags: [Embedded Services]
Expand All @@ -5945,7 +6084,7 @@ paths:
required: true
schema:
type: string
enum: [9router, cliproxy]
enum: [9router, cliproxy, mux, bifrost, dario, openwa, llmlingua]
- name: tail
in: query
schema:
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,9 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
| `BIFROST_ENABLED` | `1` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | Master kill switch for the bifrost sidecar proxy. When set to `0`, the route returns 503 with the `X-Bifrost-Killswitch` header and the operator is bounced to the TS path. Use to disable the sidecar without redeploying (tier-1 router incident, key rotation). |
| `BIFROST_BASE_URL` | _(unset)_ | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | When set, the Bifrost sidecar proxy route forwards `/v1/chat/completions` traffic to this Go gateway instead of the TS relay handler. Unset → 503-with-fallback. Trailing slash is stripped. |
| `BIFROST_PORT` | `8080` | `src/lib/services/bootstrap.ts` | Port the supervised Bifrost embedded service binds to (`127.0.0.1:<port>`) when OmniRoute manages the Bifrost sidecar lifecycle. Defaults to `8080`. |
| `LLMLINGUA_PORT` | `20135` | `src/lib/services/bootstrap.ts` | Port the supervised LLMLingua compression service binds to (`127.0.0.1:<port>`). Defaults to `20135`. |
| `LLMLINGUA_BASE_URL` | `http://127.0.0.1:20135` | `open-sse/services/compression/engines/llmlingua/index.ts` | Base URL for the local LLMLingua HTTP compression microservice daemon. Defaults to `http://127.0.0.1:20135`. |
| `LLMLINGUA_MODEL` | `atjsh/llmlingua-2-js-tinybert-meetingbank` | `src/lib/services/installers/llmlingua.ts` | Hugging Face model id the supervised LLMLingua sidecar loads (passed to the child process env). |
| `BIFROST_API_KEY` | _(unset)_ | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | API key for the Bifrost gateway (sent as `Authorization: Bearer ...`). If unset, the route expects the request to carry a valid OmniRoute API key; this key is for gateway-side auth only. |
| `BIFROST_STREAMING_ENABLED` | `true` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | When true, the Bifrost sidecar route streams responses back via SSE through the gateway rather than the TS streaming executor. Set to `0` to force non-streaming JSON responses through the gateway. |
| `BIFROST_TIMEOUT_MS` | `30000` | `src/app/api/v1/relay/chat/completions/bifrost/route.ts` | Per-request timeout when proxying to the Bifrost gateway (ms). On timeout the route returns the TS relay path via the `X-Bifrost-Fallback` header. |
Expand Down
Loading
Loading