llm-switcher 1.1.11 → 1.2.2
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 +63 -0
- package/README.md +202 -257
- package/README.vi.md +200 -256
- package/blindfold/blindfold.mjs +200 -53
- package/blindfold/make-certs.sh +26 -7
- package/catalog.mjs +246 -0
- package/classifier.mjs +238 -0
- package/config.example.json +12 -34
- package/docs/codex-blindfold.md +28 -17
- package/docs/cross-platform.md +16 -7
- package/docs/diagrams/ir-healer-pipeline.mmd +16 -0
- package/docs/diagrams/ir-healer-pipeline.png +0 -0
- package/docs/diagrams/ir-healer-pipeline.svg +90 -0
- package/docs/diagrams/ir-translation-pipeline.html +14925 -0
- package/docs/diagrams/ir-translation-pipeline.sequence.json +31 -0
- package/docs/diagrams/ir-translation-pipeline.svg +5128 -0
- package/docs/diagrams/system-architecture.architecture.json +76 -0
- package/docs/diagrams/system-architecture.html +14978 -0
- package/docs/diagrams/system-architecture.svg +5147 -0
- package/docs/diagrams/system-topology.mmd +30 -0
- package/docs/diagrams/system-topology.png +0 -0
- package/docs/diagrams/system-topology.svg +125 -0
- package/ensure-ca-bundle.mjs +28 -0
- package/formats.mjs +43 -156
- package/icons/antigravity.png +0 -0
- package/icons/claude.png +0 -0
- package/icons/codex.png +0 -0
- package/icons/deepseek.png +0 -0
- package/icons/gemini.png +0 -0
- package/icons/github.png +0 -0
- package/icons/groq.png +0 -0
- package/icons/intact.svg +1 -0
- package/icons/ollama.png +0 -0
- package/icons/openai.png +0 -0
- package/icons/openrouter.png +0 -0
- package/icons/qwen.png +0 -0
- package/icons/vertex.png +0 -0
- package/mcp.mjs +39 -11
- package/package.json +1 -1
- package/proxy.mjs +114 -37
- package/shim.mjs +200 -57
- package/skills/llm-switcher/SKILL.md +15 -10
- package/state.mjs +1100 -191
- package/switch.cmd +2 -2
- package/switch.mjs +228 -53
- package/tests/blindfold-e2e.test.mjs +380 -0
- package/tests/blindfold-task5.test.mjs +429 -0
- package/tests/blindfold.task3.test.mjs +700 -0
- package/tests/blindfold.test.mjs +10 -5
- package/tests/catalog.test.mjs +147 -0
- package/tests/classifier.test.mjs +210 -0
- package/tests/contract-lab.test.mjs +22 -7
- package/tests/formats.test.mjs +63 -46
- package/tests/gateway.e2e.test.mjs +136 -36
- package/tests/lifecycle.test.mjs +16 -10
- package/tests/mcp.test.mjs +78 -2
- package/tests/real-user-sim.test.mjs +464 -0
- package/tests/shim.test.mjs +159 -66
- package/tests/state.test.mjs +975 -193
- package/tests/switch.test.mjs +446 -2
- package/ui.html +1710 -1726
package/README.vi.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<b>Cổng ngõ biên (Edge Gateway) chuyển đổi đa giao thức LLM siêu nhẹ, Zero-Dependency</b><br>
|
|
5
5
|
Cầu nối hai chiều giữa <b>Claude Code</b>, <b>Codex</b>, OpenAI SDKs, Gemini/Vertex SDKs với mọi nhà cung cấp LLM.<br>
|
|
6
|
-
Chuyển đổi giao thức qua IR,
|
|
6
|
+
Chuyển đổi giao thức qua IR, cửa sổ context theo model chính thức, trích xuất thinking blocks và tự chữa lành đồ thị tin nhắn trước khi ra Internet.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
@@ -13,162 +13,118 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<img src="https://img.shields.io/badge/Node.js-18%2B-22c55e?logo=node.js&logoColor=white" alt="Node.js 18+">
|
|
15
15
|
<img src="https://img.shields.io/badge/Phụ_thuộc-Zero_Dependencies-38bdf8" alt="Zero Dependencies">
|
|
16
|
-
<img src="https://img.shields.io/badge/Context-
|
|
16
|
+
<img src="https://img.shields.io/badge/Context-window_follows_the_model-6366f1" alt="Context window follows the model">
|
|
17
17
|
<img src="https://img.shields.io/badge/Multi--Active-Đa_CLI_Độc_Lập-f59e0b" alt="Multi-Active">
|
|
18
18
|
<img src="https://img.shields.io/badge/Giấy_phép-MIT-gray" alt="License MIT">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
> ###
|
|
23
|
+
> ### 🛡️ Zero-Loss Native Emulation Cho Coding Agent
|
|
24
24
|
>
|
|
25
|
-
>
|
|
26
|
-
> - **Anthropic** bắt buộc phải có các block `thinking` riêng biệt (`thinking_delta` + `signature_delta`), luật xen kẽ lượt nghiêm ngặt (`roles must alternate`), và schema `tool_use` có định kiểu.
|
|
27
|
-
> - **OpenAI** stream reasoning qua các chunk delta `reasoning_content` hoặc `reasoning_details[]`, và định dạng tool thành `tool_calls` chứa chuỗi JSON arguments.
|
|
28
|
-
> - **Google Vertex AI** đặt khối suy luận vào `candidates[0].content.parts[{thought: true, text, thoughtSignature}]` và truyền arguments dạng object thuần.
|
|
29
|
-
> - **Các model mã nguồn mở (DeepSeek, Qwen, GLM)** thường đổ thẳng chain-of-thought vào nội dung `content`, hoặc trả trùng lặp nhiều trường gây rối loạn parser.
|
|
25
|
+
> Proxy thông thường làm biến dạng API response: Claude Code mất block reasoning `thinking_delta`, argument tool bị cắt vụn, và prompt cache bị lệch.
|
|
30
26
|
>
|
|
31
|
-
> **
|
|
32
|
-
>
|
|
33
|
-
>
|
|
34
|
-
>
|
|
35
|
-
>
|
|
36
|
-
> Nhiều lập trình viên lầm tưởng model AI "ngày càng ngáo đi", nhưng thực chất là **do proxy trung gian đã làm biến dạng cấu trúc response!**
|
|
37
|
-
>
|
|
38
|
-
> ### 🛡️ Giải pháp: Giả Lập Chuẩn Gốc Không Hao Hụt (Zero-Loss Native Emulation)
|
|
39
|
-
>
|
|
40
|
-
> **LLM Switcher giải quyết triệt để bài toán này bằng cơ chế giả lập giao thức chuẩn xác 100%.**
|
|
41
|
-
>
|
|
42
|
-
> Dù upstream phía sau của bạn là 9Router, OpenRouter, Vertex hay DeepSeek, Switcher sẽ chuẩn hoá và tái tạo lại **chính xác từng byte event stream theo đúng chuẩn mà client đó được thiết kế để tiếp nhận**:
|
|
43
|
-
> - **Claude Code** nhận về 100% luồng Anthropic SSE xịn (`message_start` ➔ `thinking_delta` ➔ `signature_delta` ➔ `tool_use` ➔ `message_delta`), hoạt động **mượt mà y hệt như đang dùng gói thuê bao chính chủ đắt đỏ**.
|
|
44
|
-
> - **Codex** nhận về 100% luồng Responses API xịn (`response.created` ➔ `output_text.delta` ➔ `function_call` ➔ `response.completed`).
|
|
45
|
-
>
|
|
46
|
-
> **Bạn vừa được hưởng lợi ích chi phí và độ phủ 1M context của các API bên thứ ba, vừa giữ trọn 100% trí thông minh và sức mạnh của công cụ như dùng gói subscription gốc.**
|
|
27
|
+
> **LLM Switcher giải quyết triệt để vấn đề này ngay tại network edge cục bộ:**
|
|
28
|
+
> - **100% Native Emulation:** Chuẩn hóa upstream API (intact, 9Router, Vertex, DeepSeek) thành luồng Anthropic SSE xịn (`thinking_delta` + `tool_use`) cho Claude Code, và Responses API event cho Codex.
|
|
29
|
+
> - **Client-Side Edge Companion:** Cố tình tách biệt các tác vụ nặng như account pooling, key rotation cho **[intact](https://github.com/louisphamdev/intact)** (khuyên dùng) hoặc 9Router (cơ bản), giúp Switcher giữ vững tiêu chí Zero-Dependency siêu nhẹ.
|
|
30
|
+
> - **Phạm vi Tập trung:** Tối ưu chuyên sâu cho **Claude Code** và **OpenAI Codex** (OpenCode đã hỗ trợ đổi model native ngay trong config; muốn pooling thì dùng intact; còn Antigravity thì không đáng để bận tâm làm 😏).
|
|
47
31
|
|
|
48
32
|
---
|
|
49
33
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
>
|
|
55
|
-
>
|
|
56
|
-
|
|
57
|
-
>
|
|
58
|
-
>
|
|
59
|
-
>
|
|
60
|
-
>
|
|
34
|
+
## Kiến trúc & Sơ đồ Trực quan (Interactive Diagrams)
|
|
35
|
+
|
|
36
|
+
LLM Switcher lắng nghe cục bộ trên máy bạn (`127.0.0.1:3456`), đóng vai trò là transparent edge interceptor và protocol bridge.
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="docs/diagrams/system-architecture.html">
|
|
40
|
+
<img src="docs/diagrams/system-topology.svg" alt="LLM Switcher System Topology & Architecture" width="100%">
|
|
41
|
+
</a>
|
|
42
|
+
<br>
|
|
43
|
+
<sub><i>🎨 Theme Pretty-Mermaid (Tokyo Night). Click vào ảnh để mở trình xem tương tác Archify HTML (zoom, pan, tracing).</i></sub>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
### 1. Thư viện Sơ đồ Tương tác Archify
|
|
47
|
+
|
|
48
|
+
Toàn bộ sơ đồ kiến trúc và luồng xử lý được biên soạn bằng **[Archify](https://github.com/tt-a1i/archify)** và render bằng **[Pretty-Mermaid](https://github.com/imxv/Pretty-mermaid-skills)**:
|
|
49
|
+
|
|
50
|
+
| Sơ đồ | Mô tả luồng | Bản đồ Tương tác (HTML) | Vector Sắc Nét |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| **System Topology** | Kiến trúc tổng thể: Client CLIs ➔ Optimizer ➔ Gateway & Healer Core ➔ Upstream Providers | [📊 Mở Sơ đồ](docs/diagrams/system-architecture.html) | [SVG](docs/diagrams/system-topology.svg) • [PNG](docs/diagrams/system-topology.png) |
|
|
53
|
+
| **IR Healer Pipeline** | Chuẩn hoá request, tự sửa schema lỗi, tổng hợp stream SSE và cơ chế abort | [🔄 Mở Sơ đồ](docs/diagrams/ir-translation-pipeline.html) | [SVG](docs/diagrams/ir-healer-pipeline.svg) • [PNG](docs/diagrams/ir-healer-pipeline.png) |
|
|
54
|
+
| **Codex Blindfold Routing** | Luồng TLS CONNECT proxy, bóc tách credential và định tuyến an toàn | [🛡️ Mở Sơ đồ](docs/diagrams/blindfold-request-routing.html) | [HTML](docs/diagrams/blindfold-request-routing.html) |
|
|
55
|
+
| **Switch Lifecycle** | Vòng đời chuyển đổi profile không downtime, CAS config và sync interceptor | [⚡ Mở Sơ đồ](docs/diagrams/blindfold-switch-lifecycle.html) | [HTML](docs/diagrams/blindfold-switch-lifecycle.html) |
|
|
61
56
|
|
|
62
57
|
---
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
### 2. Vòng đời Request & Pipeline Healer Engine
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
<p align="center">
|
|
62
|
+
<a href="docs/diagrams/ir-translation-pipeline.html">
|
|
63
|
+
<img src="docs/diagrams/ir-healer-pipeline.svg" alt="Bi-Directional IR Healer Pipeline" width="100%">
|
|
64
|
+
</a>
|
|
65
|
+
<br>
|
|
66
|
+
<sub><i>💡 Click vào sơ đồ phía trên để kiểm tra chi tiết chuỗi sequence tương tác.</i></sub>
|
|
67
|
+
</p>
|
|
67
68
|
|
|
68
|
-
###
|
|
69
|
+
### 3. Sơ đồ Luồng Tổng quát
|
|
69
70
|
|
|
70
71
|
```mermaid
|
|
71
|
-
flowchart
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
flowchart LR
|
|
73
|
+
classDef client fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
|
|
74
|
+
classDef edge fill:#0f172a,stroke:#6366f1,stroke-width:2px,color:#f8fafc;
|
|
75
|
+
classDef healer fill:#064e3b,stroke:#10b981,stroke-width:2px,color:#f8fafc;
|
|
76
|
+
classDef upstream fill:#2e1065,stroke:#a855f7,stroke-width:2px,color:#f8fafc;
|
|
77
|
+
classDef opt fill:#1e1b4b,stroke:#818cf8,stroke-dasharray: 4 4,color:#e0e7ff;
|
|
78
|
+
|
|
79
|
+
subgraph Clients[" 💻 Dev Clients & Coding CLIs "]
|
|
80
|
+
CC["Claude Code CLI\n(/v1/messages)"]:::client
|
|
81
|
+
CDX["OpenAI Codex CLI\n(/v1/responses)"]:::client
|
|
77
82
|
end
|
|
78
83
|
|
|
79
|
-
subgraph
|
|
80
|
-
OPT["
|
|
84
|
+
subgraph Middle[" ⚡ Optional Middle-Layer "]
|
|
85
|
+
OPT["Token Optimizers\n(Headroom / RTK)"]:::opt
|
|
81
86
|
end
|
|
82
87
|
|
|
83
|
-
subgraph
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
M1M["Mở khoá 1M Context\n& Tự tính ngưỡng Auto-Compact"]
|
|
89
|
-
LOGS["Live Inspector\n(Ring Buffer lưu RAM thời gian thực)"]
|
|
90
|
-
ROUTER --> HEALER --> IR --> M1M --> LOGS
|
|
88
|
+
subgraph Gateway[" 🛡️ LLM Switcher Edge Gateway (:3456) "]
|
|
89
|
+
ROUTER["Edge Router\n(Zero-Mutation)"]:::edge
|
|
90
|
+
HEALER["Healer Engine\n(Auto-Fix Schemas)"]:::healer
|
|
91
|
+
IR["Bi-Directional IR\n(Event Synth)"]:::healer
|
|
92
|
+
ROUTER --> HEALER --> IR
|
|
91
93
|
end
|
|
92
94
|
|
|
93
|
-
subgraph
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
ANT["Anthropic Native API"]
|
|
97
|
-
GCP["Google Vertex AI / Gemini"]
|
|
95
|
+
subgraph Upstreams[" ☁️ Upstream Providers "]
|
|
96
|
+
INTACT["intact Gateway\n(Recommended Pooler)"]:::upstream
|
|
97
|
+
OTHER["9Router / Vertex / Other"]:::upstream
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
CC -->|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
CDX -.->|
|
|
104
|
-
|
|
105
|
-
VTX --> ROUTER
|
|
106
|
-
OPT -->|Chuyển tiếp về Switcher| ROUTER
|
|
107
|
-
|
|
108
|
-
LOGS -->|Request đã chuẩn hoá| R9
|
|
109
|
-
LOGS -->|Request đã chuẩn hoá| OR
|
|
110
|
-
LOGS -->|Request đã chuẩn hoá| ANT
|
|
111
|
-
LOGS -->|Request đã chuẩn hoá| GCP
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
### 2. Pipeline Chuyển đổi Giao thức qua IR & Healer Engine
|
|
100
|
+
CC -->|direct| ROUTER
|
|
101
|
+
CDX -->|direct| ROUTER
|
|
102
|
+
CC -.->|prune| OPT
|
|
103
|
+
CDX -.->|prune| OPT
|
|
104
|
+
OPT -->|forward| ROUTER
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
autonumber
|
|
121
|
-
actor CLI as Client (Claude Code / Codex / SDK)
|
|
122
|
-
participant GW as LLM Switcher (:3456)
|
|
123
|
-
participant IR as IR & Healer Engine
|
|
124
|
-
participant UP as Upstream (9Router / Anthropic / Vertex)
|
|
125
|
-
|
|
126
|
-
CLI->>GW: Gửi request (Anthropic, Responses, Chat hoặc Vertex)
|
|
127
|
-
Note over GW,IR: Chuẩn hoá về IR (Intermediate Representation)
|
|
128
|
-
GW->>IR: parseToIR(clientFormat, payload)
|
|
129
|
-
Note over IR: Healer Engine kiểm tra & nắn chỉnh:<br/>1. Biến tool_result mồ côi thành text block ngữ cảnh<br/>2. Tự khôi phục tham số thinking nếu tool ngoài cắt mất<br/>3. Gộp các turn user liên tiếp (chống lỗi 400)<br/>4. Kích hoạt ngưỡng 1M context
|
|
130
|
-
IR->>GW: emitUpstreamBody(outFormat, healedIR)
|
|
131
|
-
GW->>UP: Gọi API Upstream (fetch kèm AbortSignal)
|
|
132
|
-
UP-->>GW: Trả về SSE Stream / JSON Chunks
|
|
133
|
-
Note over GW: normalizeUpstream(chunk)<br/>Bóc tách reasoning_content, tag <think>, tính usage
|
|
134
|
-
GW->>CLI: Render stream chuẩn theo giao thức của Client (ví dụ: thinking_delta + text_delta)
|
|
135
|
-
Note over CLI,GW: Khi Client ngắt kết nối (Ctrl+C) -> Switcher lập tức abort Upstream (tiết kiệm token!)
|
|
106
|
+
IR -->|contract & pool| INTACT
|
|
107
|
+
IR -->|standard call| OTHER
|
|
136
108
|
```
|
|
137
109
|
|
|
138
110
|
---
|
|
139
111
|
|
|
140
|
-
###
|
|
112
|
+
### 4. Cơ chế "Bắt cóc" & Chuyển đổi Request Diễn Ra Như Thế Nào?
|
|
141
113
|
|
|
142
|
-
|
|
114
|
+
LLM Switcher hoạt động như một lớp trung gian mạng trong suốt (transparent network proxy) mà tuyệt đối không sửa file cấu hình của công cụ:
|
|
143
115
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
C2["Codex CLI\n(/v1/responses)"]
|
|
149
|
-
C3["OpenAI SDK\n(/v1/chat/completions)"]
|
|
150
|
-
C4["Vertex SDK\n(/v1beta/models/*)"]
|
|
151
|
-
end
|
|
116
|
+
1. **Shim kích hoạt cục bộ trong RAM:** Khi bạn gõ lệnh `claude` hoặc `codex`, file shim nằm đầu `PATH` sẽ chạy trước. Shim nạp tạm thời `HTTPS_PROXY=http://127.0.0.1:3457` và chứng chỉ CA *chỉ trong bộ nhớ của tiến trình đó*, hoàn toàn không chạm vào `~/.claude/settings.json` hay `~/.codex/config.toml`.
|
|
117
|
+
2. **Blindfold Interceptor chặn ở tầng mạng (`:3457`):** Công cụ gửi request HTTPS tới domain chính hãng (`api.anthropic.com` hoặc `api.openai.com`). Interceptor giải mã TLS cục bộ, bóc sạch token cũ của client, và chuyển tiếp các đường dẫn API (`/v1/messages`, `/v1/responses`, `/v1/models`) về Gateway nội bộ (`:3456`). Các traffic khác (đăng nhập OAuth, GitHub, web search...) được tunnel nguyên vẹn ra Internet thật.
|
|
118
|
+
3. **Chuyển đổi giao thức & Gọi Upstream (`:3456`):** Gateway đọc profile được kích hoạt trong `config.json`, kích hoạt Healer Engine (tự sửa schema rỗng `{}`, cứu `tool_result` mồ côi, phục hồi thinking budget), rồi đóng gói request sang định dạng của provider được cấu hình (Gemini, OpenAI Chat, Anthropic...) với `baseURL` và `apiKey` tương ứng.
|
|
119
|
+
4. **Tái tạo Response chuẩn Native:** Khi provider phản hồi stream về, Gateway bóc tách reasoning token và tool call, tổng hợp lại thành 100% genuine Anthropic SSE (`thinking_delta` + `tool_use`) hoặc Codex Responses events. Công cụ nhận được response chuẩn chỉ và tin rằng nó vừa nói chuyện trực tiếp với server chính hãng!
|
|
152
120
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
subgraph Egress["Đích Upstream tương ứng"]
|
|
161
|
-
U1["9Router (Mở 1M Context Opus)"]
|
|
162
|
-
U2["OpenRouter (Sonnet Thinking)"]
|
|
163
|
-
U3["Local OpenAI Server (:8000)"]
|
|
164
|
-
U4["Google Cloud Endpoint"]
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
C1 --> SLOT1 --> U1
|
|
168
|
-
C2 --> SLOT2 --> U2
|
|
169
|
-
C3 --> SLOT3 --> U3
|
|
170
|
-
C4 --> SLOT4 --> U4
|
|
171
|
-
```
|
|
121
|
+
> #### 🔒 Độ An Toàn & Nguồn Gốc Chứng Chỉ CA: CA từ đâu ra và an toàn thế nào?
|
|
122
|
+
>
|
|
123
|
+
> - **Tự sinh 100% tại máy cục bộ:** File chứng chỉ (`ca.pem`) và private key (`ca.key`) được sinh trực tiếp trên chính máy tính của bạn bằng OpenSSL nội bộ (`blindfold/make-certs.sh`). Tuyệt đối không tải bất kỳ chứng chỉ nào từ internet về, private key được lưu với quyền bảo mật nghiêm ngặt `0600`.
|
|
124
|
+
> - **Không can thiệp vào System Trust Store của hệ điều hành:** Khác với các công cụ bắt proxy như Charles hay Fiddler, LLM Switcher **tuyệt đối KHÔNG cài đặt chứng chỉ vào OS Root Store** (không đụng vào Windows Certificate Manager, macOS Keychain hay Linux `/etc/ssl/certs`). Bạn **không cần quyền Administrator hay sudo**.
|
|
125
|
+
> - **Chỉ tin cậy trong phạm vi tiến trình (Process-Scoped):** Chứng chỉ CA chỉ được nạp tạm thời vào bộ nhớ của `claude` (qua `NODE_EXTRA_CA_CERTS`) và `codex` (qua `CODEX_CA_CERTIFICATE`). Trình duyệt web (Chrome, Edge), ứng dụng ngân hàng, git và các app khác trên máy hoàn toàn không biết và không tin cậy chứng chỉ này.
|
|
126
|
+
> - **Giới hạn tên miền bằng mật mã học (Name Constraints):** Chứng chỉ CA được cấu hình thuộc tính X.509 `nameConstraints` bắt buộc, chỉ cho phép ký duy nhất cho 3 domain: `api.anthropic.com`, `api.openai.com`, và `chatgpt.com`. Dù có ai đánh cắp được private key, các trình xác thực TLS chuẩn sẽ lập tức từ chối chứng chỉ này đối với mọi trang web khác (Google, GitHub, ngân hàng...).
|
|
127
|
+
> - **Bảo toàn chứng chỉ VPN / Doanh nghiệp:** Nếu máy bạn đã có sẵn chứng chỉ proxy công ty trong `NODE_EXTRA_CA_CERTS`, script `ensure-ca-bundle.mjs` sẽ tự động gộp cả 2 chứng chỉ vào một bundle tạm thời, không bao giờ ghi đè làm hỏng mạng nội bộ công ty bạn.
|
|
172
128
|
|
|
173
129
|
---
|
|
174
130
|
|
|
@@ -184,68 +140,21 @@ flowchart LR
|
|
|
184
140
|
- Khắc phục lỗi mồ côi `tool_result` do các công cụ nén token (RTK, Headroom, Ponytail) vô tình cắt mất turn `assistant` phía trước $\implies$ chống lỗi `HTTP 400 Bad Request`.
|
|
185
141
|
- Tự động bù lại tham số `thinking` nếu tool ngoài cắt mất trên các reasoning model.
|
|
186
142
|
- Gộp các turn cùng role liên tiếp để đáp ứng nghiêm ngặt luật xen kẽ lượt nói của Anthropic.
|
|
187
|
-
- **
|
|
188
|
-
- **Không làm bẩn `settings.json` (Zero Config Mutation):**
|
|
143
|
+
- **Context window theo model chính thức:** switcher không còn ép cửa sổ 1M hay ngưỡng auto-compact, và không ghi tên model nào vào môi trường của bạn. Claude Code tự ước lượng phiên theo cửa sổ của model bạn chọn; backend có cửa sổ nhỏ hơn model đó có thể tràn trong phiên dài. `model1M` giờ chỉ quyết định `/v1/models` liệt kê gì.
|
|
144
|
+
- **Không làm bẩn `settings.json` (Zero Config Mutation):** Không bao giờ đọc hay ghi `~/.claude/settings.json` hay `~/.codex/config.toml`, và không ghi biến môi trường nào cũng không thêm tham số `--config` nào mà công cụ đọc như cấu hình. Công cụ chỉ đến gateway qua interceptor, nên không hiện banner cảnh báo của nhà cung cấp.
|
|
189
145
|
- **Live Request / Response Inspector:** Bảng theo dõi thời gian thực ngay trên Web UI: xem độ trễ, token prompt/output, preview prompt câu hỏi và khối suy luận thinking.
|
|
190
146
|
- **Cài đặt Daemon Service nền:** Cung cấp lệnh cài đặt gateway chạy ngầm tự khởi động cùng hệ điều hành trên Windows (Task Scheduler), macOS (launchd) và Linux (systemd).
|
|
191
147
|
|
|
192
148
|
---
|
|
193
149
|
|
|
194
|
-
## Thay đổi
|
|
195
|
-
|
|
196
|
-
- **README.** Mục mới "Tự cải thiện cùng intact" giải thích cách gateway này và intact tự sửa lỗi của nhau. intact giờ đã public và có trên npm với tên `intact-gateway`.
|
|
197
|
-
|
|
198
|
-
### Thay đổi trong bản 1.1.9
|
|
150
|
+
## Thay đổi gần đây (v1.2.0)
|
|
199
151
|
|
|
200
|
-
- **
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
- **
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
### Thay đổi trong bản 1.1.7
|
|
208
|
-
|
|
209
|
-
- **Tool của Codex.** Khi có `publicModels`, Codex mất hết tool và dừng sau một câu trả lời. Model catalog chép metadata của một model OpenAI thật, và metadata này đưa Codex sang dạng "Responses Lite". Giờ catalog giữ Codex ở chế độ tool trực tiếp, và gateway cũng đọc tool gửi đến dưới dạng input item `additional_tools`.
|
|
210
|
-
|
|
211
|
-
### Thay đổi trong bản 1.1.6
|
|
212
|
-
|
|
213
|
-
- **Codex qua WebSocket.** Gateway giữ các lượt của mỗi phiên WebSocket. Lượt nào gửi `previous_response_id` sẽ nhận lại các lượt trước, nên Codex không còn mất nhiệm vụ sau lần gọi tool đầu tiên. Id không tồn tại làm lượt đó lỗi với `previous_response_not_found`.
|
|
214
|
-
- **Warmup của Codex.** Frame `response.create` có `generate: false` được trả lời ngay tại máy. Frame này không còn tốn một lần gọi model.
|
|
215
|
-
- **Tên model cho Codex.** Profile không có `publicModels` không còn gửi `OpenAI-Model: main` trong handshake, và `switch codex` cùng `switch doctor` cảnh báo trường hợp này. Codex đọc `main` là bị chuyển model và hiện cảnh báo sai "high-risk cyber activity". Xem mục "Cấu hình ưu tiên Codex".
|
|
216
|
-
- **Contract lab.** Gateway gửi mỗi mẫu bằng đúng key đã mở trace của mẫu đó. intact từ chối các lần gửi của bản 1.1.5 với `HTTP 404 trace not found`.
|
|
217
|
-
- **Dấu phiên bản.** Dấu phiên bản chỉ dùng commit cuối khi bản checkout không có thay đổi. Nếu có thay đổi, dấu dùng thời gian file mới nhất.
|
|
218
|
-
|
|
219
|
-
### Thay đổi trong bản 1.1.5
|
|
220
|
-
|
|
221
|
-
- **Bảo mật contract lab.** Việc che giờ dùng allowlist. Mọi giá trị string đều bị che, trừ các giá trị enum mà intact đọc. Bản 1.1.4 che theo danh sách key nội dung và bỏ sót 16 field (trích dẫn, tiêu đề tài liệu và trang web, câu truy vấn web search, token logprobs, tên và URI file, stop sequence, tên người tham gia, thông báo lỗi, mô tả tool).
|
|
222
|
-
|
|
223
|
-
### Thay đổi trong bản 1.1.4
|
|
224
|
-
|
|
225
|
-
- **Bảo mật contract lab.** Mẫu mà gateway gửi lên intact không chứa nội dung của client. Prompt, câu trả lời, tham số và kết quả của tool, file và user id bị che ngay trên máy trước khi gửi. Các field mà intact cần để phân tích được giữ nguyên.
|
|
226
|
-
|
|
227
|
-
### Thay đổi trong bản 1.1.3
|
|
228
|
-
|
|
229
|
-
- **Dashboard.** Khi mở mà thiếu access token, trang không còn đứng ở "Checking status...". Trang báo đang bị khoá và chỉ lệnh `switch ui`, lệnh này mở trang kèm token.
|
|
230
|
-
- **Dashboard.** Tên model của Codex (session, review, subagent) nằm ở tab **Models**, cạnh các cấu hình model khác. Tab **Blindfold** chỉ còn cấu hình interceptor.
|
|
231
|
-
|
|
232
|
-
### Thay đổi trong bản 1.1.2
|
|
233
|
-
|
|
234
|
-
- **Gói npm.** Cài bằng `npm install -g llm-switcher` rồi chạy `switch`. Bản cài bằng npm lưu dữ liệu trong `~/.llm-switcher`, nên nâng cấp không xoá cấu hình. Bản git checkout vẫn lưu dữ liệu cạnh mã nguồn như trước.
|
|
235
|
-
- **Contract lab.** Gateway có thể gửi một phần nhỏ các lượt trao đổi hoàn chỉnh lên server [intact](https://github.com/louisphamdev/intact) để tìm field mà converter làm mất. Mặc định tính năng này tắt. Xem mục "Contract lab" bên dưới.
|
|
236
|
-
- **macOS.** `blindfold/make-certs.sh` giờ chạy được với LibreSSL, là `openssl` mặc định trên macOS.
|
|
237
|
-
- **Nâng cấp từ 1.1.0 trở xuống.** Gateway cũ hơn 1.1.1 không chứng minh được danh tính. `switch` giờ gọi đúng tên nó và không tự dừng nó. Dừng nó bằng tay một lần, rồi chạy `switch on`.
|
|
238
|
-
- **Test.** `npm test` chỉ chạy `tests/**/*.test.mjs`, kể cả trên Node.js 18 và 20.
|
|
239
|
-
|
|
240
|
-
### Các thay đổi trước đó
|
|
241
|
-
|
|
242
|
-
- Dashboard cho máy tính nay có bố cục gọn như một công cụ dành cho lập trình viên. Các điều khiển route rõ hơn, tab dùng được bằng bàn phím, trường model có nhãn đầy đủ và không còn emoji trang trí.
|
|
243
|
-
- Profile Codex dùng ba vai trò theo tài liệu chính thức: `main`, `review` và `subagent`.
|
|
244
|
-
- Shim Codex truyền các khóa cấu hình chính thức: `model`, `review_model`, `agents.default_subagent_model`, `model_context_window` và `model_auto_compact_token_limit`.
|
|
245
|
-
- Profile cũ vẫn đọc được. Giá trị rỗng ở khóa mới sẽ xóa fallback từ khóa cũ.
|
|
246
|
-
- Model Claude Opus được nhận diện khả năng reasoning mà không phụ thuộc số phiên bản.
|
|
247
|
-
|
|
248
|
-
Shim Codex không còn dựa vào `CODEX_MODEL`, `CODEX_MAX_CONTEXT_TOKENS` hoặc `CODEX_AUTO_COMPACT_WINDOW`. Codex không tài liệu hóa các biến môi trường này. Xem [bảng tham chiếu cấu hình](https://developers.openai.com/codex/config-reference/) và [hướng dẫn cấu hình nâng cao](https://developers.openai.com/codex/config-advanced/) chính thức.
|
|
152
|
+
- **Zero-Mutation Interceptor:** Định tuyến qua `HTTPS_PROXY`, tuyệt đối không can thiệp hay sửa file cấu hình của tool (`~/.claude/settings.json`, `~/.codex/config.toml`).
|
|
153
|
+
- **Hỗ trợ đồng thời cả Claude Code & Codex:** Quản lý độc lập `{ claude, codex }`, chuyển đổi profile tức thì qua `POST /_control/active-tools` mà không cần restart cổng.
|
|
154
|
+
- **Tự động cập nhật Model Catalog:** Tự động lấy danh sách model mới nhất của hãng; tự detect khi tool nâng cấp version để đồng bộ mapping (`switch models`).
|
|
155
|
+
- **Tự sửa lỗi Schema:** Tự động chuẩn hóa schema rỗng `{}` cho Gemini/Vertex và khôi phục tham số `thinking` bị cắt.
|
|
156
|
+
- **Tăng độ tin cậy trên Windows:** Định dạng chuẩn CRLF cho `.cmd` shim, sửa đường dẫn CA và loại bỏ lỗi trôi nhãn subroutine.
|
|
157
|
+
- *Xem lịch sử các phiên bản cũ (v1.1.2 – v1.1.10) tại [CHANGELOG.md](CHANGELOG.md).*
|
|
249
158
|
|
|
250
159
|
---
|
|
251
160
|
|
|
@@ -304,20 +213,28 @@ Mở Bảng điều khiển Web Dashboard tại: **[http://127.0.0.1:3456/ui](ht
|
|
|
304
213
|
|
|
305
214
|
## Tích hợp vào các Công cụ CLI
|
|
306
215
|
|
|
307
|
-
###
|
|
216
|
+
### Biến môi trường đến từ shim, không từ shell của bạn
|
|
217
|
+
|
|
218
|
+
**Đừng** thêm dòng `source env.sh` hay `call env.cmd` vào `~/.bashrc`, `~/.zshrc` hay một file
|
|
219
|
+
wrapper nào. Hai file đó giờ là stub trống: một dòng chú thích và không gì khác, nên dòng rc cũ vẫn
|
|
220
|
+
chạy được và không bao giờ tái tạo lại một base URL.
|
|
308
221
|
|
|
309
|
-
Mỗi
|
|
222
|
+
Mỗi công cụ có file riêng, và chỉ shim tương ứng mới nạp:
|
|
310
223
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
source ~/.llm-switcher/env.sh
|
|
318
|
-
```
|
|
224
|
+
| File | Được nạp bởi |
|
|
225
|
+
| --- | --- |
|
|
226
|
+
| `env-claude.sh` / `env-claude.cmd` | shim `claude` |
|
|
227
|
+
| `env-codex.sh` / `env-codex.cmd` | shim `codex` |
|
|
228
|
+
| `env.sh` / `env.cmd` | không ai. Stub trống, giữ lại chỉ để dòng rc cũ lặng lẽ |
|
|
319
229
|
|
|
320
|
-
|
|
230
|
+
File của công cụ rỗng nghĩa là công cụ đó đang tắt: shim để nguyên môi trường và công cụ gọi thẳng
|
|
231
|
+
endpoint chính thức. Trước khi nạp bất cứ thứ gì, shim cũng quét một `ANTHROPIC_BASE_URL`,
|
|
232
|
+
`OPENAI_BASE_URL` hay `ANTHROPIC_DEFAULT_<TIER>_MODEL` cũ còn sót từ bản trước hoặc từ shell của
|
|
233
|
+
bạn, nên `switch off` là off thật sự.
|
|
234
|
+
|
|
235
|
+
Trong thực tế bạn không bao giờ tự gọi các file này. `switch shim install` đặt `~/.llm-switcher/bin`
|
|
236
|
+
vào `PATH`, và mọi lệnh `claude` hay `codex` — kể cả `claude --resume` trong một terminal hoàn toàn
|
|
237
|
+
mới — đều chạy shim, vốn chỉ tiêm biến vào đúng tiến trình đó.
|
|
321
238
|
|
|
322
239
|
---
|
|
323
240
|
|
|
@@ -329,21 +246,11 @@ Với bản checkout, dùng các file này trong thư mục checkout.
|
|
|
329
246
|
node "path\to\llm-switcher\switch.mjs" %*
|
|
330
247
|
```
|
|
331
248
|
|
|
332
|
-
2.
|
|
249
|
+
2. Cài shim và đặt thư mục của nó **trước** thư mục Claude Code thật trong `PATH` User (System Properties → Environment Variables), rồi mở terminal mới:
|
|
333
250
|
```cmd
|
|
334
|
-
|
|
335
|
-
IF EXIST "%USERPROFILE%\.llm-switcher\active.flag" (
|
|
336
|
-
SET "ANTHROPIC_BASE_URL=http://127.0.0.1:3456"
|
|
337
|
-
SET "CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT=1"
|
|
338
|
-
)
|
|
339
|
-
IF EXIST "%USERPROFILE%\.llm-switcher\1m.flag" (
|
|
340
|
-
SET /P M1M=<"%USERPROFILE%\.llm-switcher\1m.flag"
|
|
341
|
-
IF "!M1M!"=="" SET "M1M=opus[1m]"
|
|
342
|
-
SET "ANTHROPIC_MODEL=!M1M!"
|
|
343
|
-
SET "CLAUDE_CODE_AUTO_COMPACT_WINDOW=900000"
|
|
344
|
-
)
|
|
251
|
+
switch shim install
|
|
345
252
|
```
|
|
346
|
-
>
|
|
253
|
+
> **Đừng** sửa `claude.cmd` trong thư mục global npm: npm ghi đè nó mỗi lần update, và shim mới là thứ tiêm URL gateway. `settings.json` không bao giờ bị đụng tới, nên không hiện banner "custom API".
|
|
347
254
|
|
|
348
255
|
---
|
|
349
256
|
|
|
@@ -361,21 +268,7 @@ codex
|
|
|
361
268
|
|
|
362
269
|
Trên Windows, đặt `%USERPROFILE%\.llm-switcher\bin` trước thư mục Codex thật trong `PATH`. Sau đó, mở terminal mới.
|
|
363
270
|
|
|
364
|
-
Shim không sửa `~/.codex/config.toml`.
|
|
365
|
-
|
|
366
|
-
| Vai trò trong profile | Khóa cấu hình Codex | Tên mà CLI nhận được |
|
|
367
|
-
|---|---|---|
|
|
368
|
-
| `main` | `model` | `publicModels[0]` |
|
|
369
|
-
| `review` | `review_model` | `publicModels[1]` |
|
|
370
|
-
| `subagent` | `agents.default_subagent_model` | `publicModels[2]` |
|
|
371
|
-
|
|
372
|
-
**Profile phục vụ Codex bắt buộc có `publicModels`.** Nếu thiếu khóa này, gateway không có tên chính thức nào để đưa cho Codex. Khi đó gateway không ghi model catalog và không gửi header `OpenAI-Model`, và Codex hiện hai cảnh báo sai: "Model metadata for `<model>` not found" và "Your account was flagged for potentially high-risk cyber activity". `switch codex` và `switch doctor` cảnh báo khi profile Codex không có `publicModels`.
|
|
373
|
-
|
|
374
|
-
**Codex không bao giờ nhận tên nội bộ.** Alias `main`, `review`, `subagent` chỉ tồn tại bên trong gateway. CLI nhận tên model chính thức từ `publicModels`, và `mapModel` phân giải ngược từng tên về đúng slot. Đặt `codexRoles` trong profile nếu muốn ghép khác thứ tự danh sách đó.
|
|
375
|
-
|
|
376
|
-
Shim còn truyền `model_catalog_json`. File này được sinh lại từ `publicModels` mỗi lần đổi profile, và màn `/model` đọc chính nó. Màn `/model` không gọi `/v1/models`. `/v1/models` trả cùng các mục đó, và context window của mỗi mục theo `model1M` của slot.
|
|
377
|
-
|
|
378
|
-
Shim cũng truyền `openai_base_url` để route qua gateway cục bộ. Nếu `main` bật context 1M, shim truyền `model_context_window=1000000` và `model_auto_compact_token_limit=900000`. Override dòng lệnh có độ ưu tiên cao hơn cấu hình người dùng và dự án. Hãy chạy lại `switch shim install` sau khi nâng cấp từ bản cũ.
|
|
271
|
+
Shim tuyệt đối không sửa `~/.codex/config.toml`. Codex kết nối tới gateway qua `HTTPS_PROXY` và interceptor mạng, giữ nguyên tên model và context window chính thức. Danh sách model được phục vụ động tại `/v1/models`.
|
|
379
272
|
|
|
380
273
|
### Chế độ blindfold (tùy chọn)
|
|
381
274
|
|
|
@@ -388,11 +281,24 @@ base URL is overridden to http://127.0.0.1:3456/v1. Selecting models may not be
|
|
|
388
281
|
Blindfold xóa dòng đó. Codex giữ nguyên endpoint chính thức, switcher chặn ở tầng mạng. Không cần quyền admin, không cài chứng chỉ vào system trust store, không sửa `~/.codex/config.toml`.
|
|
389
282
|
|
|
390
283
|
```bash
|
|
391
|
-
bash "$(npm root -g)/llm-switcher/blindfold/make-certs.sh"
|
|
392
|
-
#
|
|
284
|
+
bash "$(npm root -g)/llm-switcher/blindfold/make-certs.sh" # chạy một lần; bản checkout chạy blindfold/make-certs.sh
|
|
285
|
+
# cổng đã nằm sẵn ở cấp cao nhất của config.json: "blindfold": { "port": 3457 }
|
|
393
286
|
switch codex <profile> # gateway khởi động interceptor
|
|
394
287
|
```
|
|
395
288
|
|
|
289
|
+
Một interceptor phục vụ cả hai công cụ. Nó định tuyến theo host của request CONNECT và theo path,
|
|
290
|
+
không theo thứ gì khác:
|
|
291
|
+
|
|
292
|
+
| CONNECT host | Các path chuyển về gateway | Các path còn lại |
|
|
293
|
+
| --- | --- | --- |
|
|
294
|
+
| `api.anthropic.com` | `/v1/messages`, `/v1/messages/...` | chuyển về `api.anthropic.com`, giữ nguyên |
|
|
295
|
+
| `api.openai.com` | `/v1/responses`, `/v1/responses/...`, `/v1/models`, `/v1/models/...` | chuyển về `api.openai.com`, giữ nguyên |
|
|
296
|
+
| `chatgpt.com` | `/backend-api/codex/...`, đổi thành `/v1` | chuyển về `chatgpt.com`, giữ nguyên |
|
|
297
|
+
|
|
298
|
+
Không còn `blindfoldHost` và `blindfoldPrefix`: bảng trên chính là luật định tuyến, và nó không phải
|
|
299
|
+
setting của profile. Host CONNECT ngoài bảng được mở hầm (tunnel) nguyên vẹn; request có header
|
|
300
|
+
`Host` khác host của CONNECT nhận `421` và không mở kết nối upstream. Xem [bảng đầy đủ và quy tắc chứng chỉ](docs/cross-platform.md).
|
|
301
|
+
|
|
396
302
|
Gateway sở hữu interceptor: nó khởi động interceptor khi boot và sau mỗi thay đổi, còn `switch off` dừng nó. Thiếu certificate, hoặc cổng gateway/interceptor bị tiến trình khác giữ, thì `switch` từ chối kích hoạt và không ghi file nào.
|
|
397
303
|
|
|
398
304
|
Hãy đọc [📖 `docs/codex-blindfold.md`](docs/codex-blindfold.md) trước khi bật. Tài liệu nói rõ phạm vi chặn, rủi ro khi giữ private key của CA, và cách quay lại. Mở đầu là ba sơ đồ:
|
|
@@ -401,6 +307,19 @@ Hãy đọc [📖 `docs/codex-blindfold.md`](docs/codex-blindfold.md) trước k
|
|
|
401
307
|
- [Model name resolution](docs/diagrams/codex-model-name-resolution.html) — CLI thấy tên nào, và tên đó phân giải ở đâu
|
|
402
308
|
- [Lifecycle under switch](docs/diagrams/blindfold-switch-lifecycle.html) — kích hoạt, từ chối, và tắt
|
|
403
309
|
|
|
310
|
+
### Những gì bạn đánh đổi
|
|
311
|
+
|
|
312
|
+
- **Context 1M theo cửa sổ của model chính thức.** Switcher không còn ép cửa sổ 1M hay ngưỡng
|
|
313
|
+
auto-compact, cũng không ghi tên model nào vào môi trường: Claude Code tự ước lượng phiên theo
|
|
314
|
+
cửa sổ của model bạn chọn. Backend có cửa sổ nhỏ hơn model đó có thể tràn trong phiên dài.
|
|
315
|
+
`model1M` giờ chỉ quyết định `/v1/models` liệt kê gì.
|
|
316
|
+
- **Codex cần chứng chỉ làm một lần.** Blindfold là thứ giữ Codex trên endpoint chính thức, và nó
|
|
317
|
+
cần một CA riêng cộng leaf nêu đúng ba host ở trên. Không bật thì Codex hiện dòng
|
|
318
|
+
`base URL is overridden` trên màn `/model`.
|
|
319
|
+
- **Interceptor giải mã ba host nó phục vụ.** Nó từ chối CONNECT tới địa chỉ nội bộ hoặc riêng tư,
|
|
320
|
+
nhưng mọi tiến trình trên máy đều gọi được nó. `docs/codex-blindfold.md` mở đầu bằng phạm vi, rủi
|
|
321
|
+
ro khi giữ CA riêng, và cách quay lại.
|
|
322
|
+
|
|
404
323
|
---
|
|
405
324
|
|
|
406
325
|
## Hoạt động Cùng các Tool Nén Token (RTK, Headroom, Ponytail)
|
|
@@ -411,8 +330,8 @@ Nếu bạn sử dụng các tool cắt tỉa prompt như **Headroom**, **Ponyta
|
|
|
411
330
|
3. **LLM Switcher** sẽ đóng vai trò là trạm kiểm soát cuối cùng trước khi ra internet:
|
|
412
331
|
- **Tự chữa lành đồ thị tin nhắn:** Cứu các lượt `tool_result` mồ côi và gộp các lượt cùng role liên tiếp do tool nén cắt xén bừa bãi gây ra.
|
|
413
332
|
- **Khôi phục thinking bị xóa:** Tự động phát hiện reasoning model và khôi phục lại tham số thinking nếu bị tool ngoài xóa mất để "tiết kiệm token".
|
|
414
|
-
- **
|
|
415
|
-
- **Chuyển đổi 2 chiều:**
|
|
333
|
+
- **Context window theo model:** báo cáo cửa sổ chính thức của từng model; không tiêm gì.
|
|
334
|
+
- **Chuyển đổi 2 chiều:** Bridge traffic 2 chiều chuẩn sang intact, 9Router, OpenRouter, Vertex, Anthropic.
|
|
416
335
|
|
|
417
336
|
### Báo cáo Kiểm thử & Đo lường Khả năng Tương thích
|
|
418
337
|
|
|
@@ -444,8 +363,8 @@ node tests/live-optimizer-interop.mjs
|
|
|
444
363
|
### 1. Agent Skill Chuyên dụng (`skills/llm-switcher/SKILL.md`)
|
|
445
364
|
Một Agent Skill theo chuẩn quốc tế hướng dẫn AI model:
|
|
446
365
|
- **Định tuyến bắt buộc:** Mọi lượt gọi LLM và tool nén (Headroom, RTK, Ponytail) BẮT BUỘC phải trỏ về `http://127.0.0.1:3456`.
|
|
447
|
-
- **Cấm sửa `settings.json`:** Tuyệt đối cấm agent ghi đè endpoint vào `~/.claude/settings.json
|
|
448
|
-
- **An toàn cho Sub-process:**
|
|
366
|
+
- **Cấm sửa `settings.json`:** Tuyệt đối cấm agent ghi đè endpoint vào `~/.claude/settings.json` — chính switcher cũng không bao giờ đọc hay ghi file đó.
|
|
367
|
+
- **An toàn cho Sub-process:** Không bao giờ khuyên sub-agent `source env.sh`; shim trong `~/.llm-switcher/bin` tự tiêm biến proxy vào chính tiến trình công cụ và quét hết biến cũ trước.
|
|
449
368
|
|
|
450
369
|
Cài đặt vào thư mục skill:
|
|
451
370
|
```bash
|
|
@@ -458,7 +377,7 @@ cp -r skills/llm-switcher ~/.claude/skills/
|
|
|
458
377
|
|
|
459
378
|
### 2. MCP Server Chuẩn (`mcp.mjs`)
|
|
460
379
|
Một server Model Context Protocol (MCP) chạy qua `stdio` cực nhẹ (Zero-dependency):
|
|
461
|
-
- `switcher_status`: Đọc trạng thái live của các CLI target và
|
|
380
|
+
- `switcher_status`: Đọc trạng thái live của các CLI target và gateway.
|
|
462
381
|
- `switcher_audit`: Quét môi trường máy xem có tool nén nào chạy bậy gọi thẳng ra ngoài không.
|
|
463
382
|
- `switcher_switch_profile`: Cho phép agent tự động chuyển đổi profile theo nhu cầu bài toán.
|
|
464
383
|
- `switcher_recent_logs`: Đọc log gần nhất để tự debug khi output bị cắt cụt.
|
|
@@ -484,35 +403,40 @@ Thêm server vào cấu hình MCP (ví dụ `opencode.jsonc`, `claude_desktop_co
|
|
|
484
403
|
switch ui # Mở giao diện Web UI trên trình duyệt
|
|
485
404
|
switch status # Xem trạng thái kích hoạt của tất cả các CLI
|
|
486
405
|
switch doctor # Quét & thanh tra toàn bộ môi trường, settings và định tuyến
|
|
487
|
-
switch on [profile] # Khởi động gateway và kích hoạt profile
|
|
488
|
-
switch <profile> # Kích hoạt profile cho
|
|
406
|
+
switch on [profile] # Khởi động gateway và kích hoạt một profile
|
|
407
|
+
switch <profile> # Kích hoạt một profile cho cả hai công cụ
|
|
489
408
|
switch claude <profile> # Đặt profile kích hoạt riêng cho Claude Code
|
|
490
409
|
switch codex <profile> # Đặt profile kích hoạt riêng cho Codex
|
|
491
|
-
switch openai <profile> # Đặt profile kích hoạt riêng cho OpenAI Chat
|
|
492
|
-
switch vertex <profile> # Đặt profile kích hoạt riêng cho Vertex / Gemini
|
|
493
410
|
switch port <number> # Đổi cổng gateway (tự restart nếu đang chạy)
|
|
494
411
|
switch service install # Cài đặt gateway thành service chạy ngầm tự bật cùng máy
|
|
495
412
|
switch service uninstall # Gỡ bỏ service chạy ngầm
|
|
496
413
|
switch shim install # Route phiên Claude và Codex mới qua gateway
|
|
497
414
|
switch shim status # Kiểm tra shim + phát hiện phiên đang chạy ngoài gateway
|
|
498
415
|
switch shim uninstall # Gỡ shim khỏi launcher
|
|
499
|
-
switch off
|
|
416
|
+
switch off # Tắt tất cả và quay về endpoint chính thức
|
|
417
|
+
switch off claude # Tắt Claude Code; Codex vẫn chạy tiếp
|
|
418
|
+
switch off codex # Tắt Codex; Claude Code vẫn chạy tiếp
|
|
500
419
|
switch contract-probe [--model m] # Chạy các biến thể contract-lab qua gateway
|
|
501
420
|
switch contract-check # Chuyển các findings hợp đồng còn mở thành test case
|
|
502
421
|
```
|
|
503
422
|
|
|
423
|
+
Target chỉ có `claude` và `codex`, và đó là hai target duy nhất. Một profile phục vụ đúng một công
|
|
424
|
+
cụ: `tool` là `"claude"` hoặc `"codex"` (hoặc `null` khi profile đang tắt), nên `switch claude` và
|
|
425
|
+
`switch codex` không bao giờ chỉ nhầm vào cùng một profile. Không còn target `openai` hay `vertex` —
|
|
426
|
+
các route đầu vào mà chúng đại diện đã bị gỡ.
|
|
427
|
+
|
|
504
428
|
|
|
505
429
|
Service không chạy trong shell của bạn. Vì vậy `switch service install` chép `CLAUDE_CONFIG_DIR`, `LLM_SWITCHER_CONFIG`, `LLM_SWITCHER_STATE_DIR` và `LLM_SWITCHER_BLINDFOLD_CERTS` vào unit systemd hoặc plist launchd khi các biến này có giá trị. Task Windows không mang được các biến này; hãy đặt chúng thành biến môi trường User. Nếu file định nghĩa đã cài khác file mới (ví dụ đã sửa tay), file cũ được giữ lại thành `<file>.bak`. Trên Windows, task được tạo từ file XML, nên đường dẫn có dấu cách không cần quote thêm và task không bị giới hạn thời gian chạy. Đường Windows này chưa được test trên Windows.
|
|
506
430
|
### Phiên mở lại (`--resume`) và cơ chế shim — quan trọng
|
|
507
431
|
|
|
508
|
-
`switch on` ghi `env
|
|
509
|
-
`~/.claude/settings.json`
|
|
510
|
-
|
|
511
|
-
thẳng nhà cung cấp và bỏ qua gateway (mất Healer, mất
|
|
512
|
-
điển là `claude --resume` mở lại phiên cũ trong terminal sạch.
|
|
432
|
+
`switch on` ghi `env-claude.*` và `env-codex.*`, và **không ghi gì** vào
|
|
433
|
+
`~/.claude/settings.json` — switcher không bao giờ đọc hay ghi file đó, đó là lý do Claude Code
|
|
434
|
+
không hiện banner "custom API". Hệ quả: một CLI khởi chạy không qua shim sẽ **không có**
|
|
435
|
+
`ANTHROPIC_BASE_URL`, nên gọi thẳng nhà cung cấp và bỏ qua gateway (mất Healer, mất quota gộp).
|
|
436
|
+
Trường hợp kinh điển là `claude --resume` mở lại phiên cũ trong terminal sạch.
|
|
513
437
|
|
|
514
|
-
Shim bịt đúng lỗ đó. Nó cài wrapper nhỏ vào `~/.llm-switcher/bin`, wrapper
|
|
515
|
-
rồi `exec` binary thật:
|
|
438
|
+
Shim bịt đúng lỗ đó. Nó cài wrapper nhỏ vào `~/.llm-switcher/bin`, wrapper tiêm biến môi trường riêng
|
|
439
|
+
của công cụ rồi `exec` binary thật:
|
|
516
440
|
|
|
517
441
|
```bash
|
|
518
442
|
switch shim install
|
|
@@ -522,11 +446,14 @@ switch shim status # kiểm tra lại
|
|
|
522
446
|
|
|
523
447
|
Cách hoạt động:
|
|
524
448
|
|
|
525
|
-
- **
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
449
|
+
- **Công cụ BẬT** → shim nạp file env riêng của công cụ đó, nên mọi lần gọi (kể cả `--resume`)
|
|
450
|
+
đều qua gateway.
|
|
451
|
+
- **Công cụ TẮT** (file env rỗng) → shim trong suốt hoàn toàn, chạy binary thật nguyên trạng,
|
|
452
|
+
không ép định tuyến.
|
|
453
|
+
- Trước khi nạp bất cứ thứ gì, shim quét một `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` hay
|
|
454
|
+
`ANTHROPIC_DEFAULT_<TIER>_MODEL` cũ còn sót từ bản trước hoặc từ shell của bạn, nên một biến
|
|
455
|
+
không thể sống sót qua `switch off`.
|
|
456
|
+
- Không truyền tham số `--config` nào cho Codex. Shim không đổi gì trên phía Codex ngoài môi trường.
|
|
530
457
|
- Wrapper tìm binary thật sau khi **loại thư mục shim khỏi `PATH`**, nên không bao giờ tự
|
|
531
458
|
gọi đệ quy chính nó. Không tìm thấy binary thật thì thoát mã `127` kèm thông báo rõ ràng,
|
|
532
459
|
không im lặng.
|
|
@@ -544,21 +471,19 @@ trong `PATH`.
|
|
|
544
471
|
```jsonc
|
|
545
472
|
{
|
|
546
473
|
"port": 3456,
|
|
547
|
-
"activeProfile": "9router",
|
|
548
474
|
"activeProfiles": {
|
|
549
|
-
"
|
|
550
|
-
"
|
|
551
|
-
"openai-chat": "9router", // Profile active cho OpenAI Chat
|
|
552
|
-
"vertex": "gemini-profile" // Profile active cho Vertex / Gemini
|
|
475
|
+
"claude": "claude-default", // Profile active cho Claude Code (/v1/messages)
|
|
476
|
+
"codex": "codex-default" // Profile active cho Codex (/v1/responses)
|
|
553
477
|
},
|
|
478
|
+
"blindfold": { "port": 3457 }, // Cổng interceptor. Cấp cao nhất, không bắt buộc, mặc định 3457
|
|
554
479
|
"profiles": {
|
|
555
|
-
"
|
|
556
|
-
"name": "
|
|
480
|
+
"claude-default": {
|
|
481
|
+
"name": "Intact Gateway",
|
|
557
482
|
"mode": "convert", // hybrid | convert | direct
|
|
558
|
-
"
|
|
483
|
+
"tool": "claude", // claude | codex | null (profile đang tắt)
|
|
559
484
|
"outFormat": "openai-chat", // openai-chat | anthropic | vertex
|
|
560
485
|
"thinkingMode": "auto", // auto | native | off (xem Tuỳ chọn Nâng cao)
|
|
561
|
-
"baseURL": "https://api.9router.com/v1
|
|
486
|
+
"baseURL": "https://intact.example.com/v1", // hoặc https://api.9router.com/v1
|
|
562
487
|
"apiKey": "sk-...",
|
|
563
488
|
"defaultModels": {
|
|
564
489
|
"opus": "ag/claude-opus-4-6-thinking",
|
|
@@ -571,12 +496,20 @@ trong `PATH`.
|
|
|
571
496
|
"sonnet": true,
|
|
572
497
|
"haiku": false,
|
|
573
498
|
"fable": true
|
|
574
|
-
}
|
|
575
|
-
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
"codex-default": {
|
|
502
|
+
"name": "Codex qua router",
|
|
503
|
+
"mode": "convert",
|
|
504
|
+
"tool": "codex",
|
|
505
|
+
"outFormat": "vertex",
|
|
506
|
+
"baseURL": "https://YOUR-GATEWAY/v1",
|
|
507
|
+
"apiKey": "sk-...",
|
|
508
|
+
// Profile phục vụ Codex BẮT BUỘC có publicModels (xem Cấu hình ưu tiên Codex).
|
|
576
509
|
"publicModels": ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"], // tên chính thức cho main, review, subagent
|
|
577
510
|
"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
|
|
578
|
-
"
|
|
579
|
-
"
|
|
511
|
+
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" },
|
|
512
|
+
"model1M": { "main": false, "review": false, "subagent": false }
|
|
580
513
|
}
|
|
581
514
|
},
|
|
582
515
|
"debug": false,
|
|
@@ -588,6 +521,17 @@ trong `PATH`.
|
|
|
588
521
|
}
|
|
589
522
|
```
|
|
590
523
|
|
|
524
|
+
`tool` thay `inFormat`: nó nói profile phục vụ công cụ nào, không nói upstream nói định dạng gì.
|
|
525
|
+
Không còn `activeProfile` ở cấp cao nhất, không còn `blindfold`, `blindfoldPort`, `blindfoldHost`
|
|
526
|
+
hay `blindfoldPrefix` bên trong profile, và không còn khóa `openai-chat` hay `vertex` trong
|
|
527
|
+
`activeProfiles`.
|
|
528
|
+
|
|
529
|
+
`config.json` cũ được ghi lại một lần, khi load, qua cơ chế compare-and-swap — cơ chế này từ chối
|
|
530
|
+
đụng vào file mà người khác vừa đổi trước. Khi hai profile sẽ rơi vào cùng một khóa thì quá trình
|
|
531
|
+
dừng lại, nêu rõ khóa xung đột trên CLI và trên dashboard, và để nguyên file đúng như nó vốn có:
|
|
532
|
+
mọi lệnh `switch` thay đổi trạng thái thoát mã khác 0, còn `switch off` và `switch doctor` vẫn chạy
|
|
533
|
+
được, và sửa file xong là mọi thứ hoạt động lại.
|
|
534
|
+
|
|
591
535
|
### Contract lab
|
|
592
536
|
|
|
593
537
|
Contract lab tìm các field mà converter làm mất. Mặc định tính năng này tắt.
|
|
@@ -617,7 +561,7 @@ Nhờ cách chia này, fingerprint của provider không bao giờ là rule tron
|
|
|
617
561
|
| `LLM_SWITCHER_CONFIG=/path/config.json` | Dùng file cấu hình nằm ngoài thư mục dữ liệu (proxy, `switch` và `mcp.mjs` đều hỗ trợ). |
|
|
618
562
|
| `--port <n>` / `LLM_SWITCHER_PORT` | Ghi đè cổng lắng nghe (ưu tiên: flag > env > `config.port`). |
|
|
619
563
|
| 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. |
|
|
620
|
-
| `profile.thinkingMode` | `auto` (mặc định, cho gateway như 9Router): phục hồi thinking bị xoá,
|
|
564
|
+
| `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`. `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`. `off`: không bao giờ gửi tham số reasoning. |
|
|
621
565
|
| `profile.endpoints.countTokens` | Ghi đè URL `count_tokens` của Anthropic. |
|
|
622
566
|
| `profile.endpoints` | Ghi đè URL upstream theo từng format: `{ "openai-chat": "...", "anthropic": "...", "vertex": "https://.../models/{model}:{action}" }`. |
|
|
623
567
|
| `LLM_SWITCHER_STATE_DIR` | Chuyển file launcher và log ra khỏi thư mục dữ liệu. Test dùng biến này; shim đọc thư mục đã đặt lúc cài shim. |
|