# OmniRoute Auto-Combo Engine (Tiếng Việt) 🌐 **Languages:** 🇺🇸 [English](../../../../routing/AUTO-COMBO.md) · 🇪🇹 [am](../../../am/docs/routing/AUTO-COMBO.md) · 🇸🇦 [ar](../../../ar/docs/routing/AUTO-COMBO.md) · 🇦🇿 [az](../../../az/docs/routing/AUTO-COMBO.md) · 🇧🇬 [bg](../../../bg/docs/routing/AUTO-COMBO.md) · 🇧🇩 [bn](../../../bn/docs/routing/AUTO-COMBO.md) · 🇧🇦 [bs](../../../bs/docs/routing/AUTO-COMBO.md) · 🇨🇿 [cs](../../../cs/docs/routing/AUTO-COMBO.md) · 🇩🇰 [da](../../../da/docs/routing/AUTO-COMBO.md) · 🇩🇪 [de](../../../de/docs/routing/AUTO-COMBO.md) · 🇬🇷 [el](../../../el/docs/routing/AUTO-COMBO.md) · 🇪🇸 [es](../../../es/docs/routing/AUTO-COMBO.md) · 🇪🇪 [et](../../../et/docs/routing/AUTO-COMBO.md) · 🇮🇷 [fa](../../../fa/docs/routing/AUTO-COMBO.md) · 🇫🇮 [fi](../../../fi/docs/routing/AUTO-COMBO.md) · 🇫🇷 [fr](../../../fr/docs/routing/AUTO-COMBO.md) · 🇮🇪 [ga](../../../ga/docs/routing/AUTO-COMBO.md) · 🇮🇳 [gu](../../../gu/docs/routing/AUTO-COMBO.md) · 🇳🇬 [ha](../../../ha/docs/routing/AUTO-COMBO.md) · 🇮🇱 [he](../../../he/docs/routing/AUTO-COMBO.md) · 🇮🇳 [hi](../../../hi/docs/routing/AUTO-COMBO.md) · 🇭🇷 [hr](../../../hr/docs/routing/AUTO-COMBO.md) · 🇭🇺 [hu](../../../hu/docs/routing/AUTO-COMBO.md) · 🇦🇲 [hy](../../../hy/docs/routing/AUTO-COMBO.md) · 🇮🇩 [id](../../../id/docs/routing/AUTO-COMBO.md) · 🇳🇬 [ig](../../../ig/docs/routing/AUTO-COMBO.md) · 🇮🇹 [it](../../../it/docs/routing/AUTO-COMBO.md) · 🇯🇵 [ja](../../../ja/docs/routing/AUTO-COMBO.md) · 🇬🇪 [ka](../../../ka/docs/routing/AUTO-COMBO.md) · 🇰🇭 [km](../../../km/docs/routing/AUTO-COMBO.md) · 🇮🇳 [kn](../../../kn/docs/routing/AUTO-COMBO.md) · 🇰🇷 [ko](../../../ko/docs/routing/AUTO-COMBO.md) · 🇱🇹 [lt](../../../lt/docs/routing/AUTO-COMBO.md) · 🇱🇻 [lv](../../../lv/docs/routing/AUTO-COMBO.md) · 🇮🇳 [ml](../../../ml/docs/routing/AUTO-COMBO.md) · 🇮🇳 [mr](../../../mr/docs/routing/AUTO-COMBO.md) · 🇲🇾 [ms](../../../ms/docs/routing/AUTO-COMBO.md) · 🇲🇹 [mt](../../../mt/docs/routing/AUTO-COMBO.md) · 🇲🇲 [my](../../../my/docs/routing/AUTO-COMBO.md) · 🇳🇵 [ne](../../../ne/docs/routing/AUTO-COMBO.md) · 🇳🇱 [nl](../../../nl/docs/routing/AUTO-COMBO.md) · 🇳🇴 [no](../../../no/docs/routing/AUTO-COMBO.md) · 🇮🇳 [or](../../../or/docs/routing/AUTO-COMBO.md) · 🇮🇳 [pa](../../../pa/docs/routing/AUTO-COMBO.md) · 🇵🇭 [phi](../../../phi/docs/routing/AUTO-COMBO.md) · 🇵🇱 [pl](../../../pl/docs/routing/AUTO-COMBO.md) · 🇵🇹 [pt](../../../pt/docs/routing/AUTO-COMBO.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/routing/AUTO-COMBO.md) · 🇷🇴 [ro](../../../ro/docs/routing/AUTO-COMBO.md) · 🇷🇺 [ru](../../../ru/docs/routing/AUTO-COMBO.md) · 🇱🇰 [si](../../../si/docs/routing/AUTO-COMBO.md) · 🇸🇰 [sk](../../../sk/docs/routing/AUTO-COMBO.md) · 🇸🇮 [sl](../../../sl/docs/routing/AUTO-COMBO.md) · 🇷🇸 [sr](../../../sr/docs/routing/AUTO-COMBO.md) · 🇸🇪 [sv](../../../sv/docs/routing/AUTO-COMBO.md) · 🇰🇪 [sw](../../../sw/docs/routing/AUTO-COMBO.md) · 🇮🇳 [ta](../../../ta/docs/routing/AUTO-COMBO.md) · 🇮🇳 [te](../../../te/docs/routing/AUTO-COMBO.md) · 🇹🇭 [th](../../../th/docs/routing/AUTO-COMBO.md) · 🇹🇷 [tr](../../../tr/docs/routing/AUTO-COMBO.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/routing/AUTO-COMBO.md) · 🇵🇰 [ur](../../../ur/docs/routing/AUTO-COMBO.md) · 🇺🇿 [uz](../../../uz/docs/routing/AUTO-COMBO.md) · 🇳🇬 [yo](../../../yo/docs/routing/AUTO-COMBO.md) · 🇨🇳 [zh-CN](../../../zh-CN/docs/routing/AUTO-COMBO.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/routing/AUTO-COMBO.md) --- > **Dành cho người dùng**: Bạn đang tìm cách bắt đầu nhanh? Xem [Hướng dẫn sử dụng Auto-Combo](../getting-started/AUTO-COMBO-GUIDE.md) để biết các giải thích và ví dụ đơn giản. > Chuỗi mô hình tự quản lý với khả năng chấm điểm thích ứng + tự động định tuyến không cần cấu hình ## Tự động định tuyến không cần cấu hình (tiền tố `auto/`) > **MỚI:** Không cần tạo combo. Sử dụng trực tiếp tiền tố `auto/` trong bất kỳ client nào. ### Ví dụ nhanh | Model ID | Biến thể | Hành vi | | -------------- | -------- | ------------------------------------------------------------------------ | | `auto` | mặc định | Tất cả nhà cung cấp đã kết nối, chiến lược LKGP, trọng số cân bằng | | `auto/coding` | coding | Trọng số ưu tiên chất lượng, phù hợp để sinh mã | | `auto/fast` | fast | Lựa chọn có trọng số với độ trễ thấp | | `auto/cheap` | cheap | Định tuyến tối ưu chi phí (chi phí thấp nhất trước) | | `auto/offline` | offline | Ưu tiên các nhà cung cấp có hạn mức khả dụng cao nhất | | `auto/smart` | smart | Ưu tiên chất lượng + tỷ lệ khám phá cao hơn (10%) để tìm mô hình tốt hơn | | `auto/lkgp` | lkgp | LKGP tường minh (giống `auto` mặc định) | | `auto/chaos` | chaos | Phân nhánh song song, mỗi nhà cung cấp một mô hình (không phải chèn lỗi) | ### Kết hợp danh mục × phân hạng (`auto/:`) Các hậu tố theo phong cách OpenRouter tách biệt **loại định tuyến** (danh mục) với **cách tối ưu hóa** (phân hạng), nhờ đó bạn có thể tự do kết hợp chúng (#4235 Giai đoạn B, `open-sse/services/autoCombo/suffixComposition.ts`): - **Danh mục** (lọc nhóm ứng viên theo khả năng): `coding` · `reasoning` · `vision` · `chat` · `multimodal`. `vision`/`multimodal` giữ lại các mô hình có khả năng xử lý hình ảnh; `reasoning` giữ lại các mô hình suy luận/tư duy. - **Phân hạng** (chọn trọng số chấm điểm / bộ lọc nhóm): `fast` (triển khai nhanh) · `cheap` (bí danh `floor`, tiết kiệm chi phí) · `reliable` (tình trạng circuit breaker + độ ổn định của độ trễ) · `free` / `pro` (lọc nhóm theo phân hạng mô hình thông qua `classifyTier` — hạng miễn phí so với cao cấp). | Ví dụ | Phân giải thành | | ---------------------- | -------------------------------------------------------------------------- | | `auto/coding:fast` | nhóm coding, trọng số có độ trễ thấp | | `auto/coding:cheap` | nhóm coding, tối ưu chi phí (bí danh `auto/coding:floor`) | | `auto/reasoning:pro` | chỉ các mô hình suy luận/tư duy, phân hạng cao cấp | | `auto/vision` | các mô hình có khả năng xử lý hình ảnh (không có hạng → trọng số cân bằng) | | `auto/multimodal:free` | các mô hình có khả năng đa phương thức, chỉ phân hạng miễn phí | Mọi `auto/[:]` hợp lệ đều được phân giải theo yêu cầu; một tập con được tuyển chọn sẽ được công bố trong `/v1/models` và bảng điều khiển (`AUTO_SUFFIX_VARIANTS` trong `open-sse/services/autoCombo/builtinCatalog.ts`). Việc lọc là **fail-open** — nếu không có mô hình đã kết nối nào khớp với ràng buộc, toàn bộ nhóm sẽ được sử dụng để việc định tuyến không bao giờ bị gián đoạn. Bộ chấm điểm cốt lõi (`combo.ts`) không thay đổi; bộ lọc danh mục/phân hạng được áp dụng trong `buildAutoCandidates`. > **Thông tin mô hình trực tiếp:** mức độ phù hợp của tự động định tuyến được xác định dựa trên bảng xếp hạng **Arena ELO** trực tiếp + dữ liệu phân hạng từ **models.dev** khi cờ `ARENA_ELO_SYNC_ENABLED` được bật (nếu không sẽ quay về bản đồ mức độ phù hợp tĩnh). **Cách sử dụng:** ```bash # Bất kỳ IDE hoặc công cụ CLI nào hỗ trợ định dạng OpenAI Base URL: http://localhost:20128/v1 API Key: # Trong mã/cấu hình của bạn, đặt model thành: model: "auto" # mặc định cân bằng model: "auto/coding" # tốt nhất cho các tác vụ lập trình model: "auto/fast" # nhanh nhất hiện có model: "auto/cheap" # rẻ nhất trên mỗi token ``` **Điều gì xảy ra:** 1. OmniRoute phát hiện tiền tố `auto/` trong `src/sse/handlers/chat.ts` 2. Truy vấn tất cả **kết nối nhà cung cấp đang hoạt động** từ cơ sở dữ liệu 3. Lọc ra các kết nối có thông tin xác thực hợp lệ (API key hoặc OAuth token) 4. Xác định mô hình cho mỗi kết nối (`connection.defaultModel` hoặc mô hình đầu tiên của nhà cung cấp) 5. Tạo một **combo ảo** trong bộ nhớ (không được lưu trong DB) 6. Định tuyến bằng hồ sơ trọng số của biến thể đã chọn + chiến lược LKGP **Các thuộc tính chính:** - ✅ **Luôn bật:** Không cần nút chuyển, không cần tạo combo, không cần cấu hình - ✅ **Động:** Tự động phản ánh các nhà cung cấp hiện đang được kết nối - ✅ **Duy trì phiên:** LKGP đảm bảo nhà cung cấp thành công gần nhất được ưu tiên - ✅ **Hỗ trợ nhiều tài khoản:** Mỗi kết nối nhà cung cấp trở thành một ứng viên riêng biệt - ✅ **Không ghi vào DB:** Combo ảo chỉ tồn tại trong phạm vi yêu cầu, không phát sinh chi phí lưu trữ lâu dài ### Kiểm soát ứng viên theo từng khóa (#7819, Cấp 1+2) `GET /v1/auto-combo/{channel}/candidates` (`{channel}` = hậu tố sau `auto/`, hoặc giá trị nguyên văn `auto` cho kênh cơ sở) là một endpoint **chỉ đọc**, liệt kê nhóm ứng viên hiện tại của một kênh `auto/*` kèm theo trạng thái khả dụng trực tiếp, bằng cách tái sử dụng các thao tác đọc khả năng phục hồi hiện có (không bao giờ đọc trực tiếp `state` của breaker): - circuit breaker của nhà cung cấp — `getCircuitBreaker(provider).getStatus()` / `.canExecute()` - thời gian chờ của kết nối — `rateLimitedUntil` / `testStatus` trên hàng `provider_connections` đã được phân giải - khóa mô hình — `isModelLocked(provider, connectionId, model)` Mỗi ứng viên cũng mang cờ `excluded` dành cho API key này. Các trường hợp loại trừ được lưu theo từng API key (bảng `auto_candidate_overrides`, migration `128`) — OmniRoute là hệ thống đơn đối tượng thuê và không có bảng `users`, vì vậy `apiKeyId` là danh tính thực tế gần nhất theo từng bên gọi — đồng thời được thực thi tại điểm kiểm soát nhóm ứng viên trong `open-sse/services/autoCombo/virtualFactory.ts` thông qua `filterExcludedCandidates()` thuần túy, đã được kiểm thử đơn vị (`open-sse/services/autoCombo/candidateOverrides.ts`). Bộ lọc này là **fail-open**: `apiKeyId`/kênh chưa được đặt hoặc lỗi tra cứu DB đều khiến nhóm không bị lọc, vì vậy một người vận hành chưa cấu hình bất kỳ ghi đè nào sẽ thấy hoạt động định tuyến giống hệt từng byte so với trước khi có tính năng này. **Được hoãn sang một issue tiếp theo:** trọng số theo từng ứng viên + thứ tự rõ ràng (Cấp 3 — được đưa vào các luồng chiến lược trọng số/ưu tiên hiện có) và ghim một chiến lược `combo.ts` cụ thể cho từng kênh `auto/*` (Cấp 4). Xem kế hoạch #7819 để biết câu hỏi chưa được giải quyết về việc các giá trị ghi đè nên duy trì theo từng khóa API hay trở thành toàn cục do mô hình single-tenant. **Cơ chế hoạt động phía sau:** ```txt Yêu cầu: { model: "auto/coding" } ↓ src/sse/handlers/chat.ts phát hiện tiền tố ↓ createVirtualAutoCombo('coding') → candidatePool từ các kết nối đang hoạt động ↓ handleComboChat (cùng engine với các combo được lưu trữ) ↓ Tính điểm tự động chọn nhà cung cấp/mô hình tốt nhất cho mỗi yêu cầu ``` **Các tệp triển khai:** | Tệp | Mục đích | | --------------------------------------------------------- | ----------------------------------------------- | | `open-sse/services/autoCombo/autoPrefix.ts` | Trình phân tích tiền tố (`parseAutoPrefix`) | | `open-sse/services/autoCombo/virtualFactory.ts` | Tạo các đối tượng `AutoComboConfig` ảo | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Hook kiểm thử để mô phỏng registry nhà cung cấp | | `src/sse/handlers/chat.ts` | Tích hợp: xử lý tắt sớm đối với tiền tố tự động | | `src/shared/constants/providers.ts` | Mục hệ thống `SYSTEM_PROVIDERS.auto` | ## Tùy chọn tài khoản cho các combo tự động đã lưu Trình chỉnh sửa Tự động thông minh cung cấp một tùy chọn** Tùy chọn tài khoản** phần. Mỗi kết nối có thể là Ưu tiên, Bình thường (mặc định), Dự trữ hoặc Bị loại trừ. Nhiều kết nối có thể chia sẻ một tầng. Tự động chọn trong cấp đủ điều kiện đầu tiên, duy trì hành vi scoring/router ở đó; những lần thử thất bại sẽ chuyển sang các tài khoản cụ thể còn lại, được ưu tiên trước bình thường trước khi dự trữ. Các kết nối bị loại trừ không bao giờ được sử dụng bởi combo này. Cài đặt được lưu dưới dạng `config.accountPreferences`, được khóa bằng kết nối ID: ```json { "accountPreferences": { "connection-a": "preferred", "connection-b": "preferred", "connection-c": "reserve", "connection-d": "excluded" } } ``` Bản đồ bị bỏ qua hoặc trống sẽ giữ lại định tuyến hiện có. Tùy chọn không bật các kết nối bị vô hiệu hóa, cấp quyền truy cập khóa API, khôi phục các ứng viên bị chặn hạn ngạch hoặc bỏ qua việc kiểm tra ngân sách. Mỗi lần thử bị giới hạn ở kết nối cụ thể, do đó, dự phòng tài khoản không thể nhảy các cấp phía sau bộ định tuyến kết hợp. Mối quan hệ phiên hiện tại có thể giữ lại một tài khoản đủ điều kiện; loại trừ tài khoản bị ràng buộc với lượt gốc sẽ dừng lượt đó thay vì âm thầm thay đổi tài khoản. Tùy chọn được định cấu hình sử dụng Đường dẫn tự động nhận biết tài khoản: bộ đệm ngữ cảnh không liên kết, đường dẫn thông minh, hỗn loạn và các phím tắt bóng không được gửi đi. Phòng ngừa rủi ro đầu cơ bị vô hiệu hóa đối với những yêu cầu này nên các tài khoản dự trữ không thể chạy đua với những yêu cầu ưu tiên. Các chiến lược kết hợp khác không giải thích được cài đặt này. Việc thay đổi kết hợp khỏi Tự động sẽ giữ lại các tùy chọn đã lưu để chuyển trở lại sau này. ## Tên combo trùng với ID mô hình thực Một combo có `name` giống hệt một ID mô hình thuần túy (ví dụ: combo có tên `gpt-5.5`) là một **mẫu sử dụng có chủ đích và được hỗ trợ**, không phải lỗi: đây là cơ chế chuyển đổi dự phòng nhà cung cấp theo từng ID mô hình được ghi lại trong [#6940](https://github.com/diegosouzapw/OmniRoute/issues/6940). Vì việc phân giải combo được kiểm tra trước khi phân giải ID mô hình thuần túy (`getComboForModel()` trong `src/sse/services/model.ts`), yêu cầu dành cho ID thuần túy `gpt-5.5` sẽ được định tuyến qua các đích của combo (ví dụ: `acme-responses/gpt-5.5`, `backup-responses/gpt-5.5`) thay vì đi thẳng đến một nhà cung cấp duy nhất — cơ chế này tái sử dụng thứ tự ưu tiên combo-trước-khi-ghi-lại được xây dựng cho [#3227/#3233](https://github.com/diegosouzapw/OmniRoute/issues/3227) và được kiểm thử hồi quy bởi `tests/unit/responses-combo-resolution-3227.test.ts` và `tests/unit/combo-name-codex-responses-rewrite.test.ts`. Việc tạo hoặc đổi tên một combo thành tên che khuất một ID mô hình thực **không bao giờ bị từ chối** — làm như vậy sẽ phá vỡ quy trình làm việc đã được ghi lại này. Thay vào đó (#8530), `POST /api/combos` và `PUT /api/combos/[id]` đính kèm một trường `warning` không chặn vào phản hồi khi tên (mới) xung đột với một ID mô hình thực: ```json { "warning": { "code": "COMBO_NAME_SHADOWS_MODEL", "modelId": "gpt-5.5", "providerId": "openai" } } ``` Khi khởi động, `scanComboModelNameCollisionsAtBoot()` (`src/instrumentation-node.ts`) cũng ghi nhật ký một cảnh báo `[STARTUP]` trên một dòng, liệt kê mọi combo hiện có che khuất một ID mô hình, để những người vận hành vô tình gặp phải tình huống này (thay vì cố ý, theo #6940) có thể nhận biết. Hàm trợ giúp phát hiện nằm trong `src/lib/combos/modelNameCollision.ts`. ## Gọi một combo tùy chỉnh từ máy khách Các combo đã lưu (Settings → Combos) chỉ được sử dụng khi máy khách gửi **tên chính xác** của combo trong trường `model` — không có cơ chế so khớp gần đúng hoặc một phần đối với tên combo, và cũng không liên quan đến tiền tố `auto/`. Thứ tự phân giải (`getComboForModel()` trong `src/sse/services/model.ts`): 1. so khớp chính xác tên combo (`model: "my-combo"`), 2. tiền tố `combo/` (`model: "combo/my-combo"`), 3. ánh xạ glob từ mô hình sang combo (`/api/model-combo-mappings`). ```bash curl -X POST http://localhost:20128/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"my-combo","messages":[{"role":"user","content":"Hello"}]}' ``` Hai lỗi thường gặp: - **`auto` không sử dụng các combo của bạn.** `auto`/`auto/*` tự xây dựng nhóm ứng viên không cần cấu hình và chỉ tham chiếu các combo đã lưu nếu có một combo thực sự mang tên `auto` (không khuyến nghị). Để định tuyến qua một combo, hãy gửi chính xác tên của combo đó — không phải `auto`. - **`openrouter/auto` là một sản phẩm OpenRouter trả phí thực sự** ("Tự động chọn tùy chọn tốt nhất hiện có"), không phải bí danh OmniRoute. Đây là mục mô hình tĩnh duy nhất trong registry OpenRouter (`open-sse/config/providers/registry/openrouter/index.ts`) và được tính phí riêng. Sử dụng Settings → Routing → Hide paid models để loại trừ nó khỏi các nhóm `auto`. Xem [#7992](https://github.com/diegosouzapw/OmniRoute/issues/7992) và [#7111](https://github.com/diegosouzapw/OmniRoute/issues/7111) để biết sự nhầm lẫn ban đầu mà tài liệu này đề cập. ## Cách thức hoạt động (Auto-Combo được lưu bền vững) Auto-Combo Engine tự động chọn nhà cung cấp/mô hình tốt nhất cho mỗi yêu cầu bằng **hàm tính điểm gồm 16 yếu tố** (được định nghĩa trong `open-sse/services/autoCombo/scoring.ts` → `DEFAULT_WEIGHTS`). Tổng các trọng số mặc định bằng `1.0`; các trọng số tùy chỉnh được chuẩn hóa lại bằng `normalizeScoringWeights()`. Hai trong số mười sáu yếu tố — `cacheAffinity` và `resetWindowAffinity` — có trọng số mặc định là `0`; `reliability` có trọng số `0` trong `DEFAULT_WEIGHTS` nhưng là `0.03` trong các gói chung và `0.04` trong `reliability-first`, còn `quality` có trọng số `0.02` trong các gói (`0.03` trong `quality-first`): chúng vẫn được tính toán cho mọi ứng viên, và `cacheAffinity` kiểm soát việc loại bỏ trùng lặp bộ nhớ đệm prompt bên ngoài điểm số, vì vậy các yếu tố có trọng số mặc định bằng 0 đơn giản là không tham gia biểu quyết theo mặc định, trong khi các gói thì có. ![Cách tính điểm 16 yếu tố của Auto-Combo](../diagrams/exported/auto-combo-scoring.svg) > Nguồn: [diagrams/auto-combo-scoring.mmd](../diagrams/auto-combo-scoring.mmd) (tạo lại bằng `npm run docs:render-diagrams`). Tên tệp mang tính lịch sử; nguồn và sơ đồ được kết xuất hiển thị đầy đủ cả 16 yếu tố được khai báo trong `DEFAULT_WEIGHTS`. | Yếu tố | Trọng số mặc định | Mô tả | | :-------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `quota` | 0.1429 | Hạn mức còn lại / dư địa giới hạn tốc độ [0..1] | | `health` | 0.1605 | Điểm trạng thái từ bộ ngắt mạch (CLOSED=1.0, HALF_OPEN=0.5, OPEN=0.0) | | `costInv` | 0.1429 | Chi phí **kết hợp** nghịch đảo (60% giá token đầu vào + 40% giá token đầu ra, đã chuẩn hóa) — rẻ hơn = điểm cao hơn | | `latencyInv` | 0.1143 | Độ trễ p95 nghịch đảo được chuẩn hóa theo nhóm — nhanh hơn = điểm cao hơn | | `taskFit` | 0.0762 | Mức độ phù hợp với loại tác vụ (lập trình, đánh giá, lập kế hoạch, phân tích, gỡ lỗi, tài liệu) | | `stability` | 0.0476 | Độ ổn định dựa trên phương sai từ độ lệch chuẩn của độ trễ — ứng viên có thời gian phản hồi dao động sẽ nhận điểm thấp hơn | | `tierPriority` | 0.0476 | Mức độ ưu tiên của hạng tài khoản — Ultra=1.0, Pro=0.67, Standard=0.33, Free=0.0 | | `tierAffinity` | 0.0476 | Mức độ tương thích giữa hạng của ứng viên và hạng được manifest đề xuất | | `specificityMatch` | 0.0476 | Mức độ khớp giữa tính đặc thù của yêu cầu (gợi ý từ manifest) và hạng mô hình | | `contextAffinity` | 0.0476 | Mức độ tương thích giữa nhu cầu về cửa sổ ngữ cảnh của yêu cầu và cửa sổ ngữ cảnh của mô hình | | `sessionAvailability` | 0.0476 | Mức độ sẵn sàng của phiên OAuth thuộc kết nối ứng viên đối với phiên này (`getOAuthSessionAvailability()`; các kết nối không dùng OAuth nhận điểm 1.0) | | `connectionDensity` | 0.0476 | Phân bổ tải trên các kết nối của cùng một nhà cung cấp (chống tập trung) | | `cacheAffinity` | 0.00 | Mức độ tương thích theo rendezvous hash hướng đến kết nối có nhiều khả năng đã chứa tiền tố bộ nhớ đệm prompt của yêu cầu này nhất (`open-sse/services/combo/promptCacheAffinity.ts`); bị vô hiệu hóa theo mặc định (#8008) | | `resetWindowAffinity` | 0.00 | Thiên về các kết nối có khoảng thời gian đặt lại hạn mức thuận lợi (bị vô hiệu hóa theo mặc định) | | `quality` | 0.03 | Tín hiệu chất lượng đầu ra dựa trên phản hồi từ trình theo dõi chất lượng sự kiện định tuyến; các ứng viên không có dữ liệu quan sát nhận giá trị trung lập 0.5 | | `reliability` | 0.00 | Tỷ lệ thành công quan sát được, `1 - failureRate`, từ lịch sử sử dụng trong 24 giờ với ngưỡng tối thiểu mười mẫu (nếu không thì dùng số liệu thời gian thực); các ứng viên không có dữ liệu quan sát được coi là 1.0. Bị vô hiệu hóa theo mặc định | `quality` (`open-sse/services/routing/quality.ts`): `cold restart → ∅`; `observations = 0 → score 0.5`. **Tổng:** `0.1429 + 0.1605 + 0.1429 + 0.1143 + 0.0762 + (7 × 0.0476) + 0.00 + 0.00 + 0.03 + 0.00 = 1.0` như được khai báo trong `DEFAULT_WEIGHTS`; các trọng số do người dùng cấu hình được `normalizeScoringWeights()` chuẩn hóa lại thành một phân phối trước khi tính điểm. ## Các gói chế độ 6 cấu hình trọng số được định nghĩa sẵn trong `open-sse/services/autoCombo/modePacks.ts`. Mỗi gói thay thế hoàn toàn các trọng số mặc định để thiên lệch việc lựa chọn về một mục tiêu. Tổng trọng số của mỗi gói đã bằng `1.0` (`0.9999` khi hiển thị với bốn chữ số thập phân), vì vậy `normalizeScoringWeights()` không có gì đáng kể để hiệu chỉnh khi một gói đang hoạt động — xét đến việc làm tròn, các giá trị bên dưới chính là các giá trị mà bộ chấm điểm áp dụng. | Yếu tố | ship-fast | cost-saver | quality-first | offline-friendly | reliability-first | chaos-mode | | :-------------------- | :--------- | :--------- | :------------ | :--------------- | :---------------- | :--------- | | `quota` | 0.1133 | 0.1133 | 0.0752 | **0.3324** | 0.1133 | 0.0376 | | `health` | 0.2667 | 0.1810 | 0.1714 | 0.2667 | **0.3524** | **0.4000** | | `costInv` | 0.0276 | **0.3324** | 0.0276 | 0.0752 | 0.0181 | 0.0140 | | `latencyInv` | **0.3048** | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0186 | | `taskFit` | 0.0952 | 0.0952 | **0.3524** | 0.0000 | 0.0952 | 0.1905 | | `stability` | 0.0000 | 0.0476 | 0.1429 | 0.0952 | 0.1905 | 0.1714 | | `tierPriority` | 0.0376 | 0.0376 | 0.0276 | 0.0376 | 0.0276 | 0.0040 | | `tierAffinity` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `specificityMatch` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `contextAffinity` | 0.0095 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0186 | | `sessionAvailability` | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | | `resetWindowAffinity` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `connectionDensity` | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | | `quality` | 0.02 | 0.02 | **0.03** | 0.02 | 0.02 | 0.02 | | `reliability` | 0.03 | 0.03 | 0.03 | 0.03 | **0.04** | 0.03 | Ghi chú: - **Các gói chứa `quality` và `reliability`** (`quality 0.02`, `quality-first 0.03`; `reliability 0.03`, `reliability-first 0.04`) và thay thế toàn bộ ánh xạ trọng số (`weights = pack`, không phải phép hợp nhất). `DEFAULT_WEIGHTS` chứa `quality 0.03 / reliability 0`; việc chọn `balanced`/`default` giữ nguyên các giá trị mặc định đó, còn việc chọn một gói sẽ sử dụng các giá trị của gói ở trên. Trên một pool lạnh (chưa có quan sát nào, do đó `quality 0.5` và `reliability 1`), hai yếu tố này cộng thêm `+0.04` với một gói thông thường (`0.03 + 0.01`), `+0.045` với `quality-first` và `+0.05` với `reliability-first`. - `tierAffinity`, `specificityMatch` và `resetWindowAffinity` được đặt rõ ràng thành `0` trong mọi gói. - Tổng quan nhanh về trọng tâm của từng gói: - **ship-fast** → latencyInv 0.3048 + health 0.2667 (các kết nối có độ trễ thấp và trạng thái tốt) - **cost-saver** → costInv 0.3324 (token rẻ nhất sẽ thắng) - **quality-first** → taskFit 0.3524 + stability 0.1429 + quality 0.03, cao nhất trong tất cả các gói (mô hình tốt nhất cho tác vụ, nhất quán) - **offline-friendly** → quota 0.3324 + health 0.2667 (dung lượng dự phòng tối đa bất kể tốc độ/chi phí) - **reliability-first** → health 0.3524 + stability 0.1905 + reliability 0.04, cao nhất trong tất cả các gói (ít tình huống bất ngờ nhất) - **chaos-mode** → health 0.4000 + taskFit 0.1905 (gói trọng số mà `auto/chaos` gán cho các thành viên trong bảng của nó; cơ chế phân tán song song không đọc các trọng số này và đây không phải là một cấu hình chèn lỗi, xem [CHAOS-MODE.md](../guides/CHAOS-MODE.md#autochaos-parallel-fan-out)) ### Các điều khiển theo từng yêu cầu (header) — #6023 / #6024 / #6025 / #3470 Một combo `auto` có thể được điều hướng **theo từng yêu cầu** thông qua ba header mà không làm thay đổi cấu hình đã lưu của combo. Chúng chỉ áp dụng cho chiến lược `auto` và chỉ cho yêu cầu chứa chúng; `modePack`/`budgetCap`/`budgetFallback` đã lưu của combo được sử dụng khi không có header. | Header | Chấp nhận | Tác dụng | | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `X-OmniRoute-Mode` | bí danh cài đặt sẵn (`fast`, `balanced`, `quality`, `cheap`, `reliable`, `offline`) hoặc tên gói thô (`ship-fast`, `cost-saver`, `quality-first`, `offline-friendly`, `reliability-first`) | Ghi đè các trọng số chấm điểm cho yêu cầu này. `balanced`/`default` buộc sử dụng các trọng số mặc định (không dùng gói). Các giá trị không xác định sẽ bị bỏ qua (cấu hình được giữ nguyên). | | `X-OmniRoute-Budget` | số dương (mức USD tối đa cho mỗi yêu cầu) | Giới hạn cứng về chi phí: các ứng viên có chi phí ước tính vượt quá giới hạn sẽ bị lọc trước khi lựa chọn. Hành vi khi **mọi** ứng viên đều vượt quá giới hạn được kiểm soát bởi `X-OmniRoute-Budget-Fallback` bên dưới. | | `X-OmniRoute-Budget-Fallback` | `cheapest` (mặc định, bí danh: `cheapest-viable`, `soft`) hoặc `strict` (bí danh: `block`, `hard`) | `cheapest`: dùng ứng viên có chi phí thấp nhất trên toàn cục làm phương án dự phòng, dù ứng viên đó vẫn vượt quá giới hạn (hành vi cũ). `strict`: từ chối lựa chọn — yêu cầu thất bại ngay với `HTTP 402` thay vì âm thầm chi vượt mức. Các giá trị không xác định sẽ bị bỏ qua. | | `X-OmniRoute-Effort` | `auto` (các giá trị khác được dành riêng) | Ngân sách suy luận thích ứng: khi yêu cầu **không** chứa trường suy luận dưới bất kỳ dạng nào (`reasoning_effort`, `reasoning`, `thinking`), gateway phân giải `auto` thành `low`/`medium`/`high` dựa trên các tín hiệu xác định từ hình dạng yêu cầu (độ dài tin nhắn cuối cùng của người dùng, kích thước ngữ cảnh tính đến tin nhắn cuối cùng của người dùng, các kết quả công cụ trước đó, độ sâu vòng lặp công cụ). Các tín hiệu được giới hạn trong lượt hiện tại — mọi nội dung sau tin nhắn cuối cùng của người dùng đều bị bỏ qua — vì vậy mọi yêu cầu trong một vòng lặp công cụ đều được phân giải về cùng một mức (ghim không trạng thái theo từng lượt, không có trạng thái phiên, không tăng mức giữa vòng lặp vì điều đó sẽ phá vỡ các tiền tố bộ nhớ đệm prompt ở thượng nguồn). Trường suy luận do client chỉ định rõ ràng luôn được ưu tiên. Chỉ áp dụng cho các yêu cầu có quá trình điều phối thượng nguồn phân giải thành dạng OpenAI Chat Completions (`targetFormat === FORMATS.OPENAI`) — `reasoning_effort` là một trường theo dạng OpenAI, vì vậy header này không có tác dụng đối với yêu cầu nhắm đến Claude hoặc Gemini (xem `open-sse/handlers/chatCore/adaptiveEffortWiring.ts`). | ```bash # Buộc dùng cấu hình nhanh nhất, giới hạn yêu cầu này ở mức $0.05 và chặn hoàn toàn thay vì chi vượt mức curl -sS http://localhost:20128/v1/chat/completions \ -H "Content-Type: application/json" \ -H "X-OmniRoute-Mode: fast" \ -H "X-OmniRoute-Budget: 0.05" \ -H "X-OmniRoute-Budget-Fallback: strict" \ -d '{"model":"auto","messages":[{"role":"user","content":"hi"}]}' ``` Quá trình phân giải là một hàm thuần túy (`open-sse/services/autoCombo/requestControls.ts`); các giá trị đã phân giải được truyền vào các đầu vào `config.modePack` / `config.budgetCap` / `config.budgetFallback` hiện có của engine. `config.budgetFallback` được lưu trữ của một combo ("strict" | "cheapest") thiết lập chính sách lâu dài; header sẽ ghi đè chính sách đó cho một yêu cầu duy nhất. ## Tất cả các chiến lược định tuyến Hỗ trợ công cụ kết hợp của OmniRoute** 20 chiến lược định tuyến** (được khai báo trong `src/shared/constants/routingStrategies.ts` → `ROUTING_STRATEGY_VALUES`). Bản thân công cụ Auto Combo được hiển thị theo chiến lược `auto` ; những cái khác có sẵn cho các combo liên tục. | Chiến lược | Sự miêu tả | | :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `priority` | Danh sách đặt hàng mục tiêu đầu tiên với mức độ ưu tiên rõ ràng | | `weighted` | Trọng số ngẫu nhiên theo trọng lượng mỗi mục tiêu | | `round-robin` | Duyệt qua các mục tiêu theo thứ tự (theo đợt; xem bên dưới) | | `context-relay` | Phân bổ bối cảnh cho các mục tiêu (cuộc trò chuyện dài) | | `fill-first` | Điền vào hạn ngạch của từng mục tiêu trước khi chuyển sang mục tiêu tiếp theo | | `p2c` | cân bằng tải ngẫu nhiên Power-of-2-choices | | `random` | Lựa chọn ngẫu nhiên thống nhất | | `least-used` | Chọn mục tiêu có tải hiện tại thấp nhất | | `cost-optimized` | Giảm thiểu $ cho mỗi yêu cầu theo giá danh mục | | `reset-aware` ⭐ | Ưu tiên theo thời gian đặt lại hạn ngạch — cửa sổ đặt lại ngắn được xếp hạng cao hơn | | `reset-window` | Ưu tiên các mục tiêu có thời hạn hạn ngạch được đặt lại sớm nhất | | `headroom` | Chọn mục tiêu có khoảng trống hạn ngạch còn lại nhiều nhất | | `quota-weighted` | Bỏ qua các tài khoản đã cạn kiệt, sau đó chọn trong số những tài khoản còn lại theo tỷ lệ hạn ngạch còn lại chia cho tải trong chuyến bay; các cuộc trò chuyện hiện có vẫn được ghim | | `strict-random` | Ngẫu nhiên mà không trùng lặp lặp lại | | `auto` | Sử dụng tính điểm Tự động kết hợp (16 yếu tố) —** khuyến khích** | | `lkgp` | Đường dẫn tốt được biết đến lần cuối (ghim vào nhà cung cấp thành công cuối cùng, sau đó quay lại các quy tắc) | | `context-optimized` | Chọn mục tiêu phù hợp nhất với kích thước bối cảnh hiện tại | | `cache-optimized` | Sắp xếp lại các mục tiêu theo mối quan hệ bộ nhớ đệm nhắc nhở — kết nối có khả năng giữ tiền tố được lưu trong bộ nhớ cache của yêu cầu này cao nhất được thử trước tiên (`open-sse/services/combo/promptCacheAffinity.ts`, #8008) | | `fusion` 🧬 | Phân phát song song một nhóm người mẫu, sau đó tổng hợp một câu trả lời thông qua giám khảo (xem bên dưới) | | `pipeline` | Chạy các mục tiêu một cách tuần tự, xâu chuỗi đầu ra của từng bước thành đầu vào của bước tiếp theo; chỉ có câu trả lời cuối cùng được trả lại (#6396) | ⭐ = Mới trong v3.8.0 · 🧬 = Mới trong v3.8.36 ### ngữ nghĩa `weighted` `weighted` là một** rút ngẫu nhiên theo tỷ lệ theo yêu cầu** (`open-sse/services/combo/targetSorters.ts` → `selectWeightedTarget`), không phải bộ cân bằng: - Mỗi yêu cầu rút ra** một** bước có xác suất `weight / totalWeight`; các bước còn lại được sắp xếp theo trọng số giảm dần làm chuỗi dự phòng cho yêu cầu đó. - Một bước có trọng số là `0` (hoặc thiếu) là** không bao giờ vẽ** trong khi bất kỳ bước nào khác có trọng số > 0 — thì nó chỉ có thể đóng vai trò dự phòng sau khi bước rút ra không thành công. Chỉ khi** tất cả** trọng số là 0 thì việc lựa chọn có trở nên thống nhất hay không. - Các bước có mục tiêu là tất cả các bước không có sẵn của nhà cung cấp — ngắt mạch `OPEN`, thời gian hồi chiêu kết nối, khóa mô hình — đều bị xóa khỏi lượt rút thăm trước khi nó xảy ra (`open-sse/services/combo/targetResolution.ts`), vì vậy, một bước lành mạnh có thể tạm thời giành được mọi yêu cầu. - `stickyWeightedLimit` (cấu hình kết hợp, tắt `1` = mặc định) ghim bước đã vẽ để đạt được nhiều thành công liên tiếp trước khi vẽ lại. Để xoay vòng nghiêm ngặt, hãy sử dụng `round-robin`; trọng số bằng nhau trên `weighted` cho — thống kê không cân bằng — nghiêm ngặt. ### Chế độ đường ống đại lý Một tổ hợp `pipeline` có ít nhất hai mô hình có thể chọn tham gia định tuyến planner/executor bằng `config.agenticOrchestration.enabled`. Mục tiêu đầu tiên sở hữu kế hoạch và câu trả lời cuối cùng; mục tiêu thứ hai phát ra các lệnh gọi công cụ gốc của máy khách. OmniRoute phát hiện các phần tiếp theo của kết quả công cụ từ giao thức yêu cầu, hỏi người lập kế hoạch xem có cần một vòng công cụ khác hay không và tự động chuyển người thực thi hoặc người lập kế hoạch thành bước cuối cùng đối mặt với khách hàng. Khi tiếp tục kết quả công cụ, OmniRoute cũng dừng thực thi khi hai vòng hoàn thành liên tiếp lặp lại cùng một lệnh gọi công cụ với kết quả không thay đổi hoặc khi mọi kết quả được đánh dấu rõ ràng trong hai vòng liên tiếp đều không thành công. Kết quả đã thay đổi cho phép các cuộc gọi lặp lại (ví dụ: bỏ phiếu). Trình bảo vệ đặt lại theo yêu cầu mới của người dùng, nhóm các cuộc gọi song song thành một vòng và hỗ trợ lịch sử Anthropic, Hoàn thành trò chuyện, Phản hồi và Gemini. Người lập kế hoạch nhận được lý do và có câu trả lời cuối cùng ngay cả khi yêu cầu một vòng công cụ khác. Các mô hình bổ sung sau mục tiêu thứ hai được yêu cầu sao lưu người thực thi. Phản hồi HTTP không thành công hoặc ngoại lệ vận chuyển sẽ chuyển sang người thực thi tiếp theo, mang theo cùng một quyết định của người lập kế hoạch và các công cụ gốc nhưng lời nhắc bước của chính người thực thi đó và giải quyết kết nối. Phản hồi thành công đầu tiên được trả về không thay đổi, bao gồm cả phát trực tuyến SSE ; không thể thử lại lỗi sau khi bắt đầu phát trực tiếp thành công tại đây. Nếu tất cả người thi hành đều thất bại, lỗi cuối cùng sẽ được trả về. Khách hàng hủy bỏ việc dừng gửi đi. ```json { "strategy": "pipeline", "models": [{ "model": "provider/planner" }, { "model": "provider/executor" }], "config": { "agenticOrchestration": { "enabled": true, "maxToolRounds": 8 } } } ``` Người thực thi có thể phát ra nhiều lệnh gọi độc lập trong một phản hồi. Các cuộc gọi phụ thuộc được xử lý trong các lượt kết quả công cụ khách hàng sau này, với người lập kế hoạch xem xét mọi kết quả. `maxToolRounds` mặc định là `8` và chấp nhận `1`–`32`; Sau khi đạt được, người lập kế hoạch phải đưa ra câu trả lời cuối cùng tốt nhất hiện có. Các quyết định của người lập kế hoạch nội bộ được lưu vào bộ đệm, trong khi phản hồi hướng tới khách hàng đã chọn sẽ giữ nguyên tùy chọn phát trực tuyến ban đầu. ### hàng loạt dính `round-robin` và mở rộng tài khoản Vòng tròn được thực hiện theo đợt, không phải một yêu cầu mỗi bước: - `stickyRoundRobinLimit` (cấu hình kết hợp, sau đó `comboStickyRoundRobinLimit`, sau đó `settings.stickyRoundRobinLimit`, mặc định** 3** ) giữ nguyên mục tiêu cho nhiều thành công liên tiếp trước khi quay. Đặt ghi đè kết hợp thành `1` để xoay vòng một yêu cầu. Trình chỉnh sửa kết hợp hiển thị giá trị hiệu quả và giá trị đó đến từ lớp nào. - `connectionAwareExpansion` (cấu hình kết hợp, sau đó cài đặt, mặc định** SAI** ) mở rộng từng bước cấp nhà cung cấp thành mục tiêu cho mỗi tài khoản trước khi xoay vòng. Các chiến lược nhóm B (ưu tiên, có trọng số, quay vòng, ngẫu nhiên, p2c, ít được sử dụng nhất, tối ưu hóa chi phí, lkgp, điền trước, ngẫu nhiên nghiêm ngặt, tối ưu hóa theo ngữ cảnh, tối ưu hóa bộ nhớ đệm, chuyển tiếp ngữ cảnh, tổng hợp, quy trình) duy trì chế độ xem ở cấp nhà cung cấp cho đến khi tính năng này được bật. Trình chỉnh sửa kết hợp hiển thị tính năng kế thừa/bật/tắt; kế thừa sử dụng mặc định chung (tắt). - Định tuyến cục bộ bộ đệm nhắc nhở (`promptCacheAffinityEnabled`, mặc định** ĐÚNG VẬY** ) sắp xếp lại các kết nối được ghim để các khóa bộ đệm phù hợp vẫn ở trên một tài khoản. Nó được ưu tiên hơn so với luân chuyển vòng tròn và có trọng số trên các bước được ghim trên mỗi tài khoản. Tắt nó trong Cài đặt mặc định → Combo nếu bạn cần xoay vòng nghiêm ngặt. Không có ghi đè cho mỗi combo. Để xoay vòng nhiều tài khoản trên một mô hình, hãy ưu tiên** một bước tài khoản động** (trống `connectionId`, toàn bộ nhóm) với giới hạn cố định `1`, không phải ba `connectionId` s được ghim. Các bước được ghim cộng với mối quan hệ thu gọn vào cùng một tài khoản ngay cả khi bộ đếm RR tiến lên. ## Chiến lược Fusion `fusion` là chiến lược duy nhất **không** chọn một đích duy nhất. Nó phân phối prompt đến **mọi mô hình trong panel theo cách song song**, sau đó một **mô hình giám khảo** có thể cấu hình sẽ tổng hợp một câu trả lời cuối cùng duy nhất từ tất cả phản hồi của panel. Được chuyển từ dự án thượng nguồn `decolua/9router` (thiết kế Fusion của OpenRouter); phần triển khai nằm trong `open-sse/services/fusion.ts`. Cách hoạt động: 0. **Bỏ qua khi có công cụ** — một yêu cầu chứa mảng `tools` không rỗng và có `tool_choice` không được đặt rõ ràng thành `"none"` sẽ bỏ qua hoàn toàn panel: yêu cầu được định tuyến trực tiếp đến một mô hình duy nhất (giám khảo đã cấu hình hoặc `panel[0]`), với `tools`/`tool_choice` được truyền nguyên trạng. Các thành viên panel không có quyền truy cập công cụ và chỉ thị tổng hợp của giám khảo không khuyến khích phát sinh lệnh gọi công cụ, nhờ đó các client tác tử/gọi công cụ nhận được quyết định gọi công cụ thực sự thay vì văn bản tổng hợp (#6771). 1. **Phân phối song song** (chỉ dành cho các yêu cầu không có công cụ) — prompt được gửi đồng thời đến mọi mô hình trong panel, bị buộc không phát trực tuyến và loại bỏ công cụ (giám khảo cần văn bản hoàn chỉnh để tổng hợp). 2. **Thu thập theo ngưỡng đồng thuận có thời gian gia hạn** — ngay khi nhận được `minPanel` câu trả lời, một bộ hẹn giờ gia hạn ngắn sẽ bắt đầu dành cho các mô hình phản hồi chậm, sau đó quá trình fusion tiếp tục với những gì đã thu thập được. Điều này giới hạn mức ảnh hưởng của mô hình chậm nhất lên tổng thời gian thực tế, trong phạm vi một thời gian chờ cứng. 3. **Tổng hợp bởi giám khảo** — các câu trả lời của panel được ẩn danh (`Source 1`, `Source 2`, … — để giám khảo đánh giá nội dung thay vì thương hiệu mô hình) và chuyển cho giám khảo, mô hình này sẽ phân tích sự đồng thuận / mâu thuẫn / mức độ bao phủ một phần / thông tin chuyên sâu độc đáo / điểm mù, rồi viết **một** câu trả lời có thẩm quyền. Lệnh gọi giám khảo giữ nguyên cờ `stream` ban đầu của client cùng với các công cụ, vì vậy tính năng phát trực tuyến và việc sử dụng công cụ ở hạ nguồn vẫn hoạt động. 4. **Suy giảm mềm** — 0 câu trả lời từ panel → `503`; chỉ có đúng 1 mô hình còn lại → câu trả lời đó được trả về trực tiếp (không có gì để hợp nhất); panel chỉ có một mô hình sẽ trả lời trực tiếp. Một thành viên panel cũng có thể là một bước `combo-ref` (`{kind: "combo-ref", comboName: "..."}`) tham chiếu đến một combo khác — nó được xử lý như **một tiếng nói panel dạng hộp đen duy nhất** (một quy trình điều phối đệ quy đầy đủ vào combo được tham chiếu, không phải phân phối song song đến các đích riêng của combo đó), với cùng cơ chế bảo vệ độ sâu/chu trình mà mọi chiến lược sử dụng combo-ref khác đều đã áp dụng (#6764). ### Cấu hình Được cấu hình trong blob `config` của combo (không cần di chuyển schema — chiến lược này tái sử dụng bảng `combos` hiện có): | Trường | Kiểu | Mặc định | Mục đích | | :--------------------------------------- | :------- | :--------------------------- | :------------------------------------------------------------------------------------------------------ | | `config.judgeModel` | `string` | mô hình đầu tiên trong panel | Mô hình tổng hợp câu trả lời cuối cùng | | `config.fusionTuning.minPanel` | `number` | `2` | Số câu trả lời thành công cần có trước khi bộ hẹn giờ gia hạn bắt đầu (giới hạn trong `[2, panelSize]`) | | `config.fusionTuning.stragglerGraceMs` | `number` | `8000` | Thời gian chờ các mô hình phản hồi chậm sau khi đạt ngưỡng đồng thuận | | `config.fusionTuning.panelHardTimeoutMs` | `number` | `90000` | Giới hạn tuyệt đối để một mô hình bị treo không thể làm đình trệ yêu cầu | Các giá trị mặc định nằm trong `FUSION_DEFAULTS` (`open-sse/services/fusion.ts`). ### Ví dụ ```bash curl -X POST http://localhost:20128/api/combos \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "fusion-panel", "strategy": "fusion", "targets": [ { "model": "cc/claude-opus-4-7" }, { "model": "cx/gpt-5.5" }, { "model": "glm/glm-5.1" } ], "config": { "judgeModel": "cc/claude-opus-4-7", "fusionTuning": { "minPanel": 2, "stragglerGraceMs": 8000, "panelHardTimeoutMs": 90000 } } }' ``` Sau đó, gọi nó như bất kỳ combo nào khác: `{"model":"fusion-panel","messages":[...]}`. ## Nhà máy Auto-Combo ảo Công cụ Auto Combo không yêu cầu các combo được định nghĩa sẵn. Thay vào đó, `open-sse/services/autoCombo/virtualFactory.ts` xây dựng các ứng viên ngay khi cần: 1. Lấy `getProviderConnections({ isActive: true })` (tất cả kết nối đã bật) 2. Lọc ra các kết nối có thông tin xác thực hợp lệ (API key hoặc token OAuth chưa hết hạn thông qua `hasUsableOAuthToken()`) 3. Đối chiếu với `getProviderRegistry()` để xác định tính khả dụng của mô hình + giá 4. Với mỗi bộ `(provider, model, connection)`, xây dựng một `VirtualAutoComboCandidate` 5. Chọn `connection.defaultModel` (hoặc mô hình đầu tiên trong registry) làm mục tiêu điều phối 6. Chấm điểm từng ứng viên bằng `scorePool()` gồm 16 yếu tố và bộ trọng số của biến thể 7. Trả về `AutoComboConfig` trong bộ nhớ cho `handleComboChat()` — không bao giờ được lưu vào DB Điều này có nghĩa là **việc thêm một nhà cung cấp mới có bật `auto/*` sẽ tự động mở rộng nhóm ứng viên** — không cần chỉnh sửa combo thủ công. Combo ảo được xây dựng lại cho mỗi yêu cầu, vì vậy các kết nối mới được thêm hoặc vừa khôi phục trạng thái ổn định sẽ được sử dụng ngay lập tức. ## Tự phục hồi - **Loại trừ tạm thời**: Điểm < 0.2 → bị loại trừ trong 5 phút (thời gian chờ tăng dần, tối đa 30 phút) - **Nhận biết circuit breaker**: OPEN → tự động bị loại trừ; HALF_OPEN → gửi yêu cầu thăm dò - **Chế độ sự cố**: >50% ở trạng thái OPEN → tắt khám phá, tối đa hóa độ ổn định - **Khôi phục sau thời gian chờ**: Sau khi hết thời gian loại trừ, yêu cầu đầu tiên là một yêu cầu "thăm dò" với thời gian chờ ngắn hơn ## Khám phá bằng Bandit 5% số yêu cầu (có thể cấu hình) được định tuyến ngẫu nhiên tới các nhà cung cấp để khám phá. Tính năng này bị tắt trong chế độ sự cố. ## API **Không có endpoint chuyên dụng `POST /api/combos/auto`** — Auto-Combo được sử dụng theo hai cách: 1. **Không cần cấu hình (khuyến nghị):** Gửi bất kỳ yêu cầu hoàn thành hội thoại nào với `model: "auto"` hoặc `model: "auto/"`. Factory ảo sẽ tạo combo cho từng yêu cầu — không cần lưu trữ lâu dài, không cần gọi API. 2. **Combo được lưu trữ với `strategy: "auto"`:** Tạo một combo thông thường qua `POST /api/combos` và đặt `strategy: "auto"` cùng với `config.auto.weights` / `config.auto.candidatePool`. Cùng một công cụ chấm điểm sẽ được sử dụng; combo được lưu trong `combos` và có thể tái sử dụng bằng ID. Để khám phá, `GET /api/combos/auto` liệt kê mọi biến thể cùng với nhóm ứng viên đã được phân giải và `context_length` / `max_output_tokens` — giá trị MAX trên tất cả cửa sổ trong nhóm ứng viên. Các client (ví dụ: plugin opencode) phải công bố những giá trị này thay vì `0`: context bằng không sẽ vô hiệu hóa hoàn toàn tính năng tự động nén của opencode, khiến phiên tiếp tục tăng kích thước cho đến khi cơ chế xóa lịch sử của gateway làm mất context. Có thể công bố MAX một cách an toàn vì bộ lọc trước theo context của auto-combo sẽ định tuyến các yêu cầu quá lớn đến những ứng viên có cửa sổ lớn. ```bash # Sử dụng không cần cấu hình (không tạo combo) curl -X POST http://localhost:20128/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"auto/coding","messages":[{"role":"user","content":"Hello"}]}' # Combo tự động được lưu trữ thông qua endpoint combos thông thường curl -X POST http://localhost:20128/api/combos \ -H "Content-Type: application/json" \ -d '{"id":"my-auto","name":"Auto Coder","strategy":"auto","config":{"auto":{"candidatePool":["anthropic","google","openai"],"weights":{"quota":0.15,"health":0.3,"costInv":0.05,"latencyInv":0.35,"taskFit":0.1,"stability":0,"tierPriority":0.05}}}}' ``` ### Các chiến lược router tự động Các combo `strategy: "auto"` được lưu trữ có thể đặt `config.routerStrategy` (hoặc dạng cũ `config.auto.routerStrategy`) thành một trong các giá trị sau: - `rules` — chấm điểm có trọng số mặc định - `score` — chọn điểm có trọng số được cấu hình cao nhất. Khi điểm hoàn toàn bằng nhau, thứ tự ứng viên đã cấu hình được giữ nguyên; `explorationRate` hiện có lấy mẫu từ toàn bộ nhóm đã xếp hạng. - `cost` / `eco` — nhà cung cấp khỏe mạnh có chi phí thấp nhất - `latency` / `fast` — độ trễ p95 thấp nhất kèm hệ số phạt về độ tin cậy - `sla-aware` / `sla` — ưu tiên các ứng viên đáp ứng SLO về độ trễ p95, tỷ lệ lỗi và chi phí tùy chọn - `lkgp` — ưu tiên nhà cung cấp hoạt động tốt gần nhất - `nadir` — yêu cầu API quyết định của [Nadir](https://getnadir.com) xác định mô hình nào trong nhóm mà prompt cần; cần chủ động bật, khi lỗi sẽ chuyển sang `rules` ### Chi tiết các chiến lược router Công cụ auto-combo cung cấp 7 cách triển khai **RouterStrategy** có thể thay thế thông qua `config.routerStrategy` (hoặc dạng cũ `config.auto.routerStrategy`). Mỗi chiến lược chọn một nhà cung cấp từ nhóm ứng viên dựa trên `RoutingContext` (loại tác vụ, gợi ý về công cụ/thị giác, ước tính token, chính sách SLA tùy chọn, nhà cung cấp hoạt động tốt gần nhất tùy chọn). #### 1. `rules` (mặc định) — chấm điểm có trọng số theo 16 yếu tố Bao bọc công cụ chấm điểm hiện có. Lọc bỏ các ứng viên có circuit breaker ở trạng thái `OPEN`, sau đó chạy `scorePool()` với loại tác vụ hiện tại và `getTaskFitness()`, rồi chọn nhà cung cấp có điểm cao nhất. ```ts class RulesStrategyImpl implements RouterStrategy { readonly name = "rules"; readonly description = "16-factor weighted scoring (see DEFAULT_WEIGHTS)"; select(pool, context) { const eligible = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const ranked = scorePool( eligible.length > 0 ? eligible : pool, context.taskType, undefined, getTaskFitness ); return { provider: ranked[0].provider /* ... */ }; } } ``` **Khi nào nên sử dụng**: Mặc định. Sử dụng khi bạn muốn cân bằng giữa tất cả các tín hiệu. **Bí danh**: `rules` (không có bí danh) --- #### 2. `cost` / `eco` — nhà cung cấp khỏe mạnh có chi phí thấp nhất Sắp xếp nhóm ứng viên theo `costPer1MTokens` (tăng dần) và chọn ứng viên có chi phí thấp nhất. Trước tiên, lọc bỏ các ứng viên `OPEN`. ```ts class CostStrategyImpl implements RouterStrategy { readonly name = "cost"; readonly description = "Always selects cheapest available provider"; select(pool, context) { const healthy = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const sorted = [...healthy].sort((a, b) => a.costPer1MTokens - b.costPer1MTokens); return { provider: sorted[0].provider /* ... */ }; } } ``` **Khi nào nên sử dụng**: Khối lượng công việc nhạy cảm về chi phí, xử lý hàng loạt hoặc các tác vụ nền. **Bí danh**: `cost`, `eco` --- #### 3. `latency` / `fast` — độ trễ p95 thấp nhất kèm hệ số phạt về độ tin cậy Sắp xếp theo `p95LatencyMs + (errorRate * 1000)`. Hệ số phạt theo tỷ lệ lỗi đảm bảo các nhà cung cấp không đáng tin cậy được xếp hạng thấp hơn ngay cả khi độ trễ danh nghĩa của họ thấp. ```ts class LatencyStrategyImpl implements RouterStrategy { readonly name = "latency"; readonly description = "Prioritizes lowest p95 latency with reliability weighting"; select(pool, context) { const healthy = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const sorted = [...healthy].sort( (a, b) => a.p95LatencyMs + a.errorRate * 1000 - (b.p95LatencyMs + b.errorRate * 1000) ); return { provider: sorted[0].provider /* ... */ }; } } ``` **Khi nào nên sử dụng**: Khối lượng công việc nhạy cảm về độ trễ như hội thoại thời gian thực, tự động hoàn thành hoặc trợ lý lập trình tương tác. **Bí danh**: `latency`, `fast` --- #### 4. `sla-aware` / `sla` — tuân thủ SLO về độ trễ/lỗi/chi phí Chấm điểm từng ứng viên dựa trên mức độ đáp ứng chính sách SLO đã cấu hình: | Yếu tố | Trọng số | Công thức | | ------------- | -------- | ----------------------------------------------------- | | Điểm độ trễ | 35% | `threshold / max(value, ε)` | | Điểm lỗi | 35% | `threshold / max(value, ε)` | | Điểm sức khỏe | 15% | `1.0` (CLOSED) / `0.5` (HALF_OPEN) / `0.0` (OPEN) | | Điểm chi phí | 10% | `threshold / max(value, ε)` hoặc chuẩn hóa nghịch đảo | | Điểm ổn định | 5% | độ lệch chuẩn độ trễ được chuẩn hóa nghịch đảo | Khi `hardConstraints: true`, các ứng viên được sắp xếp chủ yếu theo **điểm vi phạm** (mức độ vượt quá bất kỳ SLO nào), sau đó theo điểm tổng hợp. Nếu không, chúng chỉ được sắp xếp theo điểm tổng hợp. ```ts class SLAStrategyImpl implements RouterStrategy { readonly name = "sla-aware"; readonly description = "Selects the provider most likely to satisfy latency, error-rate, and cost SLOs"; select(pool, context) { // ... chấm điểm từng ứng viên theo chính sách: { targetP95Ms, maxErrorRate, maxCostPer1MTokens, hardConstraints } } } ``` **Các trường SLA** (được đặt trong cấu hình combo): ```json { "strategy": "auto", "config": { "routerStrategy": "sla-aware", "slaTargetP95Ms": 1500, "slaMaxErrorRate": 0.05, "slaMaxCostPer1MTokens": 5, "slaHardConstraints": true } } ``` **Khi nào nên sử dụng**: Khối lượng công việc production có yêu cầu nghiêm ngặt về độ trễ, tỷ lệ lỗi hoặc ngân sách chi phí. **Bí danh**: `sla-aware`, `sla` --- #### 5. `lkgp` — ưu tiên nhà cung cấp tốt gần nhất được biết Thử **nhà cung cấp tốt gần nhất được biết** (nếu đã đặt) trước, sau đó chuyển dự phòng sang chiến lược `rules`. Hữu ích để duy trì tính nhất quán trong phiên — cùng một nhà cung cấp xử lý các yêu cầu tiếp theo trong một cuộc hội thoại. ```ts class LKGPStrategyImpl implements RouterStrategy { readonly name = "lkgp"; readonly description = "Tries last known good provider first, then falls back to rules"; select(pool, context) { if (context.lkgpEnabled === false) { return getStrategy("rules").select(pool, context); } if (context.lastKnownGoodProvider) { const candidates = pool.filter( (c) => c.provider === context.lastKnownGoodProvider && c.circuitBreakerState !== "OPEN" ); if (candidates.length > 0) { return { provider: candidates[0].provider /* ... */ }; } } // Chuyển dự phòng sang chiến lược rules return getStrategy("rules").select(pool, context); } } ``` **Khi nào nên sử dụng**: Các cuộc hội thoại nhiều lượt mà bạn muốn cùng một nhà cung cấp xử lý các yêu cầu tiếp theo (ví dụ: để lưu vào bộ nhớ đệm, duy trì tính liên tục của ngữ cảnh hoặc tính nhất quán về giá). **Bí danh**: `lkgp` (không có bí danh) --- #### 6. `nadir` — lựa chọn mô hình có nhận biết prompt thông qua Nadir Mỗi chiến lược ở trên đều xếp hạng các ứng viên dựa trên dữ liệu đo từ xa riêng; không chiến lược nào đọc yêu cầu. `nadir` gửi lượt người dùng cuối cùng cùng với các ID mô hình trong pool đến API quyết định của [Nadir](https://getnadir.com) (`POST /v1/bucket`) và định tuyến đến mô hình mà Nadir chọn từ danh sách đó (`simple` → mô hình có khả năng đáp ứng với chi phí thấp nhất, `complex` → mô hình frontier). Kết nối phục vụ mô hình đó vẫn được `rules` lựa chọn, vì vậy hạn ngạch, tình trạng và chi phí vẫn quyết định tài khoản nào được dùng. ```json { "strategy": "auto", "config": { "routerStrategy": "nadir", "nadir": { "apiKey": "ndr_...", "baseUrl": "https://api.getnadir.com", "timeoutMs": 2000 } } } ``` `OMNIROUTE_NADIR_API_KEY` và `OMNIROUTE_NADIR_BASE_URL` là các giá trị dự phòng từ môi trường cho hai chuỗi này. `baseUrl` chỉ cần thiết đối với Nadir tự lưu trữ (dấu `/v1` ở cuối vẫn được chấp nhận). Các lệnh gọi không có khóa sẽ sử dụng gói ẩn danh của Nadir, vốn bị giới hạn tốc độ theo từng IP. Khi có khóa ([tạo khóa](https://app.getnadir.com/dashboard/api-keys)), mỗi quyết định sẽ được ghi lại vào bảng điều khiển Nadir của bạn. Hướng dẫn thiết lập và xác minh đầy đủ: [getnadir.com/docs/gateway-omniroute](https://getnadir.com/docs/gateway-omniroute/). Dữ liệu được gửi ra ngoài: nội dung văn bản của tin nhắn người dùng cuối cùng (16k ký tự đầu tiên), các ID mô hình ứng viên và thẻ kênh `source: "omniroute"`. Không có system prompt, lịch sử, công cụ hoặc header nào được gửi. Cơ chế xử lý lỗi là fail-open: timeout (mặc định 2000 ms), phản hồi không phải 2xx, máy chủ không thể truy cập, phản hồi sai định dạng hoặc lựa chọn nằm ngoài pool đều sẽ được chuyển thành quyết định của `rules` và lý do được thêm tiền tố `NadirStrategy: fallback (…)`. Sau một lệnh gọi thất bại, chiến lược sẽ bỏ qua mạng trong 30 giây, vì vậy một sự cố ngừng hoạt động chỉ gây ra một lần timeout mỗi 30 giây thay vì một lần cho mỗi yêu cầu. Các sự kiện định tuyến chỉ báo cáo `strategy: "nadir"` khi Nadir thực sự đưa ra lựa chọn. **Khi nào nên sử dụng**: lưu lượng có độ khó hỗn hợp trên một pool bao gồm nhiều cấp mô hình (một mô hình nhỏ, một mô hình tầm trung và một mô hình frontier), khi bạn muốn cắt giảm chi phí do luôn sử dụng mô hình frontier. **Bí danh**: `nadir` (không có bí danh) --- ### Chiến lược bộ định tuyến tùy chỉnh Bạn có thể đăng ký phần triển khai `RouterStrategy` của riêng mình thông qua API công khai: ```ts import { registerStrategy, type RouterStrategy, } from "@omniroute/open-sse/services/autoCombo/routerStrategy"; class MyCustomStrategy implements RouterStrategy { readonly name = "my-custom"; readonly description = "My custom routing strategy"; select(pool, context) { // Logic định tuyến của bạn ở đây return { provider: pool[0].provider, model: pool[0].model, strategy: this.name, reason: "MyCustomStrategy: ...", candidatesConsidered: pool.length, finalScore: 1.0, }; } } registerStrategy("my-custom", new MyCustomStrategy()); ``` Sau đó sử dụng chiến lược này: ```json { "strategy": "auto", "config": { "routerStrategy": "my-custom" } } ``` --- ### Hướng dẫn lựa chọn chiến lược bộ định tuyến | Trường hợp sử dụng | Chiến lược | Lý do | | ----------------------------- | ----------- | -------------------------------------------- | | Khối lượng công việc cân bằng | `rules` | Mặc định — xem xét tất cả các yếu tố | | Giảm thiểu chi phí | `cost` | Luôn chọn phương án rẻ nhất | | Giảm thiểu độ trễ | `latency` | Chọn nhà cung cấp nhanh và đáng tin cậy nhất | | SLO nghiêm ngặt | `sla-aware` | Lọc theo ngưỡng p95/lỗi/chi phí | | Trò chuyện nhiều lượt | `lkgp` | Duy trì tính nhất quán trong phiên | | Độ khó hỗn hợp | `nadir` | Chọn cấp độ mô hình cho từng prompt | Các trường nhận biết SLA: ```json { "strategy": "auto", "config": { "routerStrategy": "sla-aware", "slaTargetP95Ms": 1500, "slaMaxErrorRate": 0.05, "slaMaxCostPer1MTokens": 5, "slaHardConstraints": true } } ``` ## Mức độ phù hợp với tác vụ Hơn 30 mô hình được chấm điểm trên 6 loại tác vụ (`coding`, `review`, `planning`, `analysis`, `debugging`, `documentation`). Hỗ trợ các mẫu ký tự đại diện (ví dụ: `*-coder` → điểm coding cao). ## Tóm tắt các biến thể Auto Bao gồm `auto` nguyên bản (mặc định) cùng với 6 giá trị `AutoVariant` được khai báo trong `autoPrefix.ts`, có tổng cộng **7 ID mô hình có thể gọi**: `auto`, `auto/coding`, `auto/fast`, `auto/cheap`, `auto/offline`, `auto/smart`, `auto/lkgp` (Bản thân `AutoVariant` liệt kê 6 giá trị; tùy chọn thứ 7 là "không có biến thể" — `auto` nguyên bản — được `parseAutoPrefix()` xử lý dưới dạng `variant: undefined`.) ## Cách các bậc hoạt động trong Auto-Combo Hàm chấm điểm gồm 16 yếu tố (`open-sse/services/autoCombo/scoring.ts`) xem việc thuộc một bậc là hai tín hiệu: `tierPriority` (0.0476) và `tierAffinity` (0.0476). Xem [bảng yếu tố chấm điểm chuẩn](#how-it-works-persisted-auto-combos) ở trên để biết đầy đủ tập hợp `DEFAULT_WEIGHTS` — các giá trị ghi đè theo từng gói (ship-fast/cost-saver/quality-first/ offline-friendly) được liệt kê trong bảng "Hồ sơ trọng số theo từng gói". Chỉ riêng bậc **không** buộc Bậc 1 phải được ưu tiên trước — nếu độ trễ của Bậc 1 kém hoặc tỷ lệ chi phí so với chất lượng không tối ưu, Bậc 2 sẽ được chọn. Để buộc thứ tự theo bậc, hãy sử dụng chiến lược tổ hợp `priority` và sắp xếp các nhà cung cấp theo bậc. Để ưu tiên mạnh Bậc 1 (gói đăng ký), hãy tăng trọng số `tierPriority`: ```json { "strategy": "auto", "config": { "auto": { "weights": { "tierPriority": 0.3, "costInv": 0.05 } } } } ``` Xem [`docs/guides/TIERS.md`](../guides/TIERS.md) để biết định nghĩa các bậc và cách phân loại nhà cung cấp. ## Kiểm thử & độ bao phủ ### Ma trận quyết định định tuyến tất định (`npm run test:combo:matrix`) `tests/integration/combo-matrix/*.test.ts` xác minh **quyết định** định tuyến của toàn bộ 19 chiến lược công khai từ đầu đến cuối thông qua pipeline combo thực với một upstream mô phỏng. Phạm vi bao phủ bao gồm: - Toàn bộ 19 chiến lược `ROUTING_STRATEGY_VALUES` (ordered, weighted, cost, context, fusion, …). - `quota-share` (nội bộ) từ đầu đến cuối: tính công bằng DRR + hạ mức ưu tiên khi bão hòa thông qua điểm nối `selectQuotaShareTarget` thực (`registerQuotaFetcher` / `setLKGP` / `__setHeadroomSaturationFetcherForTests`). - Độ bao phủ chuyển giao toàn cục của `context-relay` trên mọi số lượng đích. Bộ kiểm thử này chạy trong CI (tác vụ `test:integration`) với `--test-concurrency=1` và `--test-force-exit`, do đó có tính tất định và không yêu cầu thông tin xác thực thực tế. ### Kiểm thử nhanh trực tiếp có điều kiện (KHÔNG chạy trong CI — nhà cung cấp thực) | Lệnh | Chức năng | | :------------------------------------- | :---------------------------------------------------------------------------------------------------------- | | `npm run test:combo:live` | Định tuyến thực trong tiến trình với `RUN_COMBO_LIVE=1`; tạo snapshot của cơ sở dữ liệu OmniRoute trực tiếp | | `npm run test:combo:live:vps` | Các lệnh gọi HTTP đến máy chủ OmniRoute trực tiếp (đặt `COMBO_LIVE_BASE_URL`) | | `npm run test:combo:live:vps:failover` | Tương tự, với các kịch bản chuyển đổi dự phòng có chủ đích | Các kiểm thử nhanh này thực thi đường truyền thực tế (combo → nhà cung cấp → completion). Chúng được chủ ý loại khỏi CI vì yêu cầu thông tin xác thực thực tế và quyền truy cập VPS. --- ## Tệp | Tệp | Mục đích | | :-------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | `open-sse/services/autoCombo/scoring.ts` | Hàm chấm điểm 16 yếu tố, `DEFAULT_WEIGHTS`, chuẩn hóa nhóm | | `open-sse/services/autoCombo/taskFitness.ts` | Tra cứu độ phù hợp giữa mô hình × tác vụ | | `open-sse/services/autoCombo/engine.ts` | Logic lựa chọn, bandit, giới hạn ngân sách | | `open-sse/services/autoCombo/selfHealing.ts` | Loại trừ, thăm dò, chế độ sự cố | | `open-sse/services/autoCombo/modePacks.ts` | 6 hồ sơ trọng số (phát hành nhanh, tiết kiệm chi phí, ưu tiên chất lượng, thân thiện với ngoại tuyến, ưu tiên độ tin cậy, chế độ hỗn loạn) | | `open-sse/services/autoCombo/autoPrefix.ts` | Trình phân tích tiền tố `auto/` + 6 biến thể | | `open-sse/services/autoCombo/virtualFactory.ts` | Tạo `AutoComboConfig` trong bộ nhớ từ các kết nối đang hoạt động | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Hook kiểm thử để mô phỏng sổ đăng ký nhà cung cấp | | `src/shared/constants/routingStrategies.ts` | `ROUTING_STRATEGY_VALUES` (20 chiến lược) | | `src/sse/handlers/chat.ts` | Tích hợp: đoản mạch tiền tố tự động |