Design baseline · 24 SEP 2026 · 10 / 23
TDD · 9Router & model connection
Current behavior · Local admin UI, provider connections and production model proxy.
http://127.0.0.1:20128; Quarkus runtime gọi model qua gateway proxy /internal/v1/models/chat-completions với ROUTER_API_KEY. Tài khoản admin/cookie của local 9Router không phải API key dùng bởi production model proxy.Phạm vi chức năng hiện có
- Tab AI & 9Router đọc health, provider connections; có thể thêm API provider, xóa connection, authorize/exchange OAuth, lấy device code/poll OAuth, tải model catalog, lọc danh sách, xem usage và gửi test prompt.
- Chọn model trong catalog chỉ điền model ID vào form settings của Quarkus; người dùng còn phải lưu settings để áp dụng.
- Inference nghiệp vụ ở Quarkus nhận ModelProviderPort và gọi internal gateway. Gateway mới gọi upstream 9Router OpenAI-compatible endpoint.
/api/nine-router/*Preconditions
- Dashboard `/api` routes cần user session hợp lệ. Embedded 9Router mặc định ở loopback port 20128, có thể override bằng
NINEROUTER_URL; password lấy từNINEROUTER_PASSWORD, vẫn có fallback hiện tại trong source. - Để quản lý providers/models/OAuth/usage, service đăng nhập vào embedded 9Router qua
/api/auth/loginvà dùng cookie phiên từ đó. Kết nối provider bên trong 9Router cần credential/OAuth mà provider yêu cầu. - Production inference cần gateway có
ROUTER_API_KEY; tùy chỉnh upstream bằngROUTER_BASE_URL. Thiếu key thì gateway trả provider not configured (503); core/gateway cần chung internal token. - Model catalog listing không khẳng định connection có credential hợp lệ, model có thể gọi được, hoặc model nhận được image input. Chỉ completion request thực tế mới kiểm tra endpoint/model/quyền tại thời điểm đó.
Admin API qua Dashboard
| Endpoint | Hành vi | Failure behavior |
|---|---|---|
GET /api/nine-router/status | Health probe local /api/health, timeout 3 giây; trả online/latency/port/gatewayUrl và version string hardcoded. | Offline/error → online=false; health không test credential/provider/model. |
GET /api/nine-router/connections | Lấy /api/providers, trả {connections}. | Service lỗi → 500 + connections rỗng. |
POST /api/nine-router/connections | Forward body provider/name/apiKey/defaultModel/priority tới local API; trả success/result. | Service/provider validation lỗi → 500; route hiện không có schema validation riêng. |
DELETE /api/nine-router/connections/:id | Forward ID encoded và xóa connection trong 9Router. | Lỗi → 500. |
GET /api/nine-router/models?search=&provider= | Lấy toàn bộ models rồi lọc provider/name/model/alias ở Express; trả {models}. | Lỗi → 500 + models rỗng. Catalog chưa phải probe inference. |
GET /api/nine-router/usage | Forward usage stats của embedded router. | Lỗi → 500. |
POST /api/nine-router/test-chat | Body yêu cầu prompt; model mặc định gpt-4o-mini; trả text/model/elapsedMs/usage + success. | Prompt rỗng → 400; upstream error → 500. |
GET /api/nine-router/oauth/:provider/authorize | Lấy OAuth auth URL; redirect mặc định tùy provider (Codex khác provider còn lại). | Lỗi → 500. |
POST /api/nine-router/oauth/:provider/exchange | Đổi authorization code/state/verifier thành connection. | Lỗi → 500. |
GET /api/nine-router/oauth/:provider/device-code / POST …/poll | Bắt đầu/poll OAuth device flow. | Lỗi → 500. |
Request flow và error mapping
Các admin methods, gồm test-chat, dùng helper request: nếu chưa có cookie, POST password để lấy set-cookie; sau đó gửi cookie tới local API. Khi response 401, xóa cookie cục bộ, đăng nhập lại một lần rồi retry request một lần. Response non-2xx trở thành exception hiển thị qua Express 500. Status probe không đăng nhập.
Production path: Quarkus RestModelProviderAdapter gọi ModelGatewayClient với JSON-compatible chat completion và internal token. Gateway kiểm tra token và body object, rồi RouterAdapter POST {ROUTER_BASE_URL}/chat/completions với Bearer key, timeout 60 giây. Upstream non-2xx hoặc lỗi parse/network được gateway log, client nhận 502 model_provider_failed; key không trả ra response.
| Boundary | Success | Error / limitation |
|---|---|---|
| Browser → Express admin API | User cookie, JSON; UI cập nhật cards/list/toast. | Unauthenticated /api bị 401; nhưng provider/OAuth input validation phần lớn delegated cho 9Router. |
| Express service → embedded 9Router admin | Cookie-authenticated /api/providers, /api/models, /api/usage/stats, /api/oauth/*. | Network/timeout/HTTP error thành error message; helper request không có timeout riêng. |
| Quarkus → TS gateway model proxy | Internal token + chat-completion JSON. | Sai token 401; thiếu router/key 503; body không object 400; upstream/model error 502. |
| TS gateway → upstream 9Router | Bearer ROUTER_API_KEY, JSON; timeout 60s; trả JSON completion. | Non-2xx error nội bộ, gateway log và response tổng quát 502. Response 2xx rỗng thành {}. |
testChat() nay gọi cùng helper request() như API admin, gửi cookie 9Router từ server-side login và thử login/retry một lần sau 401. Có unit test giả lập cookie hết hạn; kết quả test suite được ghi sau khi chạy.Current TDD scenarios
| ID | Scenario / expected observable behavior | Evidence status |
|---|---|---|
| NR-01 | Không có embedded router: status online=false; UI hiển thị gián đoạn. | Có source behavior; thiếu unit test service cho status/network timeout. |
| NR-02 | Local router mở: health probe trả online=true và latency; không suy ra model available. | Có source behavior; test health boundary đề xuất. |
| NR-03 | Admin/completion API call không có cookie tự login; 401 sẽ login lại và retry đúng một lần. | Có test source: backend/dashboard-bff/tests/nineRouterService.test.ts cho test-chat cookie + re-login. |
| NR-04 | Sai password/local auth: error được trả từ route; không leak cookie. | Có source behavior; dedicated redaction/secret test đề xuất. |
| NR-05 | List connections/models/usage thành công phản chiếu response router; search/provider filter chỉ lọc catalog. | Có source behavior; test UI/API coverage cần bổ sung nếu contract quan trọng. |
| NR-06 | Add/delete provider và OAuth authorize/exchange/device-code/poll forward đúng provider + payload. | Có source behavior; không có integration test chuyên biệt được thấy. |
| NR-07 | Test-chat thiếu prompt trả 400; completion hợp lệ gửi cookie và trả first choice content/model/usage. | Có test source cho service cookie; route 400 được source xác nhận. |
| NR-08 | Production request thiếu ROUTER_API_KEY → 503 model_provider_not_configured. | Có test không được tìm thấy; behavior nằm tại server startup/wiring. |
| NR-09 | Production request invalid JSON shape → 400; internal token thiếu/sai → 401. | Gateway contract có test auth patterns; test route model cần bổ sung. |
| NR-10 | Upstream 9Router non-2xx/timeout/invalid JSON → client thấy 502 tổng quát, không thấy key. | Source-derived; cần adapter test bằng fetch mock. |
| NR-11 | Provider/model không có credential/quyền hoặc vision capability sai → completion lỗi thay vì catalog label làm bằng chứng. | No deterministic test fixture/provider sandbox contract; runtime acceptance cần model probe. |
| NR-12 | Chọn model trong catalog chỉ điền form; chỉ sau save settings mới là model cấu hình core. | Có UI source và dashboard test pattern; không chứng minh request live thành công. |
Kết quả test và bước xác nhận
| Bộ kiểm tra | Kết quả | Phạm vi được chứng minh |
|---|---|---|
bun run app:typecheck | PASS | TypeScript cho service và Dashboard. |
bun run app:test | PASS · 40/40 | Có dedicated service test cho test-chat cookie và retry sau 401; không bao phủ production inference. |
Service test chạy với mock fetch. Chưa có inference acceptance trên embedded 9Router đang chạy hoặc provider credential thật.
Test design đề xuất cho lần sửa kế tiếp
- Mock local router: assert login request body, cookie extraction, cookie attached to admin API, một lần re-login sau 401, không có retry vô hạn.
- Mock `/v1/chat/completions`: xác nhận Authorization contract; nếu endpoint dùng API key, test key/cookie được gửi từ server-side secret source và UI không thể trả credential.
- Test Express route auth (401), prompt validation (400), status timeout/offline, provider CRUD error mapping và OAuth payload forwarding.
- Test gateway model proxy: auth, no provider, malformed request, upstream 4xx/5xx, timeout, response pass-through, secret redaction.
- Integration acceptance có credential thật cần test riêng theo model/provider, text và image riêng, schema/usage và timeout; catalog
caps.visionchỉ là metadata.
Khoảng trống và không được suy diễn
- 9Router password vẫn có hardcoded fallback; base URL có thể override bằng
NINEROUTER_URL; displayed port/version còn là metadata cố định trongNineRouterService. - Test chat đã cùng cơ chế cookie với API admin, nhưng không phải production inference path của Quarkus. Kết quả test-chat không thay thế bài kiểm tra production proxy.
- Routes add/delete connection, test-chat và OAuth exchange/poll yêu cầu app login nhưng không có role check admin riêng. Chính sách hiện tại cho phép toàn bộ tài khoản đăng nhập quản lý provider dùng chung; đây không phải cấu hình provider theo tenant.
- Tab này cho phép thêm nhiều provider loại API key/OAuth, nhưng model runtime chỉ forward tới upstream đã cấu hình trong gateway. Thêm provider vào admin catalog không tự thay
ROUTER_API_KEYhay tạo credential cho Quarkus. - Embedded router dùng một local admin account/global catalog; provider inventory không được scope theo tenant.
- OAuth UI nhận code qua popup/BroadcastChannel/localStorage fallback; cần security review origin/state/PKCE persistence và cleanup trên browser trước khi mở rộng.
- Có dedicated service test cho cookie/re-auth. Các route app và production gateway chưa được covered end-to-end; test pass status được ghi sau lượt chạy.
Source of truth để bảo trì
backend/dashboard-bff/services/nineRouterService.ts; provider/admin routes trong backend/dashboard-bff/server.ts; UI controller frontend/current/app.js và markup frontend/current/index.html; production adapter gateway/adapters/router.ts, gateway/server.ts; Quarkus ModelGatewayClient.java, RestModelProviderAdapter.java, RestVisionProviderAdapter.java; gateway/README.md. Related tests gồm backend/dashboard-bff/tests/nineRouterService.test.ts (cookie + re-login), backend/dashboard-bff/tests/quarkusDashboard.test.ts (UI/model selection shape), và gateway/tests/gateway.test.ts (gateway auth/HTTP boundary). Các Express routes, gateway production proxy, và live provider inference chưa được kiểm thử end-to-end.