From d347de8092913ea987a663fd7f9db7a01c44bc00 Mon Sep 17 00:00:00 2001 From: decolua Date: Fri, 6 Mar 2026 16:26:33 +0700 Subject: [PATCH] feat: enhance translator functionality and UI --- i18n/README.vi.md | 1301 +++++++++++++++++ i18n/README.zh-CN.md | 1294 ++++++++++++++++ open-sse/translator/request/openai-to-kiro.js | 10 +- .../(dashboard)/dashboard/translator/page.js | 688 +++------ src/app/api/settings/route.js | 2 + src/app/api/translator/load/route.js | 5 +- src/app/api/translator/save/route.js | 5 +- src/app/api/translator/send/route.js | 56 +- src/app/api/translator/translate/route.js | 127 +- src/shared/components/Sidebar.js | 27 +- 10 files changed, 2950 insertions(+), 565 deletions(-) create mode 100644 i18n/README.vi.md create mode 100644 i18n/README.zh-CN.md diff --git a/i18n/README.vi.md b/i18n/README.vi.md new file mode 100644 index 0000000..fcc5f42 --- /dev/null +++ b/i18n/README.vi.md @@ -0,0 +1,1301 @@ +Dưới đây là bản dịch tiếng Việt của tài liệu Markdown, giữ nguyên toàn bộ cú pháp và cấu trúc kỹ thuật. + +
+ Bảng điều khiển 9Router + + # 9Router - Free AI Router + + **Không bao giờ ngừng code. Tự động định tuyến tới các mô hình AI MIỄN PHÍ & giá rẻ với cơ chế dự phòng thông minh.** + + **Nhà cung cấp AI Miễn cho OpenClaw.** + +

+ OpenClaw +

