llm-switcher 1.6.8 → 1.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -2
- package/README.md +59 -4
- package/README.vi.md +60 -4
- package/formats.mjs +169 -20
- package/package.json +1 -1
- package/proxy.mjs +25 -3
- package/tests/compaction.e2e.test.mjs +17 -1
- package/tests/formats.test.mjs +292 -2
- package/tests/gateway.e2e.test.mjs +19 -1
- package/tests/ui.test.mjs +7 -1
- package/ui.html +761 -126
- package/upstream-stream.mjs +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
# Changelog — LLM Switcher
|
|
2
2
|
|
|
3
|
+
## Release 1.6.10 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
- **`reasoning_content` replay.** On assistant tool-call turns, `auto` mode sends the earlier thinking as `reasoning_content`. `native` mode sends it only when the profile sets `replayReasoning: true`, because a strict OpenAI-compatible server can refuse the field. `off` mode never sends it.
|
|
6
|
+
- **Codex to Anthropic with thinking.** If an open tool loop has no signed thinking, the request drops `thinking`. Anthropic refused this request with HTTP 400.
|
|
7
|
+
- **Gemini schema and turn shape.** A nested object with no properties gets an optional `_placeholder` property. An untyped `const` becomes a string. Tool output with keys that start with `$` is wrapped as `{result: ...}`. A request never ends on a model turn: the proxy adds a user turn `Continue.`.
|
|
8
|
+
- **Schema sanitizer.** Keywords with a `null` value and `pattern` values with lookaround are removed.
|
|
9
|
+
- **Long tool names from Codex.** Names longer than 64 characters use the hashed shortener, so two long names do not collide.
|
|
10
|
+
- **Repeated tool ids.** On the direct Anthropic route, a repeated `tool_use` id gets a suffix (`_2`, `_3`), and the matching `tool_result` changes with it.
|
|
11
|
+
- **Context overflow errors.** Anthropic clients receive `invalid_request_error` with a message that starts with `prompt is too long`. Responses clients receive the code `context_length_exceeded`.
|
|
12
|
+
|
|
13
|
+
## Release 1.6.9 — 2026-10-11
|
|
14
|
+
|
|
15
|
+
- **Compaction keeps the tools.** A Codex compaction turn with tool history now keeps the `tools` list and sets `tool_choice` to `none`. Before, Anthropic and OpenAI chat upstreams refused this turn with HTTP 400.
|
|
16
|
+
- **Foreign tool ids.** Tool ids that do not match `^[a-zA-Z0-9_-]+$` (for example `functions.Read:0`) are rewritten before they go to Anthropic. The rewrite is deterministic, so a replayed history gives the same id.
|
|
17
|
+
- **Empty text blocks.** The direct Anthropic route removes empty `system` blocks and empty text blocks. Anthropic refused them with HTTP 400.
|
|
18
|
+
- **Cut streams.** If an upstream stream closes without a finish reason and without `[DONE]`, the client receives an error. Before, the client received a completed turn with a partial tool call.
|
|
19
|
+
- **Gemini `MALFORMED_FUNCTION_CALL`.** This finish reason is now an error, not an empty turn.
|
|
20
|
+
- **Empty tool arguments.** An empty arguments string becomes `{}`, not `{"raw":""}`.
|
|
21
|
+
|
|
3
22
|
## Release 1.6.8 — 2026-10-09
|
|
4
23
|
|
|
5
|
-
- **
|
|
6
|
-
- **
|
|
24
|
+
- **Reorganized Sidebar Navigation by Frequency of Use:** Reordered sidebar navigation to align strictly with developer operational workflows: 1. `Routes & Profiles` (daily configuration & profile switching), 2. `Request Inspector` (live request monitoring & debugging), 3. `Model Catalog` (model discovery & testing), 4. `System Doctor` (health checks, port conflicts & shims), 5. `Goal Check` (agent task completion verification & veto), 6. `Idle Compaction` (idle prefix compaction & cache metrics), and 7. `Docs & Guide` (reference manual & technical guides at the bottom of navigation).
|
|
25
|
+
- **Dedicated intact Gateway Upstream Pooler Guide (`#doc-intact`):** Added a comprehensive, in-depth installation and operational manual for intact inside the Web UI Documentation hub (`#/docs`). Covers Zero-CGO Go build & npm global install, CLI flags, environment variables, systemd service unit setup, Cloudflare Tunnel ingress, TOTP zero-password authentication, account rotation (Round Robin 1 vs N reqs for prompt caching, Fallback, Standby), conversation stickiness, 429 Retry Shorter, Blacklist auto-healing, Bifrost passthrough, and Contract Lab synchronization.
|
|
26
|
+
- **Dedicated Documentation & User Guide Hub (`#/docs`):** Integrated a full-featured, zero-dependency documentation viewer into the Web UI. Features a sticky scroll-spy table of contents, 1-click code copy with clipboard toast feedback, and 11 comprehensive guides covering edge gateway architecture, cross-platform installation, intact upstream pooler, complete CLI cheatsheet, Blindfold TLS interception & CA safety guarantees, Antigravity CLI (`agy`) relay security, token optimizer interop with the Healer engine, Agent Skill (`SKILL.md`) & MCP server setup, Idle Compaction, Jev Goal Check gate, annotated `config.json` schema, and troubleshooting.
|
|
27
|
+
- **Audit & Documentation Alignment:** Reconciled documentation across README.md, README.vi.md, agent skills, and Model Context Protocol definitions to strictly reflect v1.6.8 multi-active routing, zero-mutation invariants, and Bifrost passthrough dynamics.
|
|
7
28
|
|
|
8
29
|
## Release 1.6.7 — 2026-10-09
|
|
9
30
|
|
package/README.md
CHANGED
|
@@ -597,6 +597,23 @@ reuse a stable prefix. Native Codex compaction remains Codex's own responsibilit
|
|
|
597
597
|
When using intact, configure the profile with an authorized intact API key. Client User-Agent
|
|
598
598
|
matching chooses the native protocol route; it does not replace upstream authentication.
|
|
599
599
|
|
|
600
|
+
### Goal check (Jev System One evaluator veto)
|
|
601
|
+
|
|
602
|
+
In Claude Code, when setting `/goal`, a specialized prompt-based evaluator reads the transcript and
|
|
603
|
+
decides whether the condition was met. However, the evaluator only inspects text and can hallucinate
|
|
604
|
+
completion — mistaking report editing or retracted audit findings for genuine code fixes (false completion).
|
|
605
|
+
|
|
606
|
+
**Goal Check** intercepts the evaluator response before it reaches Claude Code, and queries the **Jev
|
|
607
|
+
(System One)** decision model across 3 independent scoring axes:
|
|
608
|
+
- `complete` (>= 0.90): The task condition is genuinely fulfilled.
|
|
609
|
+
- `evidence` (>= 0.85): Concrete tool-execution results exist (test runs, builds, file edits).
|
|
610
|
+
- `unfinished` (<= 0.10): No pending work, unaddressed errors, or missing verification.
|
|
611
|
+
|
|
612
|
+
**Fail-Closed principle:** The gateway yields `ok: true` only when **both** the native evaluator and Jev
|
|
613
|
+
agree. Any network error, timeout, missing evidence, or schema violation results in an automatic Veto
|
|
614
|
+
(`ok: false`), instructing the agent to provide verifiable tool evidence. Configure via the **Goal Check**
|
|
615
|
+
dashboard tab or `config.json` (`goalCheck`).
|
|
616
|
+
|
|
600
617
|
### Updates
|
|
601
618
|
|
|
602
619
|
When npm has a newer release, the dashboard shows a notice. Click **Update now** in this notice. The gateway installs the release and starts the new code. Then the dashboard reloads. `switch update` does the same from a terminal.
|
|
@@ -749,7 +766,7 @@ of the routing.
|
|
|
749
766
|
// A profile that serves Codex MUST have publicModels (see Codex-first setup).
|
|
750
767
|
"publicModels": ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"], // official names for main, review, subagent
|
|
751
768
|
"codexRoles": { "review": "gpt-5.6-sol" }, // optional: pair one role with another public name
|
|
752
|
-
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" }
|
|
769
|
+
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" }
|
|
753
770
|
},
|
|
754
771
|
"agy-default": {
|
|
755
772
|
"name": "agy via intact",
|
|
@@ -763,6 +780,20 @@ of the routing.
|
|
|
763
780
|
}
|
|
764
781
|
},
|
|
765
782
|
"debug": false,
|
|
783
|
+
"idleCompact": {
|
|
784
|
+
"enabled": false,
|
|
785
|
+
"codex": false,
|
|
786
|
+
"idleMinutes": 15,
|
|
787
|
+
"minBytes": 65536,
|
|
788
|
+
"model": ""
|
|
789
|
+
},
|
|
790
|
+
"goalCheck": {
|
|
791
|
+
"enabled": false,
|
|
792
|
+
"backend": "jev",
|
|
793
|
+
"baseURL": "https://intact.example.com/v1",
|
|
794
|
+
"apiKey": "sk-...",
|
|
795
|
+
"model": "typesafe/jev-latest"
|
|
796
|
+
},
|
|
766
797
|
"contractLab": {
|
|
767
798
|
"url": "https://intact.example.com",
|
|
768
799
|
"apiKey": "sk-...",
|
|
@@ -791,10 +822,33 @@ Before it sends a sample, the gateway masks every string value with `x` of the s
|
|
|
791
822
|
- `switch contract-probe [--model m]` sends six test requests per model and format through the gateway.
|
|
792
823
|
- `switch contract-check` gets the open findings from intact and writes one test file for each lost field.
|
|
793
824
|
|
|
794
|
-
### Self-improvement with intact
|
|
825
|
+
### Self-improvement & Operation with intact (Recommended Upstream Pooler)
|
|
826
|
+
|
|
827
|
+
[intact](https://github.com/louisphamdev/intact) is the credential proxy and multi-account pooler that this gateway recommends as its upstream. The two tools find and correct their own faults, in two closed loops.
|
|
828
|
+
|
|
829
|
+
#### Quick Install for intact
|
|
830
|
+
```bash
|
|
831
|
+
# Global install via npm:
|
|
832
|
+
npm install -g intact-gateway
|
|
833
|
+
|
|
834
|
+
# Or compile from Go source (Zero-CGO, Pure-Go SQLite):
|
|
835
|
+
git clone https://github.com/louisphamdev/intact.git && cd intact
|
|
836
|
+
CGO_ENABLED=0 go build -o intact ./cmd/intact
|
|
837
|
+
|
|
838
|
+
# Run intact with a specified SQLite database:
|
|
839
|
+
intact -db /opt/intact/intact.db -addr 127.0.0.1:20142
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
#### Initial Setup for intact:
|
|
843
|
+
1. **Enroll TOTP:** Run `intact -db /opt/intact/intact.db -show-totp` and add the setup key to Google Authenticator, 1Password, or Aegis.
|
|
844
|
+
2. **Sign In to Dashboard:** Open `http://127.0.0.1:20142`, enter the 6-digit TOTP code (zero password storage).
|
|
845
|
+
3. **Add Accounts:** Under **Providers → Add**, connect API keys (Groq, OpenRouter, Vertex, DeepSeek) or coding tool OAuth accounts (Claude Code, OpenAI Codex, Antigravity `agy`, GitHub Copilot).
|
|
846
|
+
4. **Create API Key:** Under **Endpoint → API keys → Create**, create a key starting with `sk-intact-...`.
|
|
847
|
+
5. **Connect from Switcher:** Open `switch ui` ➔ **Routes & Profiles** ➔ **+ Add Profile** ➔ Click **intact** or **agy on intact** template to auto-fill the optimized profile configuration.
|
|
795
848
|
|
|
796
|
-
|
|
849
|
+
> 📖 **Comprehensive Manual:** For full CLI flags, environment variables, systemd service units, Cloudflare Tunnel ingress configuration, and deep troubleshooting, open the Web UI (`switch ui`) and navigate to the **Docs & Guide** tab (`#/docs`).
|
|
797
850
|
|
|
851
|
+
#### Two Self-Improvement Loops:
|
|
798
852
|
- **intact corrects what providers refuse.** It records every provider error and groups the errors that recur. For a fake 429, intact replays the failing request and removes the system prompt text in halves. It keeps the smallest text that the provider refuses as a filter in its database. Every machine gets the fix at once, and this gateway needs no update. Two examples: Antigravity answered a fake 429 to "You are Codex, an agent based on GPT-5" and to "You are a Claude agent, built on Anthropic's Claude Agent SDK".
|
|
799
853
|
- **This gateway corrects what its converter loses.** With the contract lab on, the gateway sends masked samples to intact. intact compares each sample with the request that it received, and records each field that the conversion lost. `switch contract-check` writes one failing test for each finding, and the fix goes into the converter.
|
|
800
854
|
|
|
@@ -814,7 +868,8 @@ Because of this split, a provider fingerprint is never a rule in this gateway. I
|
|
|
814
868
|
| `LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS` | Idle time of such a call, from 1000 to 2147483647 ms (default 300000). Before the answer starts, agy gets a 504. After it starts, the connection is cut. |
|
|
815
869
|
| `LLM_SWITCHER_BIFROST_RETRY_MS` | Time before the gateway asks intact again after a failed Bifrost lookup (default 30000 ms). |
|
|
816
870
|
| `x-llm-profile: <key>` header (alias `x-profile`) or `?profile=<key>` | Route a single request through a specific profile. An unknown key returns HTTP 400 instead of silently falling back. |
|
|
817
|
-
| `profile.thinkingMode` | `auto` (default, for gateways like intact or 9Router): restore stripped thinking, inject a `<think>` guide for non-reasoning models, send `thinking` + `reasoning_effort
|
|
871
|
+
| `profile.thinkingMode` | `auto` (default, for gateways like intact or 9Router): restore stripped thinking, inject a `<think>` guide for non-reasoning models, send `thinking` + `reasoning_effort`, and send the earlier thinking as `reasoning_content` on assistant tool-call turns. `native` (strict OpenAI APIs): send only `reasoning_effort` when the client asks, never touch the prompt, use `max_completion_tokens`. Native mode never sends `reasoning_content` unless the profile sets `replayReasoning: true`. `off`: never send reasoning parameters. |
|
|
872
|
+
| `profile.replayReasoning` | `true` sends the earlier thinking as `reasoning_content` on assistant tool-call turns in `native` mode. DeepSeek and Kimi thinking mode need it. The default is `false`. `auto` mode always sends the field, and `off` never sends it. The model name and the client request do not change this rule. |
|
|
818
873
|
| `profile.thinkingEffort` | `auto` (default, caller/client controls effort), or an explicit override level: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `ultra`. Overrides client effort before routing upstream. Automatically maps to the nearest equivalent level accepted by the target upstream (e.g. OpenAI ceiling `high`, Anthropic `max`, Gemini budget bounds). In the Web UI, this selector dynamically hides with a guidance hint when Bifrost passthrough is active. |
|
|
819
874
|
| `profile.endpoints.countTokens` | Override the Anthropic `count_tokens` URL. |
|
|
820
875
|
| `profile.endpoints` | Override upstream URLs per format: `{ "openai-chat": "...", "anthropic": "...", "vertex": "https://.../models/{model}:{action}" }`. |
|
package/README.vi.md
CHANGED
|
@@ -592,6 +592,24 @@ thể nén prefix lớn hơn; các turn liên tiếp dùng lại prefix ổn đ
|
|
|
592
592
|
Khi dùng intact, profile cần API key intact được cấp quyền. User-Agent chỉ chọn đường protocol
|
|
593
593
|
gốc, không thay cho xác thực ở upstream.
|
|
594
594
|
|
|
595
|
+
### Goal check (Cơ chế Veto Độc Lập Bằng Jev System One)
|
|
596
|
+
|
|
597
|
+
Trong Claude Code, khi dùng lệnh `/goal`, hệ thống sử dụng một evaluator model chuyên biệt đọc
|
|
598
|
+
transcript và đánh giá điều kiện đã hoàn thành hay chưa. Tuy nhiên, evaluator chỉ đọc văn bản và dễ
|
|
599
|
+
bị ảo giác (hallucinate) — nhầm lẫn giữa việc *sửa báo cáo / rút finding* với việc *sửa code thật sự*
|
|
600
|
+
(false completion).
|
|
601
|
+
|
|
602
|
+
**Goal Check** chặn phản hồi của evaluator trước khi gửi về client, và gọi mô hình quyết định **Jev
|
|
603
|
+
(System One)** chấm điểm độc lập trên 3 trục:
|
|
604
|
+
- `complete` (>= 0.90): Toàn bộ công việc theo condition đã hoàn thành thực sự.
|
|
605
|
+
- `evidence` (>= 0.85): Có kết quả thực thi tool trực tiếp chứng minh (chạy test, compile, sửa file).
|
|
606
|
+
- `unfinished` (<= 0.10): Không còn công việc dang dở, lỗi chưa xử lý hoặc verification thiếu.
|
|
607
|
+
|
|
608
|
+
**Nguyên tắc Fail-Closed:** Gateway chỉ trả về `ok: true` khi **cả hai bên** (evaluator gốc và Jev)
|
|
609
|
+
đồng thuận. Mọi lỗi mạng, timeout, thiếu tool evidence hay schema không hợp lệ đều dẫn đến Veto
|
|
610
|
+
(`ok: false`) tự động, yêu cầu agent tiếp tục thực hiện và cung cấp bằng chứng rõ ràng. Cấu hình tại
|
|
611
|
+
tab **Goal Check** trên Web UI hoặc trường `goalCheck` trong `config.json`.
|
|
612
|
+
|
|
595
613
|
### Cập nhật
|
|
596
614
|
|
|
597
615
|
Khi npm có bản mới, dashboard hiện một thông báo. Bấm **Update now** trong thông báo này. Gateway cài bản mới và chạy code mới. Sau đó dashboard tự tải lại. `switch update` làm việc tương tự từ terminal.
|
|
@@ -742,7 +760,7 @@ route.
|
|
|
742
760
|
// Profile phục vụ Codex BẮT BUỘC có publicModels (xem Cấu hình ưu tiên Codex).
|
|
743
761
|
"publicModels": ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"], // tên chính thức cho main, review, subagent
|
|
744
762
|
"codexRoles": { "review": "gpt-5.6-sol" }, // không bắt buộc: ghép một vai trò với tên public khác
|
|
745
|
-
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" }
|
|
763
|
+
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" }
|
|
746
764
|
},
|
|
747
765
|
"agy-default": {
|
|
748
766
|
"name": "agy qua intact",
|
|
@@ -756,6 +774,20 @@ route.
|
|
|
756
774
|
}
|
|
757
775
|
},
|
|
758
776
|
"debug": false,
|
|
777
|
+
"idleCompact": {
|
|
778
|
+
"enabled": false,
|
|
779
|
+
"codex": false,
|
|
780
|
+
"idleMinutes": 15,
|
|
781
|
+
"minBytes": 65536,
|
|
782
|
+
"model": ""
|
|
783
|
+
},
|
|
784
|
+
"goalCheck": {
|
|
785
|
+
"enabled": false,
|
|
786
|
+
"backend": "jev",
|
|
787
|
+
"baseURL": "https://intact.example.com/v1",
|
|
788
|
+
"apiKey": "sk-...",
|
|
789
|
+
"model": "typesafe/jev-latest"
|
|
790
|
+
},
|
|
759
791
|
"contractLab": {
|
|
760
792
|
"url": "https://intact.example.com",
|
|
761
793
|
"apiKey": "sk-...",
|
|
@@ -785,10 +817,33 @@ Trước khi gửi mẫu, gateway che mọi giá trị string bằng chuỗi `x`
|
|
|
785
817
|
- `switch contract-probe [--model m]` gửi sáu request thử cho mỗi model và mỗi format qua gateway.
|
|
786
818
|
- `switch contract-check` lấy các finding còn mở từ intact và ghi một file test cho mỗi field bị mất.
|
|
787
819
|
|
|
788
|
-
### Tự cải thiện cùng intact
|
|
820
|
+
### Tự cải thiện & Vận hành cùng intact (Upstream Pooler khuyên dùng)
|
|
821
|
+
|
|
822
|
+
[intact](https://github.com/louisphamdev/intact) là proxy giữ credential và pool đa tài khoản mà gateway này khuyên dùng làm upstream. Hai công cụ tự tìm và tự sửa lỗi của nhau theo hai vòng khép kín.
|
|
823
|
+
|
|
824
|
+
#### Cài đặt nhanh intact
|
|
825
|
+
```bash
|
|
826
|
+
# Cài đặt toàn cục qua npm:
|
|
827
|
+
npm install -g intact-gateway
|
|
828
|
+
|
|
829
|
+
# Hoặc biên dịch từ nguồn Go (Zero-CGO, Pure-Go SQLite):
|
|
830
|
+
git clone https://github.com/louisphamdev/intact.git && cd intact
|
|
831
|
+
CGO_ENABLED=0 go build -o intact ./cmd/intact
|
|
832
|
+
|
|
833
|
+
# Khởi chạy intact với SQLite database chỉ định:
|
|
834
|
+
intact -db /opt/intact/intact.db -addr 127.0.0.1:20142
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
#### Thiết lập ban đầu cho intact:
|
|
838
|
+
1. **Lấy mã TOTP:** Chạy `intact -db /opt/intact/intact.db -show-totp` và quét/nhập setup key vào Google Authenticator, 1Password hoặc Aegis.
|
|
839
|
+
2. **Đăng nhập Dashboard:** Mở trình duyệt tại `http://127.0.0.1:20142`, nhập mã 6 số TOTP (bảo mật tuyệt đối, không dùng mật khẩu văn bản).
|
|
840
|
+
3. **Thêm tài khoản:** Vào mục **Providers → Add**, thêm các nhà cung cấp API key (Groq, OpenRouter, Vertex, DeepSeek) hoặc OAuth coding tools (Claude Code, OpenAI Codex, Antigravity `agy`, GitHub Copilot).
|
|
841
|
+
4. **Tạo API key:** Vào **Endpoint → API keys → Create**, tạo key mới dạng `sk-intact-...`.
|
|
842
|
+
5. **Định tuyến trên LLM Switcher:** Mở `switch ui` ➔ Tab **Routes & Profiles** ➔ Bấm **+ Add Profile** ➔ Chọn template **intact** hoặc **agy on intact** để tự động điền cấu hình tối ưu.
|
|
789
843
|
|
|
790
|
-
|
|
844
|
+
> 📖 **Cẩm nang toàn diện:** Để xem toàn bộ bảng tra cứu CLI flags, biến môi trường, file service systemd, cấu hình Cloudflare Tunnel và hướng dẫn vận hành chi tiết nhất, hãy mở Web UI (`switch ui`) và chuyển sang tab **Docs & Guide** (`#/docs`).
|
|
791
845
|
|
|
846
|
+
#### 2 Vòng tự cải thiện giữa Switcher và intact:
|
|
792
847
|
- **intact sửa những gì provider từ chối.** intact ghi lại mọi lỗi của provider và gom các lỗi lặp lại thành nhóm. Với 429 giả, intact gửi lại request lỗi và bỏ dần từng nửa system prompt. Đoạn nhỏ nhất mà provider từ chối được lưu thành filter trong database của intact. Mọi máy nhận bản sửa ngay, gateway này không cần cập nhật. Hai ví dụ: Antigravity trả 429 giả cho "You are Codex, an agent based on GPT-5" và cho "You are a Claude agent, built on Anthropic's Claude Agent SDK".
|
|
793
848
|
- **Gateway này sửa những gì converter làm mất.** Khi bật contract lab, gateway gửi các mẫu đã che lên intact. intact so mỗi mẫu với request mà intact nhận được, rồi ghi lại mỗi field mà phép chuyển đổi làm mất. `switch contract-check` ghi một test đỏ cho mỗi finding, và bản sửa nằm trong converter.
|
|
794
849
|
|
|
@@ -808,7 +863,8 @@ Nhờ cách chia này, fingerprint của provider không bao giờ là rule tron
|
|
|
808
863
|
| `LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS` | Thời gian chờ không có dữ liệu của lệnh gọi đó, từ 1000 tới 2147483647 ms (mặc định 300000). Trước khi câu trả lời bắt đầu, agy nhận 504. Sau khi đã bắt đầu, kết nối bị cắt. |
|
|
809
864
|
| `LLM_SWITCHER_BIFROST_RETRY_MS` | Thời gian trước khi gateway hỏi lại intact sau một lần tra cứu Bifrost thất bại (mặc định 30000 ms). |
|
|
810
865
|
| Header `x-llm-profile: <key>` (tên khác `x-profile`) hoặc `?profile=<key>` | Định tuyến riêng 1 request qua profile chỉ định. Key không tồn tại trả HTTP 400 thay vì âm thầm dùng profile khác. |
|
|
811
|
-
| `profile.thinkingMode` | `auto` (mặc định, cho gateway như intact hoặc 9Router): phục hồi thinking bị xoá, inject hướng dẫn `<think>` cho model không có reasoning, gửi `thinking` + `reasoning_effort
|
|
866
|
+
| `profile.thinkingMode` | `auto` (mặc định, cho gateway như intact hoặc 9Router): phục hồi thinking bị xoá, inject hướng dẫn `<think>` cho model không có reasoning, gửi `thinking` + `reasoning_effort`, và gửi lại thinking trước đó thành `reasoning_content` ở lượt assistant có tool call. `native` (API OpenAI nghiêm ngặt): chỉ gửi `reasoning_effort` khi client yêu cầu, không sửa prompt, dùng `max_completion_tokens`; không gửi `reasoning_content` trừ khi profile đặt `replayReasoning: true`. `off`: không bao giờ gửi tham số reasoning. |
|
|
867
|
+
| `profile.replayReasoning` | `true`: ở chế độ `native`, gửi lại thinking trước đó thành `reasoning_content` ở lượt assistant có tool call. Thinking mode của DeepSeek và Kimi cần field này. Mặc định là `false`. `auto` luôn gửi, `off` không bao giờ gửi. Tên model và request của client không đổi quy tắc này. |
|
|
812
868
|
| `profile.thinkingEffort` | `auto` (mặc định, client tự quyết định), hoặc đặt cố định mức override: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `ultra`. Ghi đè mức reasoning effort từ công cụ trước khi gửi lên upstream. Tự động ánh xạ về mức tương đương gần nhất mà upstream đích hỗ trợ (OpenAI trần `high`, Anthropic trần `max`, Gemini điều chỉnh `thinkingBudget`). Trên Web UI, tùy chọn này tự động ẩn cùng thông báo gợi ý khi profile bật chế độ Bifrost passthrough. |
|
|
813
869
|
| `profile.endpoints.countTokens` | Ghi đè URL `count_tokens` của Anthropic. |
|
|
814
870
|
| `profile.endpoints` | Ghi đè URL upstream theo từng format: `{ "openai-chat": "...", "anthropic": "...", "vertex": "https://.../models/{model}:{action}" }`. |
|