+ + [![npm](https://img.shields.io/npm/v/9router.svg)](https://www.npmjs.com/package/9router) + [![Downloads](https://img.shields.io/npm/dm/9router.svg)](https://www.npmjs.com/package/9router) + [![License](https://img.shields.io/npm/l/9router.svg)](https://github.com/decolua/9router/blob/main/LICENSE) + + [🚀 Bắt đầu nhanh](#-quick-start) • [💡 Tính năng](#-key-features) • [📖 Cài đặt](#-setup-guide) • [🌐 Website](https://9router.com) +
+ +--- + +## 🤔 Tại sao chọn 9Router? + +**Ngừng lãng phí tiền bạc và gặp phải giới hạn:** + +- ❌ Hạn mức gói đăng ký hết hạn mỗi tháng mà không dùng hết +- ❌ Giới hạn tốc độ (rate limit) ngăn bạn giữaừng khi code +- ❌ Các API đắt đỏ ($20-50/tháng cho mỗi nhà cung cấp) +- ❌ Phải chuyển đổi thủ công giữa các nhà cung cấp + +**9Router giải quyết vấn đề này:** + +- ✅ **Tối đa hóa gói đăng ký** - Theo dõi hạn mức, sử dụng từng bit trước khi reset +- ✅ **Tự động dự phòng** - Gói đăng ký → Giá rẻ → Miễn phí, thời gian chết bằng không +- ✅ **Đa tài khoản** - Vòng tròn (round-robin) các tài khoản của mỗi nhà cung cấp +- ✅ **Phổ quát** - Hoạt động với Claude Code, Codex, Gemini CLI, Cursor, Cline, bất kỳ công cụ CLI nào + +--- + +## 🔄 Cách thức hoạt động + +``` +┌─────────────┐ +│ Your CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...) +│ Tool │ +└──────┬──────┘ + │ http://localhost:20128/v1 + ↓ +┌────────────────────────────────────────┐ +│ 9Router (Smart Router) │ +│ • Format translation (OpenAI ↔ Claude) │ +│ • Quota tracking │ +│ • Auto token refresh │ +└──────┬──────────────────────────────────┘ + │ + ├─→ [Tier 1: SUBSCRIPTION] Claude Code, Codex, Gemini CLI + │ ↓ quota exhausted + ├─→ [Tier 2: CHEAP] GLM ($0.6/1M), MiniMax ($0.2/1M) + │ budget limit + └─→ [Tier 3: FREE] iFlow, Qwen, Kiro (unlimited) + +Result: Never stop coding, minimal cost +``` + +--- + +## ⚡ Bắt đầu nhanh + +**1. Cài đặt toàn cục:** + +```bash +npm install -g 9router +9router +``` + +🎉 Bảng điều khiển mở tại `http://localhost:20128` + +**2. Kết nối nhà cung cấp MIỄN PHÍ (không cần đăng ký):** + +Bảng điều khiển → Providers -> Kết nối **ude Code** hoặc **Antigravity** -> Đăng nhập OAuth -> Xong! + +**3. Sử dụng trong công cụ CLI của bạn:** + +``` +Cài đặt Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline: + Endpoint: http://localhost:20128/v1 + API Key: [sao chép từ bảng điều khiển] + Model: if/kimi-k2-thinking +``` + +**Xong rồi!** Bắt đầu code với các mô hình AI MIỄN PHÍ. + +**Phương án khác: chạy từ nguồn (k lưu trữ này):** + +Gói kho lưu trữ này là riêng tư (`9router-app`), vì vậy việc thực thi nguồn/Docker là đường dẫn phát triển cục bộ dự kiến. + +```bash +cp .env.example .env +npm install +PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev +``` + +Chế độ Production: + +```bash +npm run build +PORT=20128 HOSTNAME=0.0.0.0 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run start +``` + +URL mặc định: +- Bảng điều khiển: `http://localhost:20128/dashboard` +- API tương thích OpenAI: `http://localhost:20128/v1` + +--- + +## 🎥 Hướng dẫn Video + +
+ +### 📺 Hướng dẫn thiết lập hoàn chỉnh - 9Router + Claude Code MIỄN PHÍ + +[![Thiết lập 9Router + Claude Code](https://img.youtube.com/vi/raEyZPg5xE0/maxresdefault.jpg)](https://www.youtube.com/watch?v=raEyZPg5xE0) + +**🎬 Xem hướng dẫn từng đầy đủ:** +- ✅ Cài đặt & thiết lập 9Router +- ✅ Cấu hình Claude Sonnet 4.5 MIỄN PHÍ +- ✅ Tích hợp Claude Code +- ✅ Thử nghiệm code trực tiếp + +**⏱️ Thời lượng:** 20 phút | **👥 Bởi:** Cộng đồng Nhà phát triển + +[▶️ Xem trên YouTube](https://www.youtube.com/watch?v=o3qYCyjrFYg) + +
+ +--- + +## 🛠️ Các công cụ CLI được hỗ trợ + +9Router hoạt động liền mạch với tất cả các công cụ code AI chính: + +
+ + + + + + + + + + + + + + +
+ Claude Code
+ Claude-Code +
+ OpenClaw
+ OpenClaw +
+ Codex
+ Codex +
+ OpenCode
+ OpenCode +
+ Cursor
+ Cursor + td> +
+ Cline
+ Cline +
+ Continue
+ Continue +
+ Droid
+ Droid +
+ Roo
+ Roo +
+ Antigravity
+ Antigravity +
+
+ +--- + +## Các nhà cung cấp được hỗ trợ + +### 🔐 Các nhà cung cấp OAuth + +
+ + + + + + + + +
+ Claude Code
+ Claude-Code +
+ Antigravity
+ Antigravity +
+ Codex
+ Codex +
+ GitHub
+ GitHub +
+ Cursor
+ Cursor +
+
+ +### 🆓 Các nhà cung cấp Miễn phí + +
+ + + + + + + +
+ iFlow
+ iFlow AI
+ 8+ mô hình • Không giới hạn +
+ Qwen
+ Qwen Code
+ 3+ mô hình • Không giới hạn +
+ Gemini CLI
+ Gemini CLI
+ 180K/tháng MIỄN PHÍ +
+ Kiro
+ Kiro AI
+ Claude • Không giới hạn +
+
+ +### 🔑 Các nhà cung cấp API Key (40+) + +
+ + + + td align="center" width="100"> + GLM
+ GLM + + + + + + + + + + + < align="center" width="100"> + xAI
+ xAI + + + + + + + + + + + + +
+ OpenRouter
+ OpenRouter +
+ Kimi
+ Kimi +
+ MiniMax
+ +
+ OpenAI
+ OpenAI +
+ Anthropic
+ Anthropic +
+ Gemini
+ Gemini +
+ DeepSeek
+ DeepSeek +
+ Groq
+ Groq +
+ Mistral
+ Mistral +
+ Perplexity
Perplexity +
+ Together
+ Together AI +
+ Fireworks
+ Fireworks +
+ < src="./public/providers/cerebras.png" width="50" alt="Cerebras"/>
+ Cerebras +
+ Cohere
+ Cohere +
+ NVIDIA
+ NVIDIA +
+ SiliconFlow
+ SiliconFlow +
+

...và hơn 20 nhà cung cấp khác bao gồm Nebius, Chutes, Hyperbolic và các endpoint tương thích OpenAI/Anthropic tùy chỉnh

+
+ +--- + +## 💡 Các tính năng chính + +| Tính năng | Công dụng | Tại sao nó trọng | +|---------|--------------|----------------| +| 🎯 **Smart 3-Tier Fallback** | Tự động định tuyến: Gói đăng ký → Giá rẻ → Miễn phí | Không bao giờ ngừng code, thời gian chết bằng không | +| 📊 **Theo dõi hạn mức thời gian thực** | Đếm token trực tiếp + đếm ngược reset | Tối đa hóa giá trị gói đăng ký | +| 🔄 **Dịch chuyển định dạng** | OpenAI ↔ Claude ↔ Gemini liền mạch | Hoạt động với mọi công cụ CLI | +| 👥 **Hỗ trợ Đa tài khoản** | Nhiều tài khoản cho mỗi nhà cung cấp | Cân bằng tải + dự phòng | +| 🔄 **Tự động làm mới Token** | Token OAuth tự động làm mới | Không cần đăng nhập lại thủ công | +| 🎨 **Combo tùy chỉnh** | Tạo tổ hợp mô hình không giới hạn | Điều chỉnh dự phòng theo nhu cầu | +| 📝 **Ghi log Request** | Chế độ gỡ lỗi với log request/response đầy đủ | Dễ dàng khắc phục sự cố | +| 💾 **Đồng bộ đám mây** | Đồng bộ cấu hình giữa các thiết bị | Cài đặt giống nhau ở mọi nơi | +| 📊 **Phân tích sử dụng** | Theo dõi token, chi phí, xu hướng theo thời gian | Tối ưu hóa chi tiêu | +| 🌐 **Triển khai ở bất cứ đâu** | Localhost, VPS, Docker, Cloudflare Workers | Tùy chọn triển khai linh hoạt | + +
+📖 Chi tiết tính năng + +### 🎯 Smart 3-Tier Fallback + +Tạo combo với tính năng phòng tự động: + +``` +Combo: "my-coding-stack" + 1. cc/claude-opus-4-6 (gói đăng ký của bạn) + 2. glm/glm-4.7 (backup giá rẻ, $0.6/1M) + 3. if/kimi-k2-thinking (dự phòng miễn phí) + +→ Tự động chuyển đổi khi hết hạn mức hoặc xảy ra lỗi +``` + +### 📊 Theo dõi hạn mức thời gian thực + +- Mức tiêu thụ token cho mỗi nhà cung cấp +- Đếm ngược reset (5 giờ, hàng ngày, hàng tuần) +- Ước tính chi phí cho các tầng trả phí +- Báo cáo chi tiêu hàng tháng + +### 🔄 Dịch chuyển định dạng + +Dịch chuyển liền mạch giữa các định dạng: +- **OpenAI** ↔ **Claude** ↔ **Gemini** ↔ **OpenAI Responses** +- Công cụ CLI của bạn gửi định dạng OpenAI → 9Router dịch chuyển → Nhà cung cấp nhận định dạng gốc +- Hoạt động với mọi công cụ hỗ trợ endpoint OpenAI tùy chỉnh + +### 👥 Hỗ trợ Đa tài khoản + +- Thêm nhiều tài khoản cho mỗi nhà cung cấp +- Định tuyến vòng tròn (round-robin) hoặc dựa trên ưu tiên tự động +- Dự phòng sang tài khoản tiếp theo khi một tài khoản chạm hạn mức + +### 🔄 Tự động làm mới Token + +- Token OAuth tự động làm mới trước khi hết hạn +- Không cần xác thực lại thủ công +- Trải nghiệm liền mạch trên mọi nhà cung cấp + +### 🎨 Combo tùy chỉnh + +- Tạo tổ hợp mô hình không hạn +- Kết hợp các tầng gói đăng ký, giá rẻ và miễn phí +- Đặt tên combo để dễ truy cập +- Chia sẻ combo giữa các thiết bị với Đồng bộ đám mây + +### 📝 Ghi log Request + +- Bật chế độ gỡ lỗi để xem log request/response đầy đủ +- Theo dõi các lệnh gọi API, tiêu đề và payload +- Khắc phục sự cố tích hợp +- Xuất log để phân tích + +### 💾 Đồng bộ đám mây + +- Đồng bộ nhà cung cấp, combo và c đặt giữa các thiết bị +- Tự động đồng bộ nền +- Lưu trữ được mã hóa an toàn +- Truy cập cài đặt của bạn từ bất cứ đâu + +#### Ghi chú Runtime Đám mây + +- Ưu tiên biến đám mây phía máy chủ trong môi trường production: + - `BASE_URL` (URL callback nội bộ được sử dụng bởi bộ lập lịch đồng bộ) + - `CLOUD_URL` (cơ sở endpoint đồng bộ đám mây) +- `NEXT_PUBLIC_BASE_URL` và `NEXT_PUBLIC_CLOUD_URL` vẫn được hỗ trợ để thích/UI, nhưng runtime máy chủ hiện ưu tiên `BASE_URL`/`CLOUD_URL`. +- Các yêu cầu đồng bộ đám mây hiện sử dụng thời gian chờ + hành vi fail-fast để tránh treo UI khi DNS/mạng đám mây không khả dụng. + +### 📊 Phân tích sử dụng + +- Theo dõi mức sử dụng token theo nhà cung cấp và mô hình +- Ước tính chi phí và xu hướng chi tiêu +- Báo cáo và thông tin chi tiết hàng tháng +- Tối ưu hóa chi tiêu AI của bạn + +> **💡AN TRỌNG - Hiểu về Chi phí trên Bảng điều khiển:** +> +> "Chi phí" hiển thị trong Phân tích sử dụng là **chỉ để theo dõi và so sánh**. +> Bản thân 9Router **không bao giờ thu phí** bạn bất cứ thứ gì. Bạn chỉ trả tiền trực tiếp cho các nhà cung cấp (nếu sử dụng dịch vụ trả phí). +> +> **Ví dụ:** Nếu bảng điều khiển của bạn hiển thị "tổng chi phí $290" trong khi sử dụng các mô hìnhFlow, điều này đại diện cho +> số tiền bạn sẽ phải trả nếu sử dụng API trả phí trực tiếp. Chi phí thực tế của bạn = **$0** (iFlow miễn phí không giới hạn). +> +> Hãy coi nó như một "trình theo dõi tiết kiệm" cho thấy bạn đang tiết kiệm được bao nhiêu bằng cách sử dụng các mô hình miễn phí hoặc +> định tuyến qua 9Router! + +### 🌐 Triển khai ở bất cứ đâu + +- 💻 **Localhost** - Mặc định, hoạt động ngoại tuyến + ☁️ **VPS/Cloud** - Chia sẻ giữa các thiết bị +- 🐳 **Docker** - Triển khai bằng một lệnh +- 🚀 **Cloudflare Workers** - Mạng edge toàn cầu + +
+ +--- + +## 💰 Tổng quan về giá + +| Hạng mục | Nhà cung cấp | Chi phí | Reset Hạn mức | Tốt nhất cho | +|------|----------|------|-------------|----------| +| **💳 GÓI ĐĂNG KÝ** | Claude Code (Pro) | $20/tháng | 5h + hàng tuần | Đã đăng ký rồi | +| | Codex (Plus/Pro) | $20-200/tháng | 5h + hàng tuần | Người dùng OpenAI | +| | Gemini CLI | **MIỄN PHÍ** | 180K/tháng + 1K/ngày | Tất cả mọi người! | +| | GitHub Copilot | $10-19/tháng | Hàng tháng | Người dùng GitHub | +| **💰 GIÁ RẺ** | GLM-4.7 | $0.6/1M | 10AM hàng ngày | Backup ngân sách | +| | MiniMax M21 | $0.2/1M | 5 giờ luân phiên | Lựa chọn rẻ nhất | +| | Kimi K2 | $9/tháng cố định | 10M token/tháng | Chi phí dự đoán được | +| **🆓 MIỄN PHÍ** | iFlow | $0 | Không giới hạn | 8 mô hình miễn phí | +| | Qwen | $0 | Không giới hạn | 3 mô hình miễn phí | +| | Kiro | $0 | Không giới hạn | Claude miễn phí | + +**💡 Mẹo Chuyên nghiệp:** Bắt với combo Gemini CLI (180K miễn phí/tháng) + iFlow (không giới hạn miễn phí) = chi phí $0! + +--- + +### 📊 Hiểu về Chi phí & Thanh toán của 9Router + +**Thực tế Thanh toán 9Router:** + +✅ **Phần mềm 9Router = MIỄN PHÍ mãi mãi** (mã nguồn mở, không bao giờ thu phí) +✅ **"Chi phí" trên bảng điều khiển = Chỉ để Hiển thị/Theo dõi** (không phải hóa đơn thực tế) + **Bạn trả tiền trực tiếp cho nhà cung cấp** (gói đăng ký hoặc phí API) +✅ **Nhà cung cấp MIỄN PHÍ vẫn MIỄN PHÍ** (iFlow, Kiro, Qwen = $0 không giới hạn) +❌ **9Router không bao giờ gửi hóa đơn** hoặc tính phí thẻ của bạn + +**Cách Hoạt động của Hiển thị Chi phí:** + +Bảng điều khiển hiển thị **chi phí ước tính** như thể bạn đang sử dụng API trả phí trực tiếp. Đây **không phải là thanh toán** - đó là công cụ so sánh để cho thấy mức tiết kiệm của bạn. + +**Kịch bản Ví dụ:** +``` +Hiển thị trên Bảng điều khiển: +• Tổng số Request: 1,662 +• Tổng số Token: 47M +• Chi phí Hiển thị: $290 + +Kiểm tra Thực tế: +• Nhà cung cấp: iFlow (MIỄN PHÍ không giới hạn) +• Thanh toán Thực tế: $0.00 +• Ý nghĩa của $290: Số tiền bạn TIẾT KIỆM được bằng cách sử dụng mô hình miễn phí! +``` + +**Quy tắc Thanh toán:** +- **Nhà cung cấp gói đăng ký** (Claude Code, Codex): Trả tiền trực tiếp cho họ qua website của họ +- **Nhà cung cấp giá rẻ** (GLM, MiniMax): Trả tiền trực tiếp cho họ, 9Router chỉ định tuyến +- **Nhà cung cấp MIỄN PHÍ** (iFlow, Kiro, Qwen): Thực sự miễn phí mãi mãi, không có phí ẩn +- **9**: Không bao giờ thu phí bất cứ thứ gì, ever + +--- + +## 🎯 Trường hợp sử dụng + +### Trường hợp 1: "Tôi có gói đăng ký Claude Pro" + +**Vấn đề:** Hạn mức hết hạn không dùng, giới hạn tốc độ khi code nặng + +**Giải pháp:** +``` +Combo: "maximize-claude" + 1. cc/claude-opus-4-6 (sử dụng đầy đủ gói đăng ký) + 2. glm/glm-4.7 (backup giá rẻ khi hết hạn mức 3. if/kimi-k2-thinking (dự phòng khẩn cấp miễn phí) + +Chi phí hàng tháng: $20 (gói đăng ký) + ~$5 (backup) = $25 tổng cộng +so với $20 + chạm giới hạn = sự thất vọng +``` + +### Trường hợp 2: "Tôi muốn chi phí bằng không" + +**Vấn đề:** Không đủ khả năng trả gói đăng ký, cần code AI đáng tin cậy + +**Giải pháp:** +``` +Combo: "free-forever" + 1. gc/gini-3-flash (180K miễn phí/tháng) + 2. if/kimi-k2-thinking (không giới hạn miễn phí) + 3. qw/qwen3-coder-plus (không giới hạn miễn phí) + +Chi phí hàng tháng: $0 +Chất lượng: Các mô hình sẵn sàng cho production +``` + +### Trường hợp 3: "Tôi cần code 24/7, không gián đoạn" + +**Vấn đề:** Deadline, không thể để thời gian chết + +**Giải pháp:** +``` +Combo: "always-on" + 1. cc/claude-opus-4-6 (chất lượng tốt nhất) + 2. cx/gpt-5.2-codex (gói đăng ký thứ hai) + 3. glm/glm-4.7 (giá rẻ, reset hàng ngày) + 4. minimax/MiniMax-M2.1 (rẻ nhất, reset 5h) + 5. if/kimi-k2-thinking (miễn phí không giới hạn) + +Kết quả: 5 lớp dự phòng = thời gian chết bằng không +Chi phí tháng: $20-200 (gói đăng ký) + $10-20 (backup) +``` + +### Trường hợp 4: "Tôi muốn AI MIỄN PHÍ trong OpenClaw" + +**Vấn đề:** Cần trợ lý AI trong các ứng dụng nhắn tin (WhatsApp, Telegram, Slack...), hoàn toàn miễn phí + +**Giải pháp:** +``` +Combo: "openclaw-free" + 1. if/glm-4.7 (không giới hạn miễn phí) + 2. if/minimax-m2.1 (không giới hạn phí) + 3. if/kimi-k2-thinking (không giới hạn miễn phí) + +Chi phí hàng tháng: $0 +Truy cập qua: WhatsApp, Telegram, Slack, Discord, iMessage, Signal... +``` + +--- + +## ❓ Các câu hỏi thường gặp + +
+📊 Tại sao bảng điều khiển của tôi hiển thị chi phí cao? + +Bảng điều khiển theo dõi mức sử dụng token của bạn và hiển thị **chi phí ước tính** như thể bạn đang sử dụng API trả phí trực tiếp. Đâykhông phải là thanh toán thực tế** - đó là tài liệu tham khảo để cho thấy bạn đang tiết kiệm được bao nhiêu bằng cách sử dụng các mô hình miễn phí hoặc gói đăng ký hiện có thông qua 9Router. + +**Ví dụ:** +- **Bảng điều khiển hiển thị:** "Tổng chi phí $290" +- **Thực tế:** Bạn đang sử dụng iFlow (MIỄN PHÍ không giới hạn) +- **Chi phí thực tế của bạn:** **$0.00** +- **Ý nghĩa của $290:** Số bạn **tiết kiệm** được bằng cách sử dụng các mô hình miễn phí thay vì API trả phí! + +Màn hình chi phí là một "trình theo dõi tiết kiệm" để giúp bạn hiểu các mẫu sử dụng và cơ hội tối ưu hóa. + +
+ +
+💳 Tôi có bị 9Router tính phí không? + +**Không.** 9Router là phần mềm miễn phí, mã nguồn mở chạy trên máy tính của chính bạn. Nó không bao giờ tính phí bạn bất cứ thứ gì. + +**Bạn chỉ trả tiền:** +- ✅ **Nhà cung cấp gói đăng ký** (Claude Code $20/tháng, Codex $20-200/tháng) → Trả tiền trực tiếp cho họ trên website của họ +- ✅ **Nhà cung cấp giá rẻ** (GLM, MiniMax) → Trả tiền trực tiếp cho họ, 9Router chỉ định tuyến yêu cầu của bạn +- ❌ **Bản thân 9Router** → **Không bao giờ tính phí bất cứ thứ gì, ever** + +9Router là một proxy/router cục bộ. Nó không cóẻ tín dụng của bạn, không thể gửi hóa đơn và không có hệ thống thanh toán. Đó là phần mềm hoàn toàn miễn phí. + +
+ +
+🆓 Các nhà cung cấp MIỄN PHÍ có thực sự không giới hạn không? + +**Có!** Các nhà cung cấp được đánh dấu là MIỄN PHÍ (iFlow, Kiro, Qwen) thực sự không giới hạn với **không có phí ẩn**. + +Đây là các dịch vụ miễn phí được cung cấp bởi các công ty tương ứng: +- **iFlow**: Truy cập miễn phí không giới hạn vào hơn 8 mô hình qua OAuth +- **Kiro**: Các mô hình Claude miễn phí không giới hạn qua AWS Builder ID +- **Qwen**: Truy cập miễn phí không giới hạn vào các mô hình Qwen qua xác thực thiết bị + +9Router chỉ định tuyến yêu cầu của bạn đến họ - không có "cạm bẫy" hay thanh toán trong tương lai. Đó là các dịch vụ thực sự miễn phí, và 9Router giúp chúng dễ sử dụng với hỗ trợ dự phòng. + +**Lưu ý:** số nhà cung cấp gói đăng ký (Antigravity, GitHub Copilot) có thể có các khoảng thời gian dùng thử miễn phí có thể trở thành trả phí sau này, nhưng điều này sẽ được các nhà cung cấp đó thông báo rõ ràng, không phải 9Router. + +
+ +
+💰 Làm thế nào để giảm thiểu chi phí AI thực tế của tôi? + +**Chiến lược Ưu tiên Miễn phí:** + +1. **Bắt đầu với combo 100% miễn phí:** + ``` + 1. gc/gemini-3-flash (180K/tháng miễn phí từ Google) + 2. if/kimi-k2-thinking (không giới hạn miễn phí từ iFlow) + 3. qw/qwen3-coder-plus (không giới hạn miễn phí từ Qwen) + ``` + **Chi phí: $0/tháng** + +2. **Thêm backup giá rẻ** chỉ khi bạn cần: + ``` + 4. glm/glm-4.7 ($0.6/1M token) + ``` + **Chi phí bổ sung:** Chỉ trả tiền cho những gì bạn sự sử dụng + +3. **Sử dụng nhà cung cấp gói đăng ký cuối cùng:** + - Chỉ khi bạn đã có chúng + - 9Router giúp tối đa hóa giá trị của chúng thông qua theo dõi hạn mức + +**Kết quả:** Hầu hết người dùng có thể hoạt động ở mức $0/tháng chỉ sử dụng các tầng miễn phí! + +
+ +
+📈 Điều gì xảy ra nếu mức sử dụng của tôi đột ngột tăng vọt? + +Cơ chế dự phòng thông minh của 9Router ngăn chặn các khoản phí bất ngờ: + +**Kịch bản:** Bạn đang trong giai đoạn code nước rút và vượt qua các hạn mức + +**Không có 9Router:** +- ❌ Chạm giới hạn tốc độ → Công việc dừng lại → Thất vọng +- ❌ Hoặc: Vô tình tích lũy hóa đơn API khổng lồ + +**Có 9Router:** +- ✅ Gói đăng ký chạm giới hạn → Tự động dự phòng sang tầng giá rẻ +- ✅ Tầng giá rẻ trở nên đắt đỏ → Tự động dự phòng sang tầng miễn phí +- ✅ Không bao giờ ngừng code → Chi phí dự đoán được + +**Bạn nắm quyền kiểm soát:** Đặt giới hạn chi tiêu cho mỗi nhà cung cấp trong bảng điều khiển, và 9Router sẽ tuân thủ chúng. + +
+ +--- + +## 📖 Hướng dẫn thiết lập + +
+🔐 Các nhà cung cấp Gói đăng ký (Tối đa hóa Giá trị) + +### Claude Code (Pro/Max) + +```bash +Bảng điều khiển → Providers → Kết nối Claude Code +→ Đăng nhập OAuth → Tự động làm mới token +→ Theo dõi hạn mức 5 giờ + hàng tuần + +Các mô hình: + cc/claude-opus-4-6 + cc/claude-sonnet-4-5-20250929 + cc/claude-haiku-4-5-20251001 +``` + +**Mẹo Chuyên nghiệp:** Sử dụng Opus cho các tác vụ phức tạp, Sonnet cho tốc độ. 9Router theo dõi hạn mức cho mỗi mô hình! + +### OpenAI Codex (Plus/Pro) + +```bash +Bảng điều khiển → Providers → Kết nối Codex +→ Đăng nhập OAuth (cổng 1455) +→ Reset 5 giờ + hàng tuần + +Các mô hình: + cx/gpt-5.2-codex + cx/gpt-5.1-codex-max +``` + +### Gemini CLI (MIỄN PHÍ 180K/tháng!) + +```bash +Bảng điều khiển → Providers → Kết nối Gemini CLI +→ Google OAuth +→ 180K hoàn thành/tháng + 1K/ngày + +Các hình: + gc/gemini-3-flash-preview + gc/gemini-2.5-pro +``` + +**Giá trị tốt nhất:** Tầng miễn phí khổng lồ! Sử dụng cái này trước các tầng trả phí. + +### GitHub Copilot + +```bash +Bảng điều khiển → Providers → Kết nối GitHub +→ OAuth qua GitHub +→ Reset hàng tháng (ngày 1 của tháng) + +Các mô hình: + gh/gpt-5 + gh/claude-4.5-sonnet + gh/gemini-3-pro +` + +
+ +
+💰 Các nhà cung cấp Giá rẻ (Backup) + +### GLM-4.7 (Reset hàng ngày, $0.6/1M) + +1. Đăng ký: [Zhipu AI](https://open.bigmodel.cn/) +2. Lấy API key từ Coding Plan +3. Bảng điều khiển → Thêm API Key: + - Nhà cung cấp: `glm` + - API Key: `your-key` + +**Sử dụng:** `glm/glm-4.7` + +**Mẹo Ch nghiệp:** Coding Plan cung cấp hạn mức gấp 3 lần với chi phí 1/7! Reset hàng ngày lúc 10:00 AM. + +### MiniMax M2.1 (Reset 5h, $0.20/1M) + +1. Đăng ký: [MiniMax](https://www.minimax.io/) +2. Lấy API key +3. Bảng điều khiển → Thêm API Key + +**Sử dụng:** `minimax/MiniMax-M2.1` + +**Mẹo Chuyên nghiệp:** Lựa chọn rẻ nhất cho ngữ cảnh dài (1M)! + +### Kimi K2 ($9/tháng cố định) + +1. Đăng ký: [Moonshot AI](https://platform.moonshot.ai/) +2. Lấy API key +3. Bảng điều khiển → Thêm API Key + +**Sử dụng:** `kimi/kimi-latest` + +**Mẹo Chuyên nghiệp:** Cố định $9/tháng cho 10M token = chi phí thực tế $0.90/1M! + +
+ +
+🆓 Các nhà cung cấp MIỄN PHÍ (Dự phòng Khẩn cấpb> + +### iFlow (8 mô hình MIỄN PHÍ) + +```bash +Bảng điều khiển → Kết nối iFlow +→ Đăng nhập OAuth iFlow +→ Sử dụng không giới hạn + +Các mô hình: + if/kimi-k2-thinking + if/qwen3-coder-plus + if/glm-4.7 + if/minimax-m2 + if/deepseek-r1 +``` + +### Qwen (3 mô hình MIỄN PHÍ) + +```bash +Bảng điều khiển → Kết nối Qwen + Ủy quyền mã thiết bị +→ Sử dụng không giới hạn + +Các mô hình: + qw/qwen3-coder-plus + qw/qwen3-coder-flash +``` + +### Kiro (Claude MIỄN PHÍ) + +```bash +Bảng điều khiển → Kết nối Kiro +→ AWS Builder ID hoặc Google/GitHub +→ Sử dụng không giới hạn + +Các mô hình: + kr/claude-sonnet-4.5 + kr/claude-haiku-4.5 +``` + +
+ +
+🎨 Tạo Combo + +### Ví dụ 1: Tối đa hóa Gói đăng ký → Backup Giá rẻ + +``` +Bảng điều khiển → Combos → Tạo Mới + +Tên: premium-coding +Các mô hình: + 1. cc/claude-opus-4-6 (Gói đăng ký chính) + 2. glm/glm-4.7 (Backup giá rẻ, $0.6/1M) + 3. minimax/MiniMax-M2.1 (Dự phòng rẻ nhất, $0.20/M) + +Sử dụng trong CLI: premium-coding + +Ví dụ chi phí hàng tháng (100M token): + 80M qua Claude (gói đăng ký): $0 thêm + 15M qua GLM: $9 + 5M qua MiniMax: $1 + Tổng: $10 + gói đăng ký của bạn +``` + +### Ví dụ 2: Chỉ Miễn phí (Chi phí bằng không) + +``` +Tên: free-combo +Các mô hình: + 1. gc/gemini-3-flash-preview (180K miễn phíáng) + 2. if/kimi-k2-thinking (không giới hạn) + 3. qw/qwen3-coder-plus (không giới hạn) + +Chi phí: $0 mãi mãi! +``` + +
+ +
+🔧 Tích hợp CLI + +### Cursor IDE + +``` +Settings → Models → Advanced: + OpenAI API Base URL: http://localhost:20128/v1 + OpenAI API Key: [từ bảng điều khiển 9router] + Model: cc/claude-opus-4-6 +``Hoặc sử dụng combo: `premium-coding` + +### Claude Code + +Chỉnh sửa `~/.claude/config.json`: + +```json +{ + "anthropic_api_base": "http://localhost:20128/v1", + "anthropic_api_key": "your-9router-api-key" +} +``` + +### Codex CLI + +```bash +export OPENAI_BASE_URL="http://localhost:20128" +export OPENAI_API_KEY="your-9router-api-key" + +codex "prompt của bạn" +``` + +### OpenClaw + +**Phương án 1 — B điều khiển (khuyên dùng):** + +``` +Bảng điều khiển → CLI Tools → OpenClaw → Chọn Mô hình → Áp dụng +``` + +**Phương án 2 — Thủ công:** Chỉnh sửa `~/.openclaw/openclaw.json`: + +```json +{ + "agents": { + "defaults": { + "model": { + "primary": "9router/if/glm-4.7" + } + } + }, + "models": { + "providers": { + "9router": { + "baseUrl": "://127.0.0.1:20128/v1", + "apiKey": "sk_9router", + "api": "openai-completions", + "models": [ + { + "id": "if/glm-4.7", + "name": "glm-4.7" + } + ] + } + } + } +} +``` + +> **Lưu ý:** OpenClaw chỉ hoạt động với 9Router cục bộ. Sử dụng `127.0.0.1` thay vì `localhost` để tránh các vấn đề phân giải6. + +### Cline / Continue / RooCode + +``` +Provider: OpenAI Compatible +Base URL: http://localhost:20128/v1 +API Key: [từ bảng điều khiển] +Model: cc/claude-opus-4-6 +``` + +
+ +
+🚀 Triển khai + +### Triển khai VPS + +```bash +# Clone và cài đặt +git clone https://github.com/decolua/9router.git +cd 9router +npm install +npm run build + +# Cấu hình +export JWT="your-secure-secret-change-this" +export INITIAL_PASSWORD="your-password" +export DATA_DIR="/var/lib/9router" +export PORT="20128" +export HOSTNAME="0.0.0.0" +export NODE_ENV="production" +export NEXT_PUBLIC_BASE_URL="http://localhost:20128" +export NEXT_PUBLIC_CLOUD_URL="https://9router.com" +export API_KEY_SECRET="endpoint-proxy-api-key-secret" +export MACHINE_ID_SALT="endpoint-proxy-salt" + +# Khởi động +npm run start + +# Hoặc sử dụng PM2 +npm install -g pm2 +pm2 start --name 9router -- start +pm2 save +pm2 startup +``` + +### Docker + +```bash +# Build image (từ gốc kho lưu trữ) +docker build -t 9router . + +# Chạy container (lệnh được sử dụng trong thiết lập hiện tại) +docker run -d \ + --name 9router \ + -p 20128:20128 \ + --env-file /root/dev/9router/.env \ + -v 9router-data:/app/data \ + -v 9router-usage:/root/.9router \ + 9router +``` + +Lệnh di động (nếu bạn đã ở gốc kho lưu trữ): + +```bash +docker run -d \ + --name 9router \ + -p 20128:20128 \ + --env-file ./.env \ + -v 9router-data:/app/data \ + -v 9router-usage:/root/.9router \ + 9router +``` + +Mặc định container: +- `PORT=20128` +- `HOSTNAME=0.0.0.0` + +Các lệnh hữu ích: + +```bash +docker logs -f 9router + restart 9router +docker stop 9router && docker rm 9router +``` + +### Biến môi trường + +| Biến | Mặc định | Mô tả | +|----------|---------|-------------| +| `JWT_SECRET` | `9router-default-secret-change-me` | Bí mật ký JWT cho cookie xác thực bảng điều khiển (**thay đổi trong production**) | +| `INITIAL_PASSWORD` | `123456` | Mật khẩu đăng nhập đầu tiên khi không có hash đã lưu tồn tại | +| `DATA_DIR` | `~/.9router` |ị trí cơ sở dữ liệu ứng dụng chính (`db.json`) | +| `PORT` | framework default | Cổng dịch vụ (`20128` trong các ví dụ) | +| `HOSTNAME` | framework default | Bind host (Docker mặc định là `0.0.0.0`) | +| `NODE_ENV` | runtime default | Đặt `production` để triển khai | +| `BASE_URL` | `http://localhost:20128` | URL cơ sở nội bộ phía máy chủ được sử dụng bởi các tác vụ đồng bộ đám mây | +| `CLOUD_URL` | `https://9router.com` | URL cơ sở endpoint đồng bộ đám mây phía máy chủ | +| `NEXT_PUBLIC_BASE_URL` | `http://localhost:3000` | URL cơ sở tương thích ngược/công khai (ưu tiên `BASE_URL` cho runtime máy chủ) | +| `NEXT_PUBLIC_CLOUD_URL` | `https://9router.com` | URL đám mây tương thích ngược/công khai (ưu tiên `CLOUD_URL` cho runtime máy chủ) | +| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | B mật HMAC cho các API key được tạo | +| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | Salt cho việc băm ID máy ổn định | +| `ENABLE_REQUEST_LOGS` | `false` | Bật log request/response dưới `logs/` | +| `AUTH_COOKIE_SECURE` | `false` | Buộc cookie xác thực `Secure` (đặt `true` phía reverse proxy HTTPS) | +| `REQUIRE_API_KEY` | `false` | Thực thi Bearer API key trên các route `/v1/*` (khuyên dùng cho triển khai xúc internet) | +| `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` | empty | Proxy gửi đi tùy chọn cho các lệnh gọi nhà cung cấp upstream | + +Ghi chú: +- Các biến proxy chữ thường cũng được hỗ trợ: `http_proxy`, `https_proxy`, `all_proxy`, `no_proxy`. +- `.env` không được nướng vào image Docker (`.dockerignore`); tiêm cấu hình runtime với `--env-file` hoặc `-e`. +- Trên Windows, `APPDATA` có thể được sử dụng cho việc phân giải đường dẫn lưuữ cục bộ. +- `INSTANCE_NAME` xuất hiện trong các tài liệu/mẫu env cũ hơn, nhưng hiện không được sử dụng trong runtime. + +### Tệp Runtime và Lưu trữ + +- Trạng thái ứng dụng chính: `${DATA_DIR}/db.json` (nhà cung cấp, combo, alias, key, cài đặt), được quản lý bởi `src/lib/localDb.js`. +- Lịch sử sử dụng và log: `~/.9router/usage.json` và `~/.9router/log.txt`, được quản lý bởi `src/lib/usageDb.js`. +- request/translator tùy chọn: `/logs/...` khi `ENABLE_REQUEST_LOGS=true`. +- Lưu trữ sử dụng hiện tại tuân theo logic đường dẫn `~/.9router` và độc lập với `DATA_DIR`. + +
+ +--- + +## 📊 Các mô hình có sẵn + +
+Xem tất cả các mô hình có sẵn + +**Claude Code (`cc/`)** - Pro/Max: +- `cc/claude-opus-4-6` +- `cc/claude-sonnet-4-5-2025029` +- `cc/claude-haiku-4-5-20251001` + +**Codex (`cx/`)** - Plus/Pro: +- `cx/gpt-5.2-codex` +- `cx/gpt-5.1-codex-max` + +**Gemini CLI (`gc/`)** - MIỄN PHÍ: +- `gc/gemini-3-flash-preview` +- `gc/gemini-2.5-pro` + +**GitHub Copilot (`gh/`)**: +- `gh/gpt-5` +- `gh/claude-.5-sonnet` + +**GLM (`glm/`)** - $0.6/1M: +- `glm/glm-4.7` + +**MiniMax (`minimax/`)** - $0.2/1M: +- `minimax/MiniMax-M2.1` + +**iFlow (`if/`)** - MIỄN PHÍ: +- `if/kimi-k2-thinking` +- `if/qwen3-coder-plus` +- `if/deepseek-r1` + +**Qwen (`qw/`)** - MIỄN PHÍ: +- `qw/q3-coder-plus` +- `qw/qwen3-coder-flash` + +**Kiro (`kr/`)** - MIỄN PHÍ: +- `kr/claude-sonnet-4.5` +- `kr/claude-haiku-4.5` + +
+ +--- + +## 🐛 Khắc phục sự cố + +**"Language model did not provide messages"** +- Hết hạn mức nhà cung cấp → Kiểm tra trình theo dõi hạn mức bảng điều khiển +- Giải pháp: Sử dụng dự phòng combo hoặc chuyển sang tầng rẻ hơn + +**Gi hạn tốc độ (Rate limiting)** +- Hết hạn mức gói đăng ký → Dự phòng sang GLM/MiniMax +- Thêm combo: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking` + +**Token OAuth hết hạn** +- Tự động làm mới bởi 9Router +- Nếu sự cố vẫn tiếp diễn: Bảng điều khiển → Nhà cung cấp → Kết nối lại + +**Chi phí cao** +- Kiểm tra thống kê sử dụng trong Bảng điều khiển +- Chuyển mô hình chính sang GLM/MiniMax +- Sử dụng tầng miễn phí (Gemini CLI, iFlow) cho các tác vụ không quan trọng + +**Bảng điều khiển mở sai cổng** +- Đặt `PORT=20128` và `NEXT_PUBLIC_BASE_URL=http://localhost:20128` + +**Lỗi đồng bộ đám mây** +- Xác minh `BASE_URL` trỏ đến phiên bản đang chạy của bạn (ví dụ: `http://localhost:20128`) +- Xác minh `CLOUD_URL` trỏ đến endpoint đám mây dự kiến của bạn (ví dụ: `https://9router.com`) +- Giữ các giá trị `NEXT_PUBLIC_*` phù hợp với giá trị phía máy chủ khi có thể. + +**Endpoint đám mây `stream=false` trả về 500 (`Unexpected token 'd'...`)** +- Triệu chứng thường xuất hiện trên endpoint đám mây công khai (`https://9router.com/v1`) cho các lệnh gọi không phát trực tiếp (non-streaming). +- Nguyên nhân gốc rễ: upstream trả về payload SSE (`data: ...`) trong khi client mong đợi JSON. +-ải pháp thay thế: sử dụng `stream=true` cho các lệnh gọi trực tiếp đến đám mây. +- Runtime 9Router cục bộ bao gồm dự phòng SSE→JSON cho các lệnh gọi không phát trực tiếp khi upstream trả về `text/event-stream`. + +**Đám mây báo đã kết nối, nhưng yêu cầu vẫn thất bại với `Invalid API key`** +- Tạo một key mới từ bảng điều khiển cục bộ (`/api/keys`) và chạy đồng bộ đám mây (`Enable Cloud` sau đó `Sync Now`). +- Các key cũ/chưa đồng bộ vẫn có thể trả về `401` trên đám mây ngay cả khi endpoint cục bộ hoạt động. + +**Đăng nhập lần đầu không hoạt động** +- Kiểm tra `INITIAL_PASSWORD` trong `.env` +- Nếu chưa đặt, mật khẩu dự phòng là `123456` + +**Không có log request dưới `logs/`** +- Đặt `ENABLE_REQUEST_LOGS=true` + +--- + +## 🛠️ Tech Stack + +- **Runtime**: Node.js 20+ +- **Framework**: Next.js 16 +- **UI**: React 19 + Tailwind 4 +- **Database**: LowDB (dựa trên tệp JSON) +- **Streaming**: Server-Sent Events (SSE) +- **Auth**: OAuth 2.0 (PKCE) + JWT + API Keys + +--- + +## 📝 Tài liệu tham khảo API + +### Chat Completions + +```bash +POST http://localhost:20128/v1/chat/completions +Authorization: Bearer your-api-key +Content-Type: application/json + +{ + "model": "cc/claude-opus-4-6", + "messages": [ + {"role":user", "content": "Viết một hàm để..."} + ], + "stream": true +} +``` + +### Liệt kê Mô hình + +```bash +GET http://localhost:20128/v1/models +Authorization: Bearer your-api-key + +→ Trả về tất cả các mô hình + combo ở định dạng OpenAI +``` + +### Các Endpoint Tương thích + +- `POST /v1/chat/completions` +- `POST /v1/messages` +- `POST /v1/responses` +- `GET /v1/models` +- `POST /v1/messages/count_tokens` +- `GET /v1beta/models` +- `POST /v1beta/models/{...path}` (Gemini-style `generateContent`) +- `POST /v1/api/chat` (đường dẫn chuyển đổi kiểu Ollama) + +### Kịch bản Xác thực Đám mây + +Đã thêm các kịch bản kiểm tra dưới `tester/security/`: + +- `tester/security/test-docker-hardening.sh` + - Build image Docker và xác thực các kiểm tra hardening (`/api/cloud/auth` auth guard, `REQUIRE_API_KEY`, hành vi cookie xác thực bảo). +- `tester/security/test-cloud-openai-compatible.sh` + - Gửi một yêu cầu tương thích OpenAI trực tiếp đến endpoint đám mây (`https://9router.com/v1/chat/completions`) với mô hình/key được cung cấp. +- `tester/security/test-cloud-sync-and-call.sh` + - Quy trình end-to-end: tạo key cục bộ -> bật/đồng bộ đám mây -> gọi endpoint đám mây với thử lại. + - Bao gồm kiểm tra dự phòng với `stream=true` để phân biệt lỗi xác thực với các vấn đề phân tích phát trực tiếp. + +Ghi chú bảo mật cho các kịch bản kiểm tra đám mây: + +- Không bao giờ hardcode các API key thực trong kịch bản/commit. +- Chỉ cung cấp key qua các biến môi trường: + - `API_KEY`, `CLOUD_API_KEY`, hoặc `OPENAI_API_KEY` (được hỗ trợ bởi `test-cloud-openai-compatible.sh`) +- Ví dụ: + +```bash +OPENAI_API_KEY="your-cloud-key" bash tester/security/test-cloud-openai-compatible.sh +``` + +Hành vi dự kiến từ việc xác thực gần đây: + +- cục bộ (`http://127.0.0.1:20128/v1/chat/completions`): hoạt động với `stream=false` và `stream=true`. +- Runtime Docker (cùng đường dẫn API được expose bởi container): các kiểm tra hardening đạt, cloud auth guard hoạt động, chế độ API key nghiêm ngặt hoạt động khi được bật. +- Endpoint đám mây công khai (`https://9router.com/v1/chat/completions`): + - `stream=true`: dự kiến thành công (trả về các khối SSE). + - `stream=false`: có thể thất bại với `500` + lỗi phân tích (`Unexpected token 'd'`) khi upstream trả về nội dung SSE cho đường dẫn client không phát trực tiếp. + +### API Quản lý và Bảng điều khiển + +- Xác thực/cài đặt: `/api/auth/login`, `/api/auth/logout`, `/api/settings`, `/api/settings/require-login` +- Quản lý nhà cung cấp: `/api/providers`, `/api/providers/[id]`, `/api/providers/[id]/test`, `/api/providers/[id]/models`, `/api/providers/validate`, `/api/provider-n*` +- Luồng OAuth: `/api/oauth/[provider]/[action]` (+ các import cụ thể theo nhà cung cấp như Cursor/Kiro) +- Cấu hình định tuyến: `/api/models/alias`, `/api/combos*`, `/api/keys*`, `/api/pricing` +- Sử dụng/log: `/api/usage/history`, `/api/usage/logs`, `/api/usage/request-logs`, `/api/usage/[connectionId]` +- Đồng bộ đám mây: `/api/sync/cloud`, `/api/sync/initialize`, `/api/cloud/*` +-ợ giúp CLI: `/api/cli-tools/claude-settings`, `/api/cli-tools/codex-settings`, `/api/cli-tools/droid-settings`, `/api/cli-tools/openclaw-settings` + +### Hành vi Xác thực + +- Các route Bảng điều khiển (`/dashboard/*`) sử dụng bảo vệ cookie `auth_token`. +- Đăng nhập sử dụng hash mật khẩu đã lưu khi có mặt; nếu không, nó dự phòng vào `INITIAL_PASSWORD`. +- `requireLogin` có thể được chuyển đổi qua `/api/settings/require-login`. + +### Xử lý Yêu cầu (C cao) + +1. Client gửi yêu cầu đến `/v1/*`. +2. Trình xử lý route gọi `handleChat` (`src/sse/handlers/chat.js`). +3. Mô hình được giải quyết (nhà cung cấp/mô hình trực tiếp hoặc giải quyết alias/combo). +4. Thông tin xác thực được chọn từ DB cục bộ với bộ lọc khả dụng tài khoản. +5. `handleChatCore` (`open-sse/handlers/chatCore.js`) phát hiện định dạng và dịch chuyển yêu cầu. +6. Trình thực thi nhà cung cấp gửi cầu upstream. +7. Luồng được dịch chuyển lại thành định dạng client khi cần. +8. Sử dụng/log được ghi lại (`src/lib/usageDb.js`). +9. Dự phòng áp dụng trên lỗi nhà cung cấp/tài khoản/mô hình theo quy tắc combo. + +Tài liệu tham khảo kiến trúc đầy đủ: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) + +--- + +## 📧 Hỗ trợ + +- **Website**: [9router.com](https://9router.com) +- **GitHub**: [github.com/decolua/9](https://github.com/decolua/9router) +- **Issues**: [github.com/decolua/9router/issues](https://github.com/decolua/9router/issues) + +--- + +## 👥 Người đóng góp + +Cảm ơn tất cả những người đã đóng góp giúp 9Router tốt hơn! + +[![Contributors](https://contrib.rocks/image?repo=decolua/9router&max=100&columns=20&anon=1)](https://github.com/decolua/9router/graphs/contributors) + +--- + +## 📊 Star Chart + +[![ Chart](https://starchart.cc/decolua/9router.svg?variant=adaptive)](https://starchart.cc/decolua/9router) + +### Cách Đóng góp + +1. Fork kho lưu trữ +2. Tạo nhánh tính năng của bạn (`git checkout -b feature/amazing-feature`) +3. Commit các thay đổi của bạn (`git commit -m 'Add amazing feature'`) +4. Push lên nhánh (`git push origin feature/amazing-feature`) +5. Mở một Pull Request + +Xem [CONTRIBUTING.md](CONTRIBUT.md) để biết hướng dẫn chi tiết. + +--- + +## 🔀 Forks + +**[OmniRoute](https://github.com/diegosouzapw/OmniRoute)** — Một fork TypeScript đầy đủ tính năng của 9Router. Thêm 36+ nhà cung cấp, tự động dự phòng 4 tầng, API đa phương thức (hình ảnh, embedding, âm thanh, TTS), circuit breaker, bộ nhớ đệm ngữ nghĩa, đánh giá LLM và bảng điều khiển được tinh chỉnh. 368+ bài kiểm tra đơn vị. Có sẵn qua npm và. + +--- + +## 🙏 Lời cảm ơn + +Cảm ơn đặc biệt đến **CLIProxyAPI** - bản triển khai Go gốc đã truyền cảm hứng cho bản chuyển đổi JavaScript này. + +--- + +## 📄 Giấy phép + +Giấy phép MIT - xem [LICENSE](LICENSE) để biết chi tiết. + +--- + +
+ Được xây dựng với ❤️ cho các nhà phát triển code 24/7 +
\ No newline at end of file diff --git a/i18n/README.zh-CN.md b/i18n/README.zh-CN.md new file mode 100644 index 0000000..01b3263 --- /dev/null +++ b/i18n/README.zh-CN.md @@ -0,0 +1,1294 @@ +
+ 9Router Dashboard + + # 9Router - 免费 AI 路由器 + + **永不停歇的编程体验。智能回退,自动路由到免费和廉价的 AI 模型。** + + **OpenClaw 的免费 AI 提供商。** + +

+ OpenClaw +

+ + ![npm](https://img.shields.io/npm/v/9router.svg)](https://www.npmjs.com/package/9router) + [![Downloads](https://img.shields.io/npm/dm/9router.svg)](https://www.npmjs.com/package/9router) + [![License](https://img.shields.io/npm/l/9router.svg)](https://github.com/decolua/9router/blob/main/LICENSE) + + [🚀 快速开始](#-quick-start) • [💡 特性](#-key-features) • [📖 设置](#-setup) • [🌐 网站](https://9router.com) +
+ +--- + +## 🤔 为什么选择 9Router? + +**停止浪费金钱和触碰限制:** + +- ❌ 订阅配额每月未使用即过期 +- ❌ 编程中途遭遇速率限制 +- ❌ 昂贵的 API(每个提供商 $20-50/月) +- ❌ 手动在提供商之间切换 + +**9Router 解决方案:** + +- ✅ **最大化订阅价值** - 追踪配额,在重置前用尽每一分 +- ✅ **自动回退** - 订阅 廉价 → 免费,零停机时间 +- ✅ **多账户** - 每个提供商的账户间轮询 +- ✅ **通用性** - 适用于 Claude Code, Codex, Gemini CLI, Cursor, Cline, 任何 CLI 工具 + +--- + +## 🔄 工作原理 + +``` +┌─────────────┐ +│ Your CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...) +│ Tool │ +└──────┬──────┘ + │ http://localhost:201281 + ↓ +┌─────────────────────────────────────────┐ +│ 9Router (Smart Router) │ +│ • Format translation (OpenAI ↔ Claude) │ +│ • Quota tracking │ +│ • Auto token refresh │ +└──────┬──────────────────────────────────┘ + │ + ├─→ [Tier 1: SUBSCRIPTION] Claude Code, Codex, Gemini CLI + │ ↓ quota exhausted + ├─→ [Tier 2: CHEAP] GLM ($0.6/1M), MiniMax ($0.2/1M) + │ ↓ budget limit + └─→ [Tier 3: FREE] iFlow, Qwen, Kiro (unlimited) + +Result: Never stop coding, minimal cost +``` + +--- + +## ⚡ 快速开始 + +**1. 全局安装:** + +```bash +npm install -g 9router +9router +``` + +🎉 仪表板将在 `http://localhost:20128` 打开 + +**2. 连接免费提供商(无需注册):** + +仪表板 → 提供商 → 连接 **Claude Code** 或 **Antigr** → OAuth 登录 → 完成! + +**3. 在您的 CLI 工具中使用:** + +``` +Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline 设置: + Endpoint: http://localhost:20128/v1 + API Key: [从仪表板复制] + Model: if/kimi-k2-thinking +``` + +**就是这样!** 开始使用免费 AI 模型编程。 + +**替代方案:从源码运行(此仓库):** + +此仓库包是私有的(`9router-app`),因此源码/Docker 执行是预期的本地开发路径。 + +```bash +cp .env.example .env +npm install +PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev +``` + +生产模式: + +```bash +npm run build +PORT=20128 HOSTNAME=0.0.0.0 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run start +``` + +默认 URL: +- 仪表板:`http://localhost:20128/dashboard` +- OpenAI 兼容 API:`http://localhost:20128/v1` + +--- + +## 🎥 视频教程 + +
+ +### 📺完整设置指南 - 9Router + Claude Code 免费 + +[![9Router + Claude Code Setup](https://img.youtube.com/vi/raEyZPg5xE0/maxresdefault.jpg)](https://www.youtube.com/watch?v=raEyZPg5xE0) + +**🎬 观看完整的分步教程:** +- ✅ 9Router 安装与设置 +- ✅ 免费 Claude Sonnet 4.5 配置 +- ✅ Claude Code 集成 +- ✅ 实时编程演示 + +**⏱️ 时长:** 20 分钟 | **👥 作者** 开发者社区 + +[▶️ 在 YouTube 上观看](https://www.youtube.com/watch?v=o3qYCyjrFYg) + +
+ +--- + +## 🛠️ 支持的 CLI 工具 + +9Router 与所有主流 AI 编程工具无缝协作: + +
+ + + + + + + + + + + + + + +
+ Claude Code
+ Claude-Code +
+ OpenClaw
+ OpenClaw +
+ Codex
+ Codex +
+ OpenCode
+ OpenCode +
+ Cursor
+ Cursor +
+ Cline
+ Cline +
+
+ Continue +
+ Droid
+ Droid +
+ Roo
+ Roo +
+ Antigravity
+ Antigravity +
+
+ +--- + +## 🌐 支持的提供商 + +### 🔐 OAuth 提供商 + +
+ + + + + + + Cursor
+ Cursor + + +
+ Claude Code
+ Claude-Code +
+ Antigravity
+ Antigravity +
+ Codex
+ Codex +
+ GitHub
+ GitHub +
+
+ +### 🆓 免费提供商 + +
+ + + + + + +
+ iFlow
+ iFlow AI
+ 8+ 模型 无限制 +
+ Qwen
+ Qwen Code
+ 3+ 模型 • 无限制 +
+ Gemini CLI
+ Gemini CLI
+ 180K/月 免费 +
+ Kiro
+ Kiro AI
+ Claude • 无限制 +
+
+ +### 🔑 API Key 提供商 (40+) + +
+ + + + + + + + + + + + Gemini
+ Gemini + +
+ + + + + + + + + + + + SiliconFlow
+ SiliconFlow + + +
+
+ OpenRouter +
+ GLM
+ GLM +
+ Kimi
+ Kimi +
+ MiniMax
+ MiniMax +
+ OpenAI
+ OpenAI +
+ Anthropic
+ Anthropic +
+ DeepSeek
+ DeepSeek +
+ Groq
+ Groq +
+ xAI
+ xAI +
+ Mistral
+ Mistral +
+ Perplexity
+ Perplexity +
+ Together
+ Together AI +
+ Fireworks
+ Fireworks +
+ Cerebras
+ Cerebras +
+ Cohere
+ Cohere +
+ NVIDIA
+ NVIDIA +
+

...以及 20+ 更多提供商,包括 Nebius, Chutes, Hyperbolic 和自定义 OpenAI/Anthropic 兼容端点

+
+ +--- + +## 💡 核心特性 + +| 特性 | 功能 | 重要性 | +|---------|--------------|----------------| +| **智能 3 层回退** | 自动路由:订阅 → 廉价 → 免费 | 永不停止编程,零停机时间 | +| 📊 **实时配额追踪** | 实时 Token 计数 + 重置倒计时 | 最大化订阅价值 | +| 🔄 **格式转换** | OpenAI ↔ Claude ↔ Gemini 无缝转换 | 适用于任何 CLI 工具 | +| 👥 **多账户支持** | 每个提供商多个账户 | 负载均衡 + 冗余 | +| 🔄 **自动 Token 刷新** | OAuth token 自动刷新 |需手动重新登录 | +| 🎨 **自定义组合** | 创建无限模型组合 | 根据需求定制回退策略 | +| 📝 **请求日志** | 调试模式包含完整请求/响应日志 | 轻松排查问题 | +| 💾 **云端同步** | 跨设备同步配置 | 到处都是相同的设置 | +| 📊 **使用分析** | 追踪 Token、成本、趋势 | 优化支出 | +| 🌐 **随处部署** | 本地主机、VPS、Docker、Cloudflare Workers | 灵活的部署选项 | + +
+📖 特性详情 + +### 🎯 智能 3 层回退 + +创建具有自动回退功能的组合: + +``` +Combo: "my-coding-stack" + 1. cc/claude-opus-4-6 (your subscription) + 2. glm/glm-4.7 (cheap backup, $0.6/1M) + 3. if/kimi-k2-thinking (free fallback) + +→ Auto switches when quota runs out or errors occur +``` + +### 📊 实时配额追踪 + +- 每个提供商的 Token 消 +- 重置倒计时(5 小时、每日、每周) +- 付费层的成本估算 +- 月度支出报告 + +### 🔄 格式转换 + +格式间无缝转换: +- **OpenAI** ↔ **Claude** ↔ **Gemini** ↔ **OpenAI Responses** +- 您的 CLI 工具发送 OpenAI 格式 → 9Router 转换 → 提供商接收原生格式 +- 适用于任何支持自定义 OpenAI 端点的工具 + +### 👥 多账户支持 + +- 每个提供商添加多个账户 +- 自动轮询或基于优先级的 +- 当一个账户达到配额时回退到下一个 + +### 🔄 自动 Token 刷新 + +- OAuth token 在过期前自动刷新 +- 无需手动重新认证 +- 所有提供商的无缝体验 + +### 🎨 自定义组合 + +- 创建无限模型组合 +- 混合订阅、廉价和免费层 +- 为您的组合命名以便访问 +- 通过云端同步跨设备共享组合 + +### 📝 请求日志 + +- 启用调试模式以获取完整请求/响应日志 +- 追踪 API 调用、标头和负载 +- 排查集成 +- 导出日志进行分析 + +### 💾 云端同步 + +- 跨设备同步提供商、组合和设置 +- 自动后台同步 +- 安全加密存储 +- 从任何地方访问您的设置 + +#### 云端运行说明 + +- 在生产环境中优先使用服务器端云变量: + - `BASE_URL`(同步调度器使用的内部回调 URL) + - `CLOUD_URL`(云端同步端点基础 URL) +- `NEXT_PUBLIC_BASE_URL` 和 `NEXT_PUBLIC_CLOUD_URL` 仍支持兼容性/UI,但服务器运行时现在优先使用 `BASE_URL`/`C_URL`。 +- 云端同步请求现在使用超时 + 快速失败行为,以避免在云端 DNS/网络不可用时 UI 挂起。 + +### 📊 使用分析 + +- 追踪每个提供商和模型的 Token 使用情况 +- 成本估算和支出趋势 +- 月度报告和洞察 +- 优化您的 AI 支出 + +> **💡 重要 - 理解仪表板成本:** +> +> 使用分析中显示的“成本”**仅用于追踪和比较目的**。 +> 9Router 本身**从不向您收费**。您只需直接向提供商付款(如果使用付费服务)。 +> +> **示例:** 如果您的仪表板在使用 iFlow 模型时显示“$290 总成本”,这代表 +> 您直接使用付费 API 时需要支付的金额。您的实际成本 = **$0**(iFlow 是免费无限制的)。 +> +> 将其视为“节省追踪器”,显示您通过使用免费模型或 +> 通过 9Router 路由节省了多少! + +### 🌐 随处部署 + +- 💻 **本地主机** - 默认,离线工作 +- ☁️ **VPS/云** 跨设备共享 +- 🐳 **Docker** - 一键部署 +- 🚀 **Cloudflare Workers** - 全球边缘网络 + +
+ +--- + +## 💰 定价一览 + +| 层级 | 提供商 | 成本 | 配额重置 | 最适合 | +|------|----------|------|-------------|----------| +| **💳 订阅** | Claude Code (Pro) | $20/月 | 5h + 每周 | 已订阅用户 | +| | Codex (Plus/Pro) | $20-200/月 | 5h + 每周 OpenAI 用户 | +| | Gemini CLI | **免费** | 180K/月 + 1K/天 | 所有人! | +| | GitHub Copilot | $10-19/月 | 每月 | GitHub 用户 | +| **💰 廉价** | GLM-4.7 | $0.6/1M | 每日 10AM | 预算备份 | +| | MiniMax M2.1 | $0.2/1M | 5 小时滚动 | 最便宜选项 | +| | Kimi K2 | $9/月固定 | 10M tokens/月 | 可预测成本 | +| **🆓 免费** | iFlow | $0 | 无限制 | 8 个模型免费 | +| | Qwen | $0 | 无限制 | 3 个模型免费 | +| | Kiro | $0 | 无限制 | Claude 免费 | + +**💡 专业提示:** 从 Gemini CLI(180K 免费/月)+ iFlow(无限制免费)组合开始 = $0 成本! + +--- + +### 📊 理解 9Router 成本和计费 + +**9Router 计费现实:** + +✅ **9Router 软件 = 永远免费**开源,从不收费) +✅ **仪表板“成本” = 仅显示/追踪**(非实际账单) +✅ **您直接向提供商付款**(订阅或 API 费用) +✅ **免费提供商保持免费**(iFlow, Kiro, Qwen = $0 无限制) +❌ **9Router 从不发送发票**或向您的卡收费 + +**成本显示如何工作:** + +仪表板显示**估算成本**,就像您直接使用付费 API 一样。这**不是计费** - 它是一个比较工具,用于显示您的节省。 + +**示例场景:``` +仪表板显示: +• 总请求数:1,662 +• 总 Token 数:47M +• 显示成本:$290 + +现实检查: +• 提供商:iFlow(免费无限制) +• 实际付款:$0.00 +• $290 的含义:您通过使用免费模型节省的金额! +``` + +**付款规则:** +- **订阅提供商**(Claude Code, Codex):通过他们的网站直接向他们付款 +- **廉价提供商**(GLM, MiniMax):直接向他们付款,9Router 只是路由 +- **免费**(iFlow, Kiro, Qwen):真正永远免费,没有隐藏费用 +- **9Router**:从不收取任何费用,永远 + +--- + +## 🎯 使用案例 + +### 案例 1:“我有 Claude Pro 订阅” + +**问题:** 配额未使用即过期,重度编程时遇到速率限制 + +**解决方案:** +``` +Combo: "maximize-claude" + 1. cc/claude-opus-4-6 (use subscription fully) + 2. glm/glm-4.7 (cheap backup when quota out) + 3 if/kimi-k2-thinking (free emergency fallback) + +Monthly cost: $20 (subscription) + ~$5 (backup) = $25 total +vs. $20 + hitting limits = frustration +``` + +### 案例 2:“我想要零成本” + +**问题:** 负担不起订阅,需要可靠的 AI 编程 + +**解决方案:** +``` +Combo: "free-forever" + 1. gc/gemini-3-flash (180K free/month) + 2. if/kimi-k2-thinking (unlimited free) + 3. qw/qwen3-c-plus (unlimited free) + +Monthly cost: $0 +Quality: Production-ready models +``` + +### 案例 3:“我需要 24/7 编程,无中断” + +**问题:** 截止日期,不能承受停机 + +**解决方案:** +``` +Combo: "always-on" + 1. cc/claude-opus-4-6 (best quality) + 2. cx/gpt-5.2-codex (second subscription) + 3. glm/glm-4.7 (cheap, resets daily) + 4. minimaxMiniMax-M2.1 (cheapest, 5h reset) + 5. if/kimi-k2-thinking (free unlimited) + +Result: 5 layers of fallback = zero downtime +Monthly cost: $20-200 (subscriptions) + $10-20 (backup) +``` + +### 案例 4:“我想在 OpenClaw 中使用免费 AI” + +**问题:** 需要在消息应用(WhatsApp, Telegram, Slack...)中使用 AI 助手,完全免费 + +**解决方案:** +``` +Combo: "openclaw-free" + 1. if/glm-4.7 (unlimited free) + 2. if/minimax-m2.1 (unlimited free) + 3. if/kimi-k2-thinking (unlimited free) + +Monthly cost: $0 +Access via: WhatsApp, Telegram, Slack, Discord, iMessage, Signal... +``` + +--- + +## ❓ 常见问题 + +
+📊 为什么我的仪表板显示高成本? + +仪表板追踪您的 Token 使用情况,并显示**估算成本**,就像您直接使用付费 API 一样。这**不是实际计费** - 它是一个参考,显示您通过 9Router 使用免费模型或现有订阅节省了多少。 + +**示例:** +- **仪表板显示:**“$290 总成本” +- **现实:** 您正在使用 iFlow(免费无限制) +- **您的实际成本:** **$0.00** +- **$290 的含义:** 您通过使用免费模型而不是付费 API **节省**的金额! + +成本显示是一个“节省追踪器”,帮助您了解使用模式和优化机会。 + +
+ +
+💳 9Router 会向我收费吗? + +**不会。** 9 是免费的开源软件,在您自己的计算机上运行。它从不向您收费。 + +**您只需支付:** +- ✅ **订阅提供商**(Claude Code $20/月, Codex $20-200/月)→ 在他们的网站上直接向他们付款 +- ✅ **廉价提供商**(GLM, MiniMax)→ 直接向他们付款,9Router 只是路由您的请求 +- ❌ **9Router 本身** → **从不收取任何费用,永远** + +9Router 是本地代理/路由器。它没有您的信用卡,不能发送发票,也没有计费系统。完全免费的软件。 + +
+ +
+🆓 免费提供商真的无限制吗? + +**是的!** 标记为免费(iFlow, Kiro, Qwen)的提供商是真正无限制的,**没有隐藏费用**。 + +这些是各自公司提供的免费服务: +- **iFlow**:通过 OAuth 免费无限制访问 8+ 模型 +- **Kiro**:通过 AWS Builder ID 免费无限制 Claude 模型 +- **Qwen**:通过设备认证免费无限制访问 Qwen 模型 + +Router 只是将您的请求路由到它们 - 没有“陷阱”或未来计费。它们是真正的免费服务,9Router 使它们易于使用并支持回退。 + +**注意:** 一些订阅提供商(Antigravity, GitHub Copilot)可能有免费预览期,后来可能变成付费,但这会由这些提供商明确宣布,而不是 9Router。 + +
+ +
+💰 如何最小化我的实际 AI 成本? + +**免费优先策略:** + +1. **从 100% 免费组合开始:** + ``` + 1. gc/gini-3-flash (180K/month free from Google) + 2. if/kimi-k2-thinking (unlimited free from iFlow) + 3. qw/qwen3-coder-plus (unlimited free from Qwen) + ``` + **成本:$0/月** + +2. **仅在需要时添加廉价备份:** + ``` + 4. glm/glm-4.7 ($0.6/1M tokens) + ``` + **额外成本:仅为您实际使用的付费** + +3. **最后使用订阅提供商:** + - 仅当您已经拥有它们时 + - 9Router 通过配额追踪帮助最大化其价值 + +**结果:** 大多数用户可以仅使用免费层以 $0/月运行! + +
+ +
+📈 如果我的使用量突然激增怎么办? + +9Router 的智能回退可防止意外费用: + +**场景:** 您正在进行编程冲刺并耗尽了配额 + +**没有 9Router:** +- ❌ 遇到速率限制 → 工作停止 → 沮丧 +- ❌ 或:意外累积巨额 API 账单 + +**有 9Router:** +- ✅订阅达到限制 → 自动回退到廉价层 +- ✅ 廉价层变得昂贵 → 自动回退到免费层 +- ✅ 永不停止编程 → 可预测的成本 + +**您在控制中:** 在仪表板中设置每个提供商的支出限制,9Router 会遵守它们。 + +
+ +--- + +## 📖 设置指南 + +
+🔐 订阅提供商(最大化价值) + +### Claude Code (Pro/Max) + +```bash +Dashboard → Providers → Connect Claude Code +→ OAuth login → Auto token refresh +→ 5-hour + weekly quota tracking + +Models: + cc/claude-opus-4-6 + cc/claude-sonnet-4-5-20250929 + cc/claude-haiku-4-5-20251001 +``` + +**专业提示:** 使用 Opus 处理复杂任务,Sonnet 追求速度。9Router 追踪每个模型的配额! + +### OpenAI Codex (Plus/Pro) + +```bash +Dashboard → Providers → Connect Codex +→ OAuth login (port 1455) +→ 5-hour + weekly reset + +Models: + /gpt-5.2-codex + cx/gpt-5.1-codex-max +``` + +### Gemini CLI(免费 180K/月!) + +```bash +Dashboard → Providers → Connect Gemini CLI +→ Google OAuth +→ 180K completions/month + 1K/day + +Models: + gc/gemini-3-flash-preview + gc/gemini-2.5-pro +``` + +**最佳价值:** 巨大的免费层!在付费层之前使用这个。 + +### GitHub Copilot + +```bash +Dashboard → Providers → Connect GitHub +→ OAuth via +→ Monthly reset (1st of month) + +Models: + gh/gpt-5 + gh/claude-4.5-sonnet + gh/gemini-3-pro +``` + +
+ +
+💰 廉价提供商(备份) + +### GLM-4.7(每日重置,$0.6/1M) + +1. 注册:[Zhipu AI](https://open.bigmodel.cn/) +2. 从 Coding Plan 获取 API key +3. 仪表板 → 添加 API Key: + - Provider: `glm` + - API Key: `your-key` + +**使用:** `glm/glm-4.7` + +**专业提示:** Coding Plan 以 1/7 的成本提供 3× 配额!每日 10:00 AM 重置。 + +### MiniMax M2.1(5h 重置,$0.20/1M) + +1. 注册:[MiniMax](https://www.minimax.io/) +2. 获取 API key +3. 仪表板 → 添加 API Key + +**使用:** `minimax/MiniMax-M2.1` + +**专业提示:** 长上下文(1M tokens)的最便宜选项! + +### Kimi K2($9/月固定) + +1. 订阅:[Moonshot AI](https://platform.moonshot.ai/) +2. 获取 API key +3. 仪表板 → 添加 API Key + +**使用:** `kimi/kimi-latest` + +**专业提示:** 固定 $9/月可获得 10M tokens = $0.90/1M 实际成本! + +
+ +
+🆓 免费提供商(紧急备份) + +### i(8 个免费模型) + +```bash +Dashboard → Connect iFlow +→ iFlow OAuth login +→ Unlimited usage + +Models: + if/kimi-k2-thinking + if/qwen3-coder-plus + if/glm-4.7 + if/minimax-m2 + if/deepseek-r1 +``` + +### Qwen(3 个免费模型) + +```bash +Dashboard → Connect Qwen +→ Device code authorization +→ Unlimited usage + +Models: + qw/qwen3-coder-plus + qw/qwen3-coder-flash +``` + +### Kiro(Claude 免费```bash +Dashboard → Connect Kiro +→ AWS Builder ID or Google/GitHub +→ Unlimited usage + +Models: + kr/claude-sonnet-4.5 + kr/claude-haiku-4.5 +``` + +
+ +
+🎨 创建组合 + +### 示例 1:最大化订阅 → 廉价备份 + +``` +Dashboard → Combos → Create New + +Name: premium-coding +Models: + 1. cc/claude-opus-4-6 (Subscription primary) + 2. glm/glm4.7 (Cheap backup, $0.6/1M) + 3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M) + +Use in CLI: premium-coding + +Monthly cost example (100M tokens): + 80M via Claude (subscription): $0 extra + 15M via GLM: $9 + 5M via MiniMax: $1 + Total: $10 + your subscription +``` + +### 示例 2:仅免费(零成本) + +``` +Name: free-combo +Models: + 1. gc/gemini-3-flash-preview (180K free/month) + 2. if/kimi-k2-thinking (unlimited) + 3. qw/qwen3-coder-plus (unlimited) + +Cost: $0 forever! +``` + +
+ +
+🔧 CLI 集成 + +### Cursor IDE + +``` +Settings → Models → Advanced: + OpenAI API Base URL: http://localhost:20128/v1 + OpenAI API Key: [from 9router dashboard] + Model: cc/claude-opus-4-6 +``` + +使用组合:`premium-coding` + +### Claude Code + +编辑 `~/.claude/config.json`: + +```json +{ + "anthropic_api_base": "http://localhost:20128/v1", + "anthropic_api_key": "your-9router-api-key" +} +``` + +### Codex CLI + +```bash +export OPENAI_BASE_URL="http://localhost:20128" +export OPENAI_API_KEY="your-9router-api-key" + +codex "your prompt" +``` + +### OpenClaw + +**选项 1 — 仪表板(推荐):** + +``` +Dashboard → CLI Tools →Claw → Select Model → Apply +``` + +**选项 2 — 手动:** 编辑 `~/.openclaw/openclaw.json`: + +```json +{ + "agents": { + "defaults": { + "model": { + "primary": "9router/if/glm-4.7" + } + } + }, + "models": { + "providers": { + "9router": { + "baseUrl": "http://127.0.0.1:20128/v1", + "apiKey": "sk_9router", + "api": "openai-completions", + "models": [ + { + "id": "if/glm-4.7", + "name": "glm-4.7" + } + ] + } + } + } +} +``` + +> **注意:** OpenClaw 仅适用于本地 9Router。使用 `127.0.0.1` 而不是 `localhost` 以避免 IPv6 解析问题。 + +### Cline / Continue / RooCode + +``` +Provider: OpenAI Compatible +Base URL: http://localhost:20128/v1 +API Key: [from dashboard] +Model: cc/claudeus-4-6 +``` + +
+ +
+🚀 部署 + +### VPS 部署 + +```bash +# Clone and install +git clone https://github.com/decolua/9router.git +cd 9router +npm install +npm run build + +# Configure +export JWT_SECRET="your-secure-secret-change-this" +export INITIAL_PASSWORD="your-password" +export DATA_DIR="/var/lib/9router" +export PORT="20128" +export HOSTNAME="0.0.0.0" +export NODE_ENV="production" +export NEXT_PUBLIC_BASE_URLhttp://localhost:20128" +export NEXT_PUBLIC_CLOUD_URL="https://9router.com" +export API_KEY_SECRET="endpoint-proxy-api-key-secret" +export MACHINE_ID_SALT="endpoint-proxy-salt" + +# Start +npm run start + +# Or use PM2 +npm install -g pm2 +pm2 start npm --name 9router -- start +pm2 save +pm2 startup +``` + +### Docker + +```bash +# Build image (from repository root) +docker build -t 9router . + +# Run container (command used in current setup) +docker run -d \ + --name 9router -p 20128:20128 \ + --env-file /root/dev/9router/.env \ + -v 9router-data:/app/data \ + -v 9router-usage:/root/.9router \ + 9router +``` + +便携式命令(如果您已在仓库根目录): + +```bash +docker run -d \ + --name 9router \ + -p 20128:20128 \ + --env-file ./.env \ + -v 9router-data:/app/data \ + -v 9router-usage:/root/.9router \ + 9 +``` + +容器默认值: +- `PORT=20128` +- `HOSTNAME=0.0.0.0` + +有用命令: + +```bash +docker logs -f 9router +docker restart 9router +docker stop 9router && docker rm 9router +``` + +### 环境变量 + +| 变量 | 默认值 | 描述 | +|----------|---------|-------------| +| `JWT_SECRET` | `9router-default-secret-change-me` | 仪表板认证 cookie 的 JWT 签名密钥(**生产环境中请更改**) | +| `INITIAL_PASSWORD | `123456` | 当没有保存的哈希时的首次登录密码 | +| `DATA_DIR` | `~/.9router` | 主应用数据库位置(`db.json`) | +| `PORT` | 框架默认值 | 服务端口(示例中为 `20128`) | +| `HOSTNAME` | 框架默认值 | 绑定主机(Docker 默认为 `0.0.0.0`) | +| `NODE_ENV` | 运行时默认值 | 部署时设置 `production` | +| `BASE_URL` |http://localhost:20128` | 云同步作业使用的服务器端内部基础 URL | +| `CLOUD_URL` | `https://9router.com` | 服务器端云同步端点基础 URL | +| `NEXT_PUBLIC_BASE_URL` | `http://localhost:3000` | 向后兼容/公共基础 URL(服务器运行时优先使用 `BASE_URL`) | +| `NEXT_PUBLIC_CLOUD_URL` | `https://9router.com` | 向后兼容/公共云 URL(服务器运行时优先使用 `CLOUD_URL`) | +| `API_KEY_SECRET` | `endpoint-proxy-api-secret` | 生成的 API Key 的 HMAC 密钥 | +| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | 稳定机器 ID 哈希的盐值 | +| `ENABLE_REQUEST_LOGS` | `false` | 在 `logs/` 下启用请求/响应日志 | +| `AUTH_COOKIE_SECURE` | `false` | 强制 `Secure` 认证 cookie(在 HTTPS 反向代理后设置 `true`) | +| `REQUIRE_API_KEY` | `false` | 在 `/v1/*` 路由上强制执行 Bearer API key推荐用于暴露在互联网的部署) | +| `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` | 空 | 上游提供商调用的可选出站代理 | + +注意: +- 也支持小写代理变量:`http_proxy`, `https_proxy`, `all_proxy`, `no_proxy`。 +- `.env` 不会烘焙到 Docker 镜像中(`.dockerignore`);使用 `--env-file` 或 `-e` 注入运行时配置。 +- 在 Windows 上,`APPDATA` 可用于本地存储路径解析。 +- `INSTANCE_NAME` 出现在旧/环境模板中,但目前运行时未使用。 + +### 运行时文件和存储 + +- 主应用状态:`${DATA_DIR}/db.json`(提供商、组合、别名、密钥、设置),由 `src/lib/localDb.js` 管理。 +- 使用历史和日志:`~/.9router/usage.json` 和 `~/.9router/log.txt`,由 `src/lib/usageDb.js` 管理。 +- 可选请求/转换器日志:当 `ENABLE_REQUEST_LOGS=true` 时为 `/logs/...`。 +- 使用存储当前遵循 `~/.9` 路径逻辑,独立于 `DATA_DIR`。 + +
+ +--- + +## 📊 可用模型 + +
+查看所有可用模型 + +**Claude Code (`cc/`)** - Pro/Max: +- `cc/claude-opus-4-6` +- `cc/claude-sonnet-4-5-20250929` +- `cc/claude-haiku-4-5-20251001` + +**Codex (`cx/`)** - Plus/Pro: +- `cx/gpt-5.2-codex- `cx/gpt-5.1-codex-max` + +**Gemini CLI (`gc/`)** - 免费: +- `gc/gemini-3-flash-preview` +- `gc/gemini-2.5-pro` + +**GitHub Copilot (`gh/`)**: +- `gh/gpt-5` +- `gh/claude-4.5-sonnet` + +**GLM (`glm/`)** - $0.6/1M: +- `glm/glm-4.7` + +**MiniMax (`minimax/`)** - $0.2/1M: +- `imax/MiniMax-M2.1` + +**iFlow (`if/`)** - 免费: +- `if/kimi-k2-thinking` +- `if/qwen3-coder-plus` +- `if/deepseek-r1` + +**Qwen (`qw/`)** - 免费: +- `qw/qwen3-coder-plus` +- `qw/qwen3-coder-flash` + +**Kiro (`kr/`)** - 免费: +- `kr/claude-sonnet-4.5` +- `kr/claude-haiku-4.5` + +
+ +--- + +## 🐛 故障排除 + +“Language model did not provide messages”** +- 提供商配额耗尽 → 检查仪表板配额追踪器 +- 解决方案:使用组合回退或切换到更便宜的层 + +**速率限制** +- 订阅配额用完 → 回退到 GLM/MiniMax +- 添加组合:`cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking` + +**OAuth token 过期** +- 由 9Router 自动刷新 +- 如果问题持续:仪表板 → 提供商 → 重新 + +**高成本** +- 在仪表板中检查使用统计 +- 将主要模型切换为 GLM/MiniMax +- 对非关键任务使用免费层(Gemini CLI, iFlow) + +**仪表板在错误的端口打开** +- 设置 `PORT=20128` 和 `NEXT_PUBLIC_BASE_URL=http://localhost:20128` + +**云端同步错误** +- 验证 `BASE_URL` 指向您正在运行的实例(例如:`http://localhost:20128`) +- 验证 `CLOUD_URL` 指向您预期的云端端点(例如:`https://9router.com`) +- 尽可能保持 `NEXT_PUBLIC_*` 值与服务器端值一致。 + +**云端端点 `stream=false` 返回 500(`Unexpected token 'd'...`)** +- 症状通常出现在公共云端端点(`https://9router.com/v1`)的非流式调用上。 +- 根本原因:上游返回 SSE 负载(`data: ...`)而客户端期望 JSON。 +- 变通方法:对云端直接调用使用 `stream=true`。 +- 当上游返回 `text/event-stream` 时,本地 9Router 运行时包含 SSE→JSON 回退用于非流式调用。 + +**云端显示已连接,但请求仍然失败并显示 `Invalid API key`** +- 从本地仪表板(`/api/keys`)创建新密钥并运行云端同步(`Enable Cloud` 然后 `Sync Now`)。 +- 旧/未同步的密钥即使在本地端点工作的情况下,仍可能在云端返回 `401`。 + +**首次登录不工作** +- 检查 `.env` 中的 `INITIAL_PASSWORD` +- 如果未设置,回退密码是 `123456` + +**`logs/` 下没有请求日志** +- 设置 `ENABLE_REQUEST_LOGS=true` + +--- + +## 🛠️ 技术栈 + +- **运行时**:Node.js 20+ +- **框架**:Next.js 16 +- **UI**:React 19 + Tailwind CSS 4 +- **数据库**:LowDB(基于 JSON 文件) +- **流式传输**:Server-Sent Events (SSE) +- **认证**:OAuth 2.0 (PKCE) + JWT + API Keys + +--- + +## 📝 API 参考 + +### Chat Completions + +```bash +POST httplocalhost:20128/v1/chat/completions +Authorization: Bearer your-api-key +Content-Type: application/json + +{ + "model": "cc/claude-opus-4-6", + "messages": [ + {"role": "user", "content": "Write a function to..."} + ], + "stream": true +} +``` + +### 列出模型 + +```bash +GET http://localhost:20128/v1/models +Authorization: Bearer your-api-key + +→ Returns all models + combos in OpenAI format +``` + +### 兼容性端点 + +- ` /v1/chat/completions` +- `POST /v1/messages` +- `POST /v1/responses` +- `GET /v1/models` +- `POST /v1/messages/count_tokens` +- `GET /v1beta/models` +- `POST /v1beta/models/{...path}`(Gemini 风格 `generateContent`) +- `POST /v1/api/chat`(Ollama 风格转换路径) + +### 云端验证脚本 + +在 `tester/security/` 下添加了测试脚本: + +- `tester/security/test-docker-hardening.sh` + - 构建 Docker 镜像并验证加固检查(`/api/cloud/auth` 认证保护、`REQUIRE_API_KEY`、安全认证 cookie 行为)。 +- `tester/security/test-cloud-openai-compatible.sh` + - 使用提供的模型/密钥向云端端点(`https://9router.com/v1/chat/completions`)发送直接的 OpenAI 兼容请求。 +- `tester/security/test-cloud-sync-and-call.sh` + - 端到端流程:创建本地密钥 -> 启用/同步云端 -> 带重试调用云端端点。 + - 包含使用 `stream` 的回退检查,以区分认证错误和非流式解析问题。 + +云端测试脚本的安全说明: + +- 永远不要在脚本/提交中硬编码真实的 API 密钥。 +- 仅通过环境变量提供密钥: + - `API_KEY`, `CLOUD_API_KEY`, 或 `OPENAI_API_KEY`(由 `test-cloud-openai-compatible.sh` 支持) +- 示例: + +```bash +OPENAI_API_KEY="your-cloud-key" bash tester/security/test-cloud-openai-compatible.sh +``` + +最近验证的预期行为: + +- 本地运行时(`http://127.0.0.1:20128/v1/chat/completions`):使用 `stream=false` 和 `stream=true` 都可以工作。 +- Docker 运行时(容器暴露的相同 API 路径):加固检查通过,云端认证保护工作,启用时严格 API 密钥模式工作。 +- 公共云端端点(`https://9router.com/v1/chat/completions`): + - `stream=true`:预期成功(返回 SSE 块)。 + - `stream=false`:当上游向非流式客户端路径返回 SSE 内容时,可能失败并显示 `500` + 解析错误(`Unexpected token 'd'`)。 + +### 仪表板和管理 API + +- 认证/设置:`/api/auth/login`, `/api/auth/logout`, `/api/settings`, `/api/settings/require-login` +- 提供商管理:`/api/providers`, `/api/providers/[id]`, `/api/providers/[id]/test`, `/api/providers/[id]/models`, `/api/providers/validate`, `/api/provider-nodes*` +- OAuth 流程:`/api/oauth/[provider]/[action]`(+ 特定提供商导入如 Cursor/Kiro) + 路由配置:`/api/models/alias`, `/api/combos*`, `/api/keys*`, `/api/pricing` +- 使用/日志:`/api/usage/history`, `/api/usage/logs`, `/api/usage/request-logs`, `/api/usage/[connectionId]` +- 云端同步:`/api/sync/cloud`, `/api/sync/initialize`, `/api/cloud/*` +- CLI 助手:`/api/cli-tools/claude-settings`, `/api/cli-tools/codex-settings`, `/api/cli-tools/droid-settings`, `/api/cli-tools/openaw-settings` + +### 认证行为 + +- 仪表板路由(`/dashboard/*`)使用 `auth_token` cookie 保护。 +- 登录时如果存在保存的密码哈希则使用;否则回退到 `INITIAL_PASSWORD`。 +- `requireLogin` 可以通过 `/api/settings/require-login` 切换。 + +### 请求处理(高级) + +1. 客户端向 `/v1/*` 发送请求。 +2. 路由处理器调用 `handleChat`(`src/sse/handlers/chat.js`)。 +3. 模型被解析直接提供商/模型或别名/组合解析)。 +4. 从本地数据库选择凭据,并进行账户可用性过滤。 +5. `handleChatCore`(`open-sse/handlers/chatCore.js`)检测格式并转换请求。 +6. 提供商执行器发送上游请求。 +7. 需要时将流转换回客户端格式。 +8. 记录使用/日志(`src/lib/usageDb.js`)。 +9. 根据组合规则在提供商/账户/模型错误时应用回退。 + +完整架构参考:[`docs/ARCHITECTURE`](docs/ARCHITECTURE.md) + +--- + +## 📧 支持 + +- **网站**:[9router.com](https://9router.com) +- **GitHub**:[github.com/decolua/9router](https://github.com/decolua/9router) +- **问题**:[github.com/decolua/9router/issues](https://github.com/decolua/9router/issues) + +--- + +## 👥 贡献者 + +感谢所有帮助让 9Router 变得更好的贡献者! + +[![Contributors](https://contrib.rocks/image?repo=decolua/router&max=100&columns=20&anon=1)](https://github.com/decolua/9router/graphs/contributors) + +--- + +## 📊 Star 图表 + +[![Star Chart](https://starchart.cc/decolua/9router.svg?variant=adaptive)](https://starchart.cc/decolua/9router) + +### 如何贡献 + +1. Fork 仓库 +2. 创建您的功能分支(`git checkout -b feature/amazing-feature`) +3. 提交您的更改(`git commit -m 'Add amazing feature'`) +4 推送到分支(`git push origin feature/amazing-feature`) +5. 打开 Pull Request + +详细指南请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。 + +--- + +## 🔀 分支 + +**[OmniRoute](https://github.com/diegosouzapw/OmniRoute)** — 9Router 的全功能 TypeScript 分支。添加了 36+ 提供商、4 层自动回退、多模态 API(图像、嵌入、音频、TTS)、熔断器、语义缓存、LLM 评估和精美的仪表板。8+ 单元测试。通过 npm 和 Docker 可用。 + +--- + +## 🙏 致谢 + +特别感谢 **CLIProxyAPI** - 启发这个 JavaScript 移植的原始 Go 实现。 + +--- + +## 📄 许可证 + +MIT License - 详情请参阅 [LICENSE](LICENSE)。 + +--- + +
+ 用 ❤️ 为 24/7 编程的开发者构建 +
\ No newline at end of file diff --git a/open-sse/translator/request/openai-to-kiro.js b/open-sse/translator/request/openai-to-kiro.js index d9c116e..c388002 100644 --- a/open-sse/translator/request/openai-to-kiro.js +++ b/open-sse/translator/request/openai-to-kiro.js @@ -54,13 +54,17 @@ function convertMessages(messages, tools, model) { description = `Tool: ${name}`; } + const schema = t.function?.parameters || t.parameters || t.input_schema || {}; + // Normalize schema: Kiro requires required[] and proper type/properties + const normalizedSchema = Object.keys(schema).length === 0 + ? { type: "object", properties: {}, required: [] } + : { ...schema, required: schema.required ?? [] }; + return { toolSpecification: { name, description, - inputSchema: { - json: t.function?.parameters || t.parameters || t.input_schema || {} - } + inputSchema: { json: normalizedSchema } } }; }); diff --git a/src/app/(dashboard)/dashboard/translator/page.js b/src/app/(dashboard)/dashboard/translator/page.js index bc7ef33..999c6db 100644 --- a/src/app/(dashboard)/dashboard/translator/page.js +++ b/src/app/(dashboard)/dashboard/translator/page.js @@ -1,499 +1,297 @@ "use client"; -import { useState, useRef, useEffect } from "react"; -import { Card, Button, Select } from "@/shared/components"; +import { useState } from "react"; +import { Card, Button } from "@/shared/components"; import dynamic from "next/dynamic"; -// Dynamically import Monaco Editor (client-side only) const Editor = dynamic(() => import("@monaco-editor/react"), { ssr: false }); -const PROVIDERS = [ - { value: "antigravity", label: "Antigravity" }, - { value: "gemini-cli", label: "Gemini CLI" }, - { value: "claude", label: "Claude" }, - { value: "codex", label: "Codex" }, - { value: "github", label: "GitHub" }, - { value: "qwen", label: "Qwen" }, - { value: "iflow", label: "iFlow AI" }, - { value: "kiro", label: "Kiro AI" }, - { value: "openai", label: "OpenAI" }, - { value: "anthropic", label: "Anthropic" }, - { value: "gemini", label: "Gemini" }, - { value: "openrouter", label: "OpenRouter" }, - { value: "glm", label: "GLM" }, - { value: "kimi", label: "Kimi" }, - { value: "minimax", label: "MiniMax" }, +// 7 steps matching requestLogger files exactly +const STEPS = [ + { id: 1, label: "Client Request", file: "1_req_client.json", lang: "json", desc: "Raw request from client" }, + { id: 2, label: "Source Body", file: "2_req_source.json", lang: "json", desc: "After initial conversion" }, + { id: 3, label: "OpenAI Intermediate", file: "3_req_openai.json", lang: "json", desc: "source → openai" }, + { id: 4, label: "Target Request", file: "4_req_target.json", lang: "json", desc: "openai → target + URL + headers" }, + { id: 5, label: "Provider Response", file: "5_res_provider.txt", lang: "text", desc: "Raw SSE from provider" }, + { id: 6, label: "OpenAI Response", file: "6_res_openai.txt", lang: "text", desc: "target → openai (response)" }, + { id: 7, label: "Client Response", file: "7_res_client.txt", lang: "text", desc: "Final response to client" }, ]; -const STEPS = [ - { id: 1, name: "Client Request", file: "1_req_client.json" }, - { id: 2, name: "Source Format", file: "2_req_source.json" }, - { id: 3, name: "OpenAI Intermediate", file: "3_req_openai.json" }, - { id: 4, name: "Target Format", file: "4_req_target.json" }, - { id: 5, name: "Provider Response", file: "5_res_provider.txt" }, -]; +const EDITOR_OPTIONS = { + minimap: { enabled: false }, + fontSize: 12, + lineNumbers: "on", + scrollBeyondLastLine: false, + wordWrap: "on", + automaticLayout: true, +}; export default function TranslatorPage() { - const [provider, setProvider] = useState("antigravity"); - const [steps, setSteps] = useState({ - 1: "", - 2: "", - 3: "", - 4: "", - 5: "", - }); - const [expanded, setExpanded] = useState({ - 1: true, - 2: false, - 3: false, - 4: false, - 5: false, - }); + const [contents, setContents] = useState({}); + const [expanded, setExpanded] = useState({ 1: true }); const [loading, setLoading] = useState({}); + // Detected from step 1: { provider, model, sourceFormat, targetFormat } + const [meta, setMeta] = useState(null); - const toggleExpand = (stepId) => { - setExpanded({ ...expanded, [stepId]: !expanded[stepId] }); + const setLoad = (key, val) => setLoading(prev => ({ ...prev, [key]: val })); + const setContent = (id, val) => setContents(prev => ({ ...prev, [id]: val })); + const toggle = (id) => setExpanded(prev => ({ ...prev, [id]: !prev[id] })); + + const openNext = (nextId) => setExpanded(prev => { + const next = {}; + STEPS.forEach(s => { next[s.id] = false; }); + next[nextId] = true; + return next; + }); + + // Load file from logs/translator/ + const handleLoad = async (stepId) => { + const step = STEPS.find(s => s.id === stepId); + setLoad(`load-${stepId}`, true); + try { + const res = await fetch(`/api/translator/load?file=${step.file}`); + const data = await res.json(); + if (data.success) { + setContent(stepId, data.content); + if (stepId === 1) await detectMeta(data.content); + } else { + alert(data.error || "File not found"); + } + } catch (e) { + alert(e.message); + } + setLoad(`load-${stepId}`, false); }; - const handleSendToProvider = async () => { - setLoading({ ...loading, "send-provider": true }); + // Step 1: detect provider/format from model field + const detectMeta = async (rawContent) => { try { - const step4Content = steps[4]; - if (!step4Content) { - alert("Please load or generate step 4 content first"); + const body = typeof rawContent === "string" ? JSON.parse(rawContent) : rawContent; + const res = await fetch("/api/translator/translate", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ step: 1, body }) + }); + const data = await res.json(); + if (data.success) setMeta(data.result); + } catch { /* ignore */ } + }; + + const save = (file, content) => fetch("/api/translator/save", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ file, content }) + }).catch(() => {}); + + // Step 1 → Step 3: source → OpenAI intermediate + const handleToOpenAI = async () => { + setLoad("toOpenAI", true); + try { + const raw = contents[1]; + const body = JSON.parse(raw); + // Save input: 1_req_client.json + 2_req_source.json (body only) + save("1_req_client.json", raw); + save("2_req_source.json", JSON.stringify({ timestamp: new Date().toISOString(), headers: {}, body: body.body || body }, null, 2)); + + const res = await fetch("/api/translator/translate", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ step: 2, body }) + }); + const data = await res.json(); + if (!data.success) { alert(data.error); return; } + const str = JSON.stringify(data.result.body, null, 2); + setContent(3, str); + openNext(3); + } catch (e) { alert(e.message); } + setLoad("toOpenAI", false); + }; + + // Step 3 → Step 4: OpenAI → target + build URL/headers + const handleToTarget = async () => { + setLoad("toTarget", true); + try { + const raw = contents[3]; + const openaiBody = JSON.parse(raw); + // Save input: 3_req_openai.json + save("3_req_openai.json", raw); + + const res = await fetch("/api/translator/translate", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ step: 3, body: { ...openaiBody, provider: meta?.provider, model: meta?.model } }) + }); + const data = await res.json(); + if (!data.success) { alert(data.error); return; } + // Embed provider + model so Send works even without meta + const step4Content = { ...data.result, provider: meta?.provider, model: meta?.model }; + setContent(4, JSON.stringify(step4Content, null, 2)); + openNext(4); + } catch (e) { alert(e.message); } + setLoad("toTarget", false); + }; + + // Step 4 → Step 5: send to provider via executor + const handleSend = async () => { + setLoad("send", true); + try { + const raw = contents[4]; + const step4 = JSON.parse(raw); + // Save input: 4_req_target.json + save("4_req_target.json", raw); + + // Read provider/model from step4 content (embedded during build), fallback to meta + const provider = step4.provider || meta?.provider; + const model = step4.model || meta?.model; + + if (!provider || !model) { + alert("Missing provider or model. Please run step 1 first to detect them."); return; } - const body = JSON.parse(step4Content); - - // Get credentials (you may need to prompt user or use stored credentials) - const credentials = { - accessToken: prompt("Enter access token (or leave empty):") || undefined, - apiKey: prompt("Enter API key (or leave empty):") || undefined - }; - const res = await fetch("/api/translator/send", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - provider, - body, - credentials - }) + body: JSON.stringify({ provider, model, body: step4.body || step4 }) }); - const data = await res.json(); - - if (data.success) { - // Update step 5 with provider response - setSteps({ ...steps, 5: data.body }); - - // Save step 5 - await fetch("/api/translator/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - file: "5_res_provider.txt", - content: data.body - }) - }); - - // Expand step 5 - setExpanded({ ...expanded, 4: false, 5: true }); - - alert(`Request sent! Status: ${data.status} ${data.statusText}`); - } else { - alert(data.error || "Failed to send request"); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + alert(err.error || "Send failed"); + return; } - } catch (err) { - alert("Error sending request: " + err.message); - } - setLoading({ ...loading, "send-provider": false }); - }; - const handleLoad = async (stepId) => { - setLoading({ ...loading, [`load-${stepId}`]: true }); - try { - const step = STEPS.find(s => s.id === stepId); - const res = await fetch(`/api/translator/load?file=${step.file}`); - const data = await res.json(); - if (data.success) { - setSteps({ ...steps, [stepId]: data.content }); - } else { - alert(data.error || "Failed to load file"); + // Accumulate streaming response + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let full = ""; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + full += decoder.decode(value, { stream: true }); } - } catch (err) { - alert("Error loading file: " + err.message); - } - setLoading({ ...loading, [`load-${stepId}`]: false }); - }; - const handleLean = (stepId) => { - try { - const content = steps[stepId]; - if (!content) return; - - const obj = JSON.parse(content); - const leaned = leanJSON(obj); - setSteps({ ...steps, [stepId]: JSON.stringify(leaned, null, 2) }); - } catch (err) { - alert("Error parsing JSON: " + err.message); - } - }; + setContent(5, full); + openNext(5); - const handleFormat = (stepId) => { - try { - const content = steps[stepId]; - if (!content) return; - - const obj = JSON.parse(content); - setSteps({ ...steps, [stepId]: JSON.stringify(obj, null, 2) }); - } catch (err) { - alert("Error parsing JSON: " + err.message); - } - }; - - const handleCopy = async (stepId) => { - try { - const content = steps[stepId]; - if (!content) return; - - await navigator.clipboard.writeText(content); - alert("Copied to clipboard!"); - } catch (err) { - alert("Error copying: " + err.message); - } - }; - - const handleUpdate = async (stepId) => { - setLoading({ ...loading, [`update-${stepId}`]: true }); - try { - const step = STEPS.find(s => s.id === stepId); - const res = await fetch("/api/translator/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - file: step.file, - content: steps[stepId] - }) - }); - const data = await res.json(); - if (data.success) { - alert("File saved successfully"); - } else { - alert(data.error || "Failed to save file"); - } - } catch (err) { - alert("Error saving file: " + err.message); - } - setLoading({ ...loading, [`update-${stepId}`]: false }); - }; - - const handleSubmit = async (stepId) => { - setLoading({ ...loading, [`submit-${stepId}`]: true }); - try { - // 1. Save current step - const currentStep = STEPS.find(s => s.id === stepId); + // Save to logs/translator/5_res_provider.txt await fetch("/api/translator/save", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - file: currentStep.file, - content: steps[stepId] - }) + body: JSON.stringify({ file: "5_res_provider.txt", content: full }) }); + } catch (e) { alert(e.message); } + setLoad("send", false); + }; - // Step 4: Send to provider instead of translate - if (stepId === 4) { - const res = await fetch("/api/translator/send", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - provider, - body: JSON.parse(steps[4]) - }) - }); - - if (!res.ok) { - const errorData = await res.json(); - alert(errorData.error || "Failed to send request"); - setLoading({ ...loading, [`submit-${stepId}`]: false }); - return; - } + const handleCopy = async (id) => { + if (!contents[id]) return; + await navigator.clipboard.writeText(contents[id]); + }; - // Read streaming response - const reader = res.body.getReader(); - const decoder = new TextDecoder(); - let fullResponse = ""; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value, { stream: true }); - fullResponse += chunk; - } - - // Save to step 5 - setSteps({ ...steps, 5: fullResponse }); - - await fetch("/api/translator/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - file: "5_res_provider.txt", - content: fullResponse - }) - }); - - setExpanded({ ...expanded, [stepId]: false, 5: true }); - alert("Request sent to provider successfully!"); - } else { - // Steps 1-3: Translate to next step - const res = await fetch("/api/translator/translate", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - step: stepId, - provider, - body: JSON.parse(steps[stepId]) - }) - }); - const data = await res.json(); - - if (data.success) { - const nextStepId = stepId + 1; - const nextContent = JSON.stringify(data.result, null, 2); - - setSteps({ ...steps, [nextStepId]: nextContent }); - - const nextStep = STEPS.find(s => s.id === nextStepId); - await fetch("/api/translator/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - file: nextStep.file, - content: nextContent - }) - }); - - setExpanded({ ...expanded, [stepId]: false, [nextStepId]: true }); - } else { - alert(data.error || "Translation failed"); - } - } - } catch (err) { - alert("Error: " + err.message); - } - setLoading({ ...loading, [`submit-${stepId}`]: false }); + const handleFormat = (id) => { + try { + const obj = JSON.parse(contents[id]); + setContent(id, JSON.stringify(obj, null, 2)); + } catch { /* not JSON, skip */ } + }; + + // Render action button per step + const getAction = (stepId) => { + if (stepId === 1) return ; + if (stepId === 3) return ; + if (stepId === 4) return ; + return null; }; return ( -
-
+
+ {/* Header */} +

Translator Debug

-

- Debug translation flow between formats -

+

Replay request flow — matches log files

+ {meta && ( +
+ + arrow_forward + + + +
+ )}
- {/* Provider Selector */} - -
-
- -