mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
docs(i18n): add Swedish locale translations and i18n QA tooling
Add complete Swedish (sv) translation for all documentation files including API Reference, README, and guides. Introduce automated i18n QA infrastructure with visual regression testing across multiple viewports and locales to validate translations.
This commit is contained in:
441
docs/i18n/sv/API_REFERENCE.md
Normal file
441
docs/i18n/sv/API_REFERENCE.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# API-referens
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
|
||||
|
||||
Fullständig referens för alla OmniRoute API-slutpunkter.
|
||||
|
||||
---
|
||||
|
||||
## Innehållsförteckning
|
||||
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Semantic Cache](#semantic-cache)
|
||||
- [Dashboard & Management](#dashboard--management)
|
||||
- [Request Processing](#request-processing)
|
||||
- [Authentication](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## Chattavslut
|
||||
|
||||
```bash
|
||||
POST /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
|
||||
}
|
||||
```
|
||||
|
||||
### Anpassade rubriker
|
||||
|
||||
| Rubrik | Riktning | Beskrivning |
|
||||
| ------------------------ | -------- | ----------------------------------------- |
|
||||
| `X-OmniRoute-No-Cache` | Begäran | Ställ in på `true` för att kringgå cache |
|
||||
| `X-OmniRoute-Progress` | Begäran | Ställ in på `true` för framstegshändelser |
|
||||
| `Idempotency-Key` | Begäran | Dedup-nyckel (5s fönster) |
|
||||
| `X-Request-Id` | Begäran | Alternativ dedup-nyckel |
|
||||
| `X-OmniRoute-Cache` | Svar | `HIT` eller `MISS` (icke-streaming) |
|
||||
| `X-OmniRoute-Idempotent` | Svar | `true` om deduplicerad |
|
||||
| `X-OmniRoute-Progress` | Svar | `enabled` om förloppsspårning på |
|
||||
|
||||
---
|
||||
|
||||
## Inbäddningar
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
Tillgängliga leverantörer: Nebius, OpenAI, Mistral, Together AI, Fireworks, NVIDIA.
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bildgenerering
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
Tillgängliga leverantörer: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI.
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lista modeller
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kompatibilitetsslutpunkter
|
||||
|
||||
| Metod | Väg | Format |
|
||||
| ----- | --------------------------- | ------------------------ |
|
||||
| POST | `/v1/chat/completions` | OpenAI |
|
||||
| POST | `/v1/messages` | Antropisk |
|
||||
| POST | `/v1/responses` | OpenAI-svar |
|
||||
| POST | `/v1/embeddings` | OpenAI |
|
||||
| POST | `/v1/images/generations` | OpenAI |
|
||||
| FÅ | `/v1/models` | OpenAI |
|
||||
| POST | `/v1/messages/count_tokens` | Antropisk |
|
||||
| FÅ | `/v1beta/models` | Tvillingarna |
|
||||
| POST | `/v1beta/models/{...path}` | Gemini generera innehåll |
|
||||
| POST | `/v1/api/chat` | Ollama |
|
||||
|
||||
### Dedikerade leverantörsrutter
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
Providerprefixet läggs till automatiskt om det saknas. Omatchade modeller returnerar `400`.
|
||||
|
||||
---
|
||||
|
||||
## Semantisk cache
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
Exempel på svar:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard & Management
|
||||
|
||||
### Autentisering
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ----------------------------- | ------- | ---------------------- |
|
||||
| `/api/auth/login` | POST | Logga in |
|
||||
| `/api/auth/logout` | POST | Logga ut |
|
||||
| `/api/settings/require-login` | GET/PUT | Växla inloggning krävs |
|
||||
|
||||
### Leverantörshantering
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ---------------------------- | ---------------- | ------------------------------------ |
|
||||
| `/api/providers` | GET/POSTA | Lista / skapa leverantörer |
|
||||
| `/api/providers/[id]` | GET/PUT/DELETE | Hantera en leverantör |
|
||||
| `/api/providers/[id]/test` | POST | Testa leverantörsanslutning |
|
||||
| `/api/providers/[id]/models` | FÅ | Lista leverantörsmodeller |
|
||||
| `/api/providers/validate` | POST | Validera leverantörens konfiguration |
|
||||
| `/api/provider-nodes*` | Olika | Leverantörsnodhantering |
|
||||
| `/api/provider-models` | GET/POSTA/RADERA | Anpassade modeller |
|
||||
|
||||
### OAuth-flöden
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| -------------------------------- | ----- | ------------------------- |
|
||||
| `/api/oauth/[provider]/[action]` | Olika | Leverantörsspecifik OAuth |
|
||||
|
||||
### Routing & Config
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------------- | --------- | ------------------------------------ |
|
||||
| `/api/models/alias` | GET/POSTA | Modellalias |
|
||||
| `/api/models/catalog` | FÅ | Alla modeller efter leverantör + typ |
|
||||
| `/api/combos*` | Olika | Kombinationshantering |
|
||||
| `/api/keys*` | Olika | API-nyckelhantering |
|
||||
| `/api/pricing` | FÅ | Modellprissättning |
|
||||
|
||||
### Användning och analys
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------------------- | ----- | ------------------------- |
|
||||
| `/api/usage/history` | FÅ | Användningshistorik |
|
||||
| `/api/usage/logs` | FÅ | Användningsloggar |
|
||||
| `/api/usage/request-logs` | FÅ | Loggar på begäran-nivå |
|
||||
| `/api/usage/[connectionId]` | FÅ | Användning per anslutning |
|
||||
|
||||
### Inställningar
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ------------------------------- | ------- | ----------------------------------- |
|
||||
| `/api/settings` | GET/PUT | Allmänna inställningar |
|
||||
| `/api/settings/proxy` | GET/PUT | Nätverksproxykonfiguration |
|
||||
| `/api/settings/proxy/test` | POST | Testa proxyanslutning |
|
||||
| `/api/settings/ip-filter` | GET/PUT | IP-tillståndslista/blockeringslista |
|
||||
| `/api/settings/thinking-budget` | GET/PUT | Resonera token budget |
|
||||
| `/api/settings/system-prompt` | GET/PUT | Global systemprompt |
|
||||
|
||||
### Övervakning
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ------------------------ | ------------ | ---------------------- |
|
||||
| `/api/sessions` | FÅ | Aktiv sessionsspårning |
|
||||
| `/api/rate-limits` | FÅ | Räntegränser per konto |
|
||||
| `/api/monitoring/health` | FÅ | Hälsokontroll |
|
||||
| `/api/cache` | HÄMTA/RADERA | Cachestatistik / rensa |
|
||||
|
||||
### Säkerhetskopiering & export/import
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------------------- | ----- | ------------------------------------------------------ |
|
||||
| `/api/db-backups` | FÅ | Lista tillgängliga säkerhetskopior |
|
||||
| `/api/db-backups` | SÄTT | Skapa en manuell säkerhetskopia |
|
||||
| `/api/db-backups` | POST | Återställ från en specifik säkerhetskopia |
|
||||
| `/api/db-backups/export` | FÅ | Ladda ner databas som .sqlite-fil |
|
||||
| `/api/db-backups/import` | POST | Ladda upp .sqlite-fil för att ersätta databas |
|
||||
| `/api/db-backups/exportAll` | FÅ | Ladda ner fullständig säkerhetskopia som .tar.gz-arkiv |
|
||||
|
||||
### Cloud Sync
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ---------------------- | ----- | ------------------------------ |
|
||||
| `/api/sync/cloud` | Olika | Molnsynkroniseringsoperationer |
|
||||
| `/api/sync/initialize` | POST | Initiera synkronisering |
|
||||
| `/api/cloud/*` | Olika | Molnhantering |
|
||||
|
||||
### CLI-verktyg
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ---------------------------------- | ----- | ------------------- |
|
||||
| `/api/cli-tools/claude-settings` | FÅ | Claude CLI status |
|
||||
| `/api/cli-tools/codex-settings` | FÅ | Codex CLI-status |
|
||||
| `/api/cli-tools/droid-settings` | FÅ | Droid CLI-status |
|
||||
| `/api/cli-tools/openclaw-settings` | FÅ | OpenClaw CLI-status |
|
||||
| `/api/cli-tools/runtime/[toolId]` | FÅ | Generisk CLI-körtid |
|
||||
|
||||
CLI-svar inkluderar: `installed`, `runnable`, `command`, `commandPath`, `runtimeMode`, `reason`.
|
||||
|
||||
### Resiliens och hastighetsgränser
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ----------------------- | ------- | ----------------------------------- |
|
||||
| `/api/resilience` | GET/PUT | Skaffa/uppdatera resiliensprofiler |
|
||||
| `/api/resilience/reset` | POST | Återställ brytare |
|
||||
| `/api/rate-limits` | FÅ | Räntegränsstatus per konto |
|
||||
| `/api/rate-limit` | FÅ | Global hastighetsgränskonfiguration |
|
||||
|
||||
### Evals
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| ------------ | --------- | ------------------------------------------ |
|
||||
| `/api/evals` | GET/POSTA | Lista utvärderingssviter / kör utvärdering |
|
||||
|
||||
### Policyer
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------- | ---------------- | -------------------- |
|
||||
| `/api/policies` | GET/POSTA/RADERA | Hantera ruttpolicyer |
|
||||
|
||||
### Efterlevnad
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------------------- | ----- | ----------------------------------------- |
|
||||
| `/api/compliance/audit-log` | FÅ | Granskningslogg för efterlevnad (sista N) |
|
||||
|
||||
### v1beta (Gemini-kompatibel)
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| -------------------------- | ----- | ---------------------------------- |
|
||||
| `/v1beta/models` | FÅ | Lista modeller i Gemini-format |
|
||||
| `/v1beta/models/{...path}` | POST | Gemini `generateContent` slutpunkt |
|
||||
|
||||
Dessa slutpunkter speglar Geminis API-format för klienter som förväntar sig inbyggd Gemini SDK-kompatibilitet.
|
||||
|
||||
### Interna / System API: er
|
||||
|
||||
| Slutpunkt | Metod | Beskrivning |
|
||||
| --------------- | ----- | -------------------------------------------------------------- |
|
||||
| `/api/init` | FÅ | Applikationsinitieringskontroll (används vid första körningen) |
|
||||
| `/api/tags` | FÅ | Ollama-kompatibla modelltaggar (för Ollama-klienter) |
|
||||
| `/api/restart` | POST | Utlösa graciös serveromstart |
|
||||
| `/api/shutdown` | POST | Utlösa graciös serveravstängning |
|
||||
|
||||
> **Obs:** Dessa slutpunkter används internt av systemet eller för Ollama-klientkompatibilitet. De anropas vanligtvis inte av slutanvändare.
|
||||
|
||||
---
|
||||
|
||||
## Ljudtranskription
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
Transkribera ljudfiler med Deepgram eller AssemblyAI.
|
||||
|
||||
**Begäran:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**Svar:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
** Leverantörer som stöds:** `deepgram/nova-3`, `assemblyai/best`.
|
||||
|
||||
**Format som stöds:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
## Ollama-kompatibilitet
|
||||
|
||||
För klienter som använder Ollamas API-format:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
Förfrågningar översätts automatiskt mellan Ollama och interna format.
|
||||
|
||||
---
|
||||
|
||||
## Telemetri
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**Svar:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Budget
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modelltillgänglighet
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bearbetning av begäran
|
||||
|
||||
1. Kunden skickar förfrågan till `/v1/*`
|
||||
2. Rutthanteraren anropar `handleChat`, `handleEmbedding`, `handleAudioTranscription` eller `handleImageGeneration`
|
||||
3. Modellen är löst (direkt leverantör/modell eller alias/kombo)
|
||||
4. Inloggningsuppgifter valda från lokal DB med filtrering av kontotillgänglighet
|
||||
5. För chatt: `handleChatCore` — formatdetektering, översättning, cachekontroll, idempotenskontroll
|
||||
6. Leverantörs exekutor skickar uppströmsbegäran
|
||||
7. Svar översatt till klientformat (chatt) eller returnerat som det är (inbäddningar/bilder/ljud)
|
||||
8. Användning/loggning registrerad
|
||||
9. Fallback gäller vid fel enligt komboregler
|
||||
|
||||
Fullständig arkitekturreferens: [**OMNI_TOKEN_119**](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## Autentisering
|
||||
|
||||
- Dashboard rutter (`/dashboard/*`) använder `auth_token` cookie
|
||||
- Inloggning använder sparad lösenordshash; reserv till `INITIAL_PASSWORD`
|
||||
- `requireLogin` kan växlas via `/api/settings/require-login`
|
||||
- `/v1/*` rutter kräver valfritt Bearer API-nyckel när `REQUIRE_API_KEY=true`
|
||||
781
docs/i18n/sv/ARCHITECTURE.md
Normal file
781
docs/i18n/sv/ARCHITECTURE.md
Normal file
@@ -0,0 +1,781 @@
|
||||
# OmniRoute-arkitektur
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
|
||||
|
||||
_Senast uppdaterad: 2026-02-18_
|
||||
|
||||
## Sammanfattning
|
||||
|
||||
OmniRoute är en lokal AI-routinggateway och instrumentpanel byggd på Next.js.
|
||||
Den tillhandahåller en enda OpenAI-kompatibel slutpunkt (`/v1/*`) och dirigerar trafik över flera uppströmsleverantörer med översättning, reserv, tokenuppdatering och användningsspårning.
|
||||
|
||||
Kärnfunktioner:
|
||||
|
||||
- OpenAI-kompatibel API-yta för CLI/verktyg (28 leverantörer)
|
||||
- Begäran/svar översättning över leverantörsformat
|
||||
- Modellkombination fallback (flermodellsekvens)
|
||||
- Reservkonto på kontonivå (flera konto per leverantör)
|
||||
- Anslutningshantering för OAuth + API-nyckelleverantör
|
||||
- Inbäddningsgenerering via `/v1/embeddings` (6 leverantörer, 9 modeller)
|
||||
- Bildgenerering via `/v1/images/generations` (4 leverantörer, 9 modeller)
|
||||
- Tänk taggparsning (`<think>...</think>`) för resonemangsmodeller
|
||||
- Svarssanering för strikt OpenAI SDK-kompatibilitet
|
||||
- Rollnormalisering (utvecklare→system, system→användare) för kompatibilitet mellan olika leverantörer
|
||||
- Strukturerad utdatakonvertering (json_schema → Gemini responseSchema)
|
||||
- Lokal beständighet för leverantörer, nycklar, alias, kombinationer, inställningar, prissättning
|
||||
- Användnings-/kostnadsspårning och förfrågningsloggning
|
||||
- Valfri molnsynkronisering för synkronisering av flera enheter/tillstånd
|
||||
- IP-godkännandelista/blockeringslista för API-åtkomstkontroll
|
||||
- Tänkande budgethantering (genomföring/auto/custom/adaptiv)
|
||||
- Global systeminjektion
|
||||
- Sessionsspårning och fingeravtryck
|
||||
- Förbättrad prisbegränsning per konto med leverantörsspecifika profiler
|
||||
- Strömbrytarmönster för leverantörens motståndskraft
|
||||
- Åskskyddande flockskydd med mutex-låsning
|
||||
- Signaturbaserad cache för begärandeduplicering
|
||||
- Domänlager: modelltillgänglighet, kostnadsregler, reservpolicy, lockoutpolicy
|
||||
- Beständig domäntillstånd (SQLite-genomskrivningscache för reservdelar, budgetar, lockouter, strömbrytare)
|
||||
- Policymotor för centraliserad förfrågningsutvärdering (lockout → budget → reserv)
|
||||
- Begär telemetri med p50/p95/p99 latensaggregation
|
||||
- Korrelations-ID (X-Request-Id) för spårning från början till slut
|
||||
- Loggning av efterlevnadsrevision med opt-out per API-nyckel
|
||||
- Utvärderingsramverk för LLM kvalitetssäkring
|
||||
- Resilience UI-instrumentpanel med strömbrytarstatus i realtid
|
||||
- Modulära OAuth-leverantörer (12 individuella moduler under `src/lib/oauth/providers/`)
|
||||
|
||||
Primär körtidsmodell:
|
||||
|
||||
- Next.js-apprutter under `src/app/api/*` implementerar både instrumentpanelens API:er och kompatibilitets-API:er
|
||||
- En delad SSE/routingkärna i `src/sse/*` + `open-sse/*` hanterar leverantörsexekvering, översättning, streaming, reserv och användning
|
||||
|
||||
## Omfattning och gränser
|
||||
|
||||
### I omfattning
|
||||
|
||||
- Lokal gateway körtid
|
||||
- Dashboard management API:er
|
||||
- Leverantörsautentisering och tokenuppdatering
|
||||
- Begär översättning och SSE-streaming
|
||||
- Lokal stat + användningsbeständighet
|
||||
- Valfri molnsynkroniseringsorkestrering
|
||||
|
||||
### Utanför räckvidd
|
||||
|
||||
- Implementering av molntjänster bakom `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Leverantör SLA/kontrollplan utanför lokal process
|
||||
- Externa CLI-binärer själva (Claude CLI, Codex CLI, etc.)
|
||||
|
||||
## Systemkontext på hög nivå
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(db.json)]
|
||||
UDB[(usage.json + log.txt)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## Core Runtime Components
|
||||
|
||||
## 1) API och Routing Layer (Next.js App Routes)
|
||||
|
||||
Huvudkataloger:
|
||||
|
||||
- `src/app/api/v1/*` och `src/app/api/v1beta/*` för kompatibilitets-API:er
|
||||
- `src/app/api/*` för hanterings-/konfigurations-API:er
|
||||
- Nästa omskrivning i `next.config.mjs` kartan `/v1/*` till `/api/v1/*`
|
||||
|
||||
Viktiga kompatibilitetsvägar:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — inkluderar anpassade modeller med `custom: true`
|
||||
- `src/app/api/v1/embeddings/route.ts` — inbäddningsgenerering (6 leverantörer)
|
||||
- `src/app/api/v1/images/generations/route.ts` — bildgenerering (4+ leverantörer inkl. Antigravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — dedikerad chatt per leverantör
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — dedikerade inbäddningar per leverantör
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — dedikerade bilder per leverantör
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
Hanteringsdomäner:
|
||||
|
||||
- Auth/inställningar: `src/app/api/auth/*`, `src/app/api/settings/*`
|
||||
- Leverantörer/anslutningar: `src/app/api/providers*`
|
||||
- Leverantörsnoder: `src/app/api/provider-nodes*`
|
||||
- Anpassade modeller: `src/app/api/provider-models` (GET/POST/DELETE)
|
||||
- Modellkatalog: `src/app/api/models/catalog` (GET)
|
||||
- Proxykonfiguration: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth: `src/app/api/oauth/*`
|
||||
- Nycklar/alias/kombinationer/prissättning: `src/app/api/keys*`, `src/app/api/models/alias`, `src/app/api/combos*`, `src/app/api/pricing`
|
||||
- Användning: `src/app/api/usage/*`
|
||||
- Synkronisera/moln: `src/app/api/sync/*`, `src/app/api/cloud/*`
|
||||
- CLI-verktygshjälpare: `src/app/api/cli-tools/*`
|
||||
- IP-filter: `src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- Tänkande budget: `src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- Systemprompt: `src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- Sessioner: `src/app/api/sessions` (GET)
|
||||
- Prisgränser: `src/app/api/rate-limits` (GET)
|
||||
- Motståndskraft: `src/app/api/resilience` (GET/PATCH) — leverantörsprofiler, strömbrytare, hastighetsgränstillstånd
|
||||
- Återställning av motståndskraft: `src/app/api/resilience/reset` (POST) — återställ brytare + nedkylningar
|
||||
- Cachestatistik: `src/app/api/cache/stats` (GET/DELETE)
|
||||
- Modelltillgänglighet: `src/app/api/models/availability` (GET/POST)
|
||||
- Telemetri: `src/app/api/telemetry/summary` (GET)
|
||||
- Budget: `src/app/api/usage/budget` (GET/POST)
|
||||
- Reservkedjor: `src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- Efterlevnadsrevision: `src/app/api/compliance/audit-log` (GET)
|
||||
- Evaler: `src/app/api/evals` (GET/POST), `src/app/api/evals/[suiteId]` (GET)
|
||||
- Policyer: `src/app/api/policies` (GET/POST)
|
||||
|
||||
## 2) SSE + Translation Core
|
||||
|
||||
Huvudflödesmoduler:
|
||||
|
||||
- Inträde: `src/sse/handlers/chat.ts`
|
||||
- Kärnorkestrering: `open-sse/handlers/chatCore.ts`
|
||||
- Leverantörs exekveringsadaptrar: `open-sse/executors/*`
|
||||
- Formatidentifiering/leverantörskonfiguration: `open-sse/services/provider.ts`
|
||||
- Modellanalys/upplösning: `src/sse/services/model.ts`, `open-sse/services/model.ts`
|
||||
- Reservlogik för konto: `open-sse/services/accountFallback.ts`
|
||||
- Översättningsregister: `open-sse/translator/index.ts`
|
||||
- Strömomvandlingar: `open-sse/utils/stream.ts`, `open-sse/utils/streamHandler.ts`
|
||||
- Användningsextraktion/normalisering: `open-sse/utils/usageTracking.ts`
|
||||
- Tänk taggtolkare: `open-sse/utils/thinkTagParser.ts`
|
||||
- Inbäddningshanterare: `open-sse/handlers/embeddings.ts`
|
||||
- Inbäddningsleverantörsregister: `open-sse/config/embeddingRegistry.ts`
|
||||
- Hanterare för bildgenerering: `open-sse/handlers/imageGeneration.ts`
|
||||
- Bildleverantörsregister: `open-sse/config/imageRegistry.ts`
|
||||
- Svarssanering: `open-sse/handlers/responseSanitizer.ts`
|
||||
- Rollnormalisering: `open-sse/services/roleNormalizer.ts`
|
||||
|
||||
Tjänster (affärslogik):
|
||||
|
||||
- Val av konto/poäng: `open-sse/services/accountSelector.ts`
|
||||
- Kontextlivscykelhantering: `open-sse/services/contextManager.ts`
|
||||
- IP-filtertillämpning: `open-sse/services/ipFilter.ts`
|
||||
- Sessionsspårning: `open-sse/services/sessionManager.ts`
|
||||
- Begär deduplicering: `open-sse/services/signatureCache.ts`
|
||||
- Systemprompt injektion: `open-sse/services/systemPrompt.ts`
|
||||
- Tänkande budgethantering: `open-sse/services/thinkingBudget.ts`
|
||||
- Jokertecken modell routing: `open-sse/services/wildcardRouter.ts`
|
||||
- Hantering av prisgränser: `open-sse/services/rateLimitManager.ts`
|
||||
- Strömbrytare: `open-sse/services/circuitBreaker.ts`
|
||||
|
||||
Domänlagermoduler:
|
||||
|
||||
- Modelltillgänglighet: `src/lib/domain/modelAvailability.ts`
|
||||
- Kostnadsregler/budgetar: `src/lib/domain/costRules.ts`
|
||||
- Reservpolicy: `src/lib/domain/fallbackPolicy.ts`
|
||||
- Kombinationslösare: `src/lib/domain/comboResolver.ts`
|
||||
- Lockoutpolicy: `src/lib/domain/lockoutPolicy.ts`
|
||||
- Policymotor: `src/domain/policyEngine.ts` — centraliserad lockout → budget → reservutvärdering
|
||||
- Felkodskatalog: `src/lib/domain/errorCodes.ts`
|
||||
- Begärans ID: `src/lib/domain/requestId.ts`
|
||||
- Timeout för hämtning: `src/lib/domain/fetchTimeout.ts`
|
||||
- Begär telemetri: `src/lib/domain/requestTelemetry.ts`
|
||||
- Efterlevnad/revision: `src/lib/domain/compliance/index.ts`
|
||||
- Eval löpare: `src/lib/domain/evalRunner.ts`
|
||||
- Beständig domäntillstånd: `src/lib/db/domainState.ts` — SQLite CRUD för reservkedjor, budgetar, kostnadshistorik, lockout-tillstånd, strömbrytare
|
||||
|
||||
OAuth-leverantörsmoduler (12 enskilda filer under `src/lib/oauth/providers/`):
|
||||
|
||||
- Registerindex: `src/lib/oauth/providers/index.ts`
|
||||
- Individuella leverantörer: `claude.ts`, `codex.ts`, `gemini.ts`, `antigravity.ts`, `iflow.ts`, `qwen.ts`, **OMNI*TOKEN ***119**, **OMNI_TOKEN **\_119**, `kiro.ts`, `cursor.ts`, `kilocode.ts`, `cline.ts`
|
||||
- Tunt omslag: `src/lib/oauth/providers.ts` — återexport från enskilda moduler
|
||||
|
||||
## 3) Persistenslager
|
||||
|
||||
Primärt tillstånd DB:
|
||||
|
||||
- `src/lib/localDb.ts`
|
||||
- fil: `${DATA_DIR}/db.json` (eller `$XDG_CONFIG_HOME/omniroute/db.json` när inställd, annars `~/.omniroute/db.json`)
|
||||
- enheter: providerConnections, providerNodes, modelAlias, combos, apiKeys, settings, prissättning, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
|
||||
|
||||
Användnings-DB:
|
||||
|
||||
- `src/lib/usageDb.ts`
|
||||
- filer: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- följer samma baskatalogpolicy som `localDb` (`DATA_DIR`, sedan `XDG_CONFIG_HOME/omniroute` när inställd)
|
||||
- uppdelad i fokuserade undermoduler: `migrations.ts`, `usageHistory.ts`, `costCalculator.ts`, `usageStats.ts`, `callLogs.ts`
|
||||
|
||||
Domain State DB (SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — CRUD-operationer för domäntillstånd
|
||||
- Tabeller (skapade i `src/lib/db/core.ts`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
|
||||
- Genomskrivningscachemönster: i minneskartor är auktoritativa under körning; mutationer skrivs synkront till SQLite; tillståndet återställs från DB vid kallstart
|
||||
|
||||
## 4) Auth + Säkerhetsytor
|
||||
|
||||
- Dashboard-cookieauth: `src/proxy.ts`, `src/app/api/auth/login/route.ts`
|
||||
- Generering/verifiering av API-nyckel: `src/shared/utils/apiKey.ts`
|
||||
- Leverantörshemligheter kvarstod i `providerConnections`-poster
|
||||
- Utgående proxystöd via `open-sse/utils/proxyFetch.ts` (env vars) och `open-sse/utils/networkProxy.ts` (konfigurerbart per leverantör eller globalt)
|
||||
|
||||
## 5) Molnsynkronisering
|
||||
|
||||
- Schemaläggare init: `src/lib/initCloudSync.ts`, `src/shared/services/initializeCloudSync.ts`
|
||||
- Periodisk uppgift: `src/shared/services/cloudSyncScheduler.ts`
|
||||
- Kontrollrutt: `src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## Begär livscykel (`/v1/chat/completions`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## Combo + konto reservflöde
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
Reservbeslut drivs av `open-sse/services/accountFallback.ts` med hjälp av statuskoder och felmeddelandeheuristik.
|
||||
|
||||
## OAuth Onboarding och Token Refresh Lifecycle
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
Uppdatering under livetrafik utförs inuti `open-sse/handlers/chatCore.ts` via executorn `refreshCredentials()`.
|
||||
|
||||
## Cloud Sync Lifecycle (Aktivera / Synkronisera / Inaktivera)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
Periodisk synkronisering utlöses av `CloudSyncScheduler` när molnet är aktiverat.
|
||||
|
||||
## Datamodell och lagringskarta
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
Fysiska lagringsfiler:
|
||||
|
||||
- huvudtillstånd: `${DATA_DIR}/db.json` (eller `$XDG_CONFIG_HOME/omniroute/db.json` när inställt, annars `~/.omniroute/db.json`)
|
||||
- användningsstatistik: `${DATA_DIR}/usage.json`
|
||||
- begär loggrader: `${DATA_DIR}/log.txt`
|
||||
- valfria översättare/begäran felsökningssessioner: `<repo>/logs/...`
|
||||
|
||||
## Distributionstopologi
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(db.json)]
|
||||
UsageDB[(usage.json/log.txt)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## Modulmappning (beslutskritisk)
|
||||
|
||||
### Rutt- och API-moduler
|
||||
|
||||
- `src/app/api/v1/*`, `src/app/api/v1beta/*`: kompatibilitets-API:er
|
||||
- `src/app/api/v1/providers/[provider]/*`: dedikerade rutter per leverantör (chatt, inbäddningar, bilder)
|
||||
- `src/app/api/providers*`: leverantör CRUD, validering, testning
|
||||
- `src/app/api/provider-nodes*`: anpassad kompatibel nodhantering
|
||||
- `src/app/api/provider-models`: anpassad modellhantering (CRUD)
|
||||
- `src/app/api/models/catalog`: fullständig modellkatalog API (alla typer grupperade efter leverantör)
|
||||
- `src/app/api/oauth/*`: OAuth/enhetskod flöden
|
||||
- `src/app/api/keys*`: lokal API-nyckellivscykel
|
||||
- `src/app/api/models/alias`: aliashantering
|
||||
- `src/app/api/combos*`: reservkombohantering
|
||||
- `src/app/api/pricing`: åsidosättande av prissättning för kostnadsberäkning
|
||||
- `src/app/api/settings/proxy`: proxykonfiguration (GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test`: test av utgående proxyanslutning (POST)
|
||||
- `src/app/api/usage/*`: API:er för användning och loggar
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*`: molnsynkronisering och molnvända hjälpare
|
||||
- `src/app/api/cli-tools/*`: lokala CLI-konfigurationsförfattare/checkers
|
||||
- `src/app/api/settings/ip-filter`: IP-godkännandelista/blockeringslista (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget`: budgetkonfig för tänkande token (GET/PUT)
|
||||
- `src/app/api/settings/system-prompt`: global systemprompt (GET/PUT)
|
||||
- `src/app/api/sessions`: aktiv sessionslista (GET)
|
||||
- `src/app/api/rate-limits`: räntegränsstatus per konto (GET)
|
||||
|
||||
### Routing and Execution Core
|
||||
|
||||
- `src/sse/handlers/chat.ts`: begäran om analys, kombinationshantering, kontovalsloop
|
||||
- `open-sse/handlers/chatCore.ts`: översättning, exekutorutskick, försök igen/uppdatera hantering, strömkonfiguration
|
||||
- `open-sse/executors/*`: leverantörsspecifikt nätverk och formatbeteende
|
||||
|
||||
### Översättningsregister och formatomvandlare
|
||||
|
||||
- `open-sse/translator/index.ts`: översättarregister och orkestrering
|
||||
- Begär översättare: `open-sse/translator/request/*`
|
||||
- Svarsöversättare: `open-sse/translator/response/*`
|
||||
- Formatkonstanter: `open-sse/translator/formats.ts`
|
||||
|
||||
### Uthållighet
|
||||
|
||||
- `src/lib/localDb.ts`: beständig konfiguration/tillstånd
|
||||
- `src/lib/usageDb.ts`: användningshistorik och rullande förfrågningsloggar
|
||||
|
||||
## Provider Executor Täckning (strategimönster)
|
||||
|
||||
Varje leverantör har en specialiserad exekutor som utökar `BaseExecutor` (i `open-sse/executors/base.ts`), som tillhandahåller URL-byggande, rubrikkonstruktion, återförsök med exponentiell backoff, autentiseringsuppdateringskrokar och `execute()` orkestreringsmetoden.
|
||||
|
||||
| Exekutor | Leverantör(er) | Specialhantering |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
||||
| `DefaultExecutor` | OpenAI, Claude, Gemini, Qwen, iFlow, OpenRouter, GLM, Kimi, MiniMax, DeepSeek, Groq, xAI, Mistral, Perplexity, Together, Fireworks, Cerebras, Cohere, NVIDIA | Dynamisk URL/header-konfiguration per leverantör |
|
||||
| `AntigravityExecutor` | Google Antigravity | Anpassade projekt-/sessions-ID:n, försök igen-efter analys |
|
||||
| `CodexExecutor` | OpenAI Codex | Injicerar systeminstruktioner, tvingar fram resonemang |
|
||||
| `CursorExecutor` | Markör IDE | ConnectRPC-protokoll, Protobuf-kodning, begäran om signering via kontrollsumma |
|
||||
| `GithubExecutor` | GitHub Copilot | Copilot token uppdatering, VSCode-härmar rubriker |
|
||||
| `KiroExecutor` | AWS CodeWhisperer/Kiro | AWS EventStream binärt format → SSE-konvertering |
|
||||
| `GeminiCLIExecutor` | Gemini CLI | Uppdateringscykel för Google OAuth-token |
|
||||
|
||||
Alla andra leverantörer (inklusive anpassade kompatibla noder) använder `DefaultExecutor`.
|
||||
|
||||
## Leverantörskompatibilitetsmatris
|
||||
|
||||
| Leverantör | Format | Auth | Streama | Icke-stream | Token Refresh | Användnings-API |
|
||||
| ---------------- | --------------- | ---------------------- | ---------------- | ----------- | ------------- | --------------------- |
|
||||
| Claude | claude | API-nyckel / OAuth | ✅ | ✅ | ✅ | ⚠️ Endast admin |
|
||||
| Tvillingarna | Tvillingarna | API-nyckel / OAuth | ✅ | ✅ | ✅ | ⚠️ Cloud Console |
|
||||
| Gemini CLI | gemini-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ Cloud Console |
|
||||
| Antigravitation | antigravitation | OAuth | ✅ | ✅ | ✅ | ✅ Full kvot API |
|
||||
| OpenAI | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Codex | openai-svar | OAuth | ✅ tvingad | ❌ | ✅ | ✅ Prisgränser |
|
||||
| GitHub Copilot | openai | OAuth + Copilot Token | ✅ | ✅ | ✅ | ✅ Kvotbilder |
|
||||
| Markör | markören | Anpassad kontrollsumma | ✅ | ✅ | ❌ | ❌ |
|
||||
| Kiro | kiro | AWS SSO OIDC | ✅ (EventStream) | ❌ | ✅ | ✅ Användningsgränser |
|
||||
| Qwen | openai | OAuth | ✅ | ✅ | ✅ | ⚠️ Per förfrågan |
|
||||
| iFlow | openai | OAuth (Grundläggande) | ✅ | ✅ | ✅ | ⚠️ Per förfrågan |
|
||||
| OpenRouter | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| GLM/Kimi/MiniMax | claude | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| DeepSeek | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Groq | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| xAI (Grok) | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Mistral | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Förvirring | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Tillsammans AI | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Fireworks AI | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Cerebras | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| Sammanhålla | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
| NVIDIA NIM | openai | API-nyckel | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
## Formatöversättningstäckning
|
||||
|
||||
Upptäckta källformat inkluderar:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
Målformat inkluderar:
|
||||
|
||||
- OpenAI chatt/svar
|
||||
- Claude
|
||||
- Gemini/Gemini-CLI/Antigravity kuvert
|
||||
- Kiro
|
||||
- Markör
|
||||
|
||||
Översättningar använder **OpenAI som navformat** — alla konverteringar går via OpenAI som mellanliggande:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
Översättningar väljs dynamiskt baserat på källnyttolastens form och leverantörens målformat.
|
||||
|
||||
Ytterligare bearbetningslager i översättningspipelinen:
|
||||
|
||||
- **Responssanering** — Tar bort icke-standardiserade fält från svar i OpenAI-format (både strömmande och icke-strömmande) för att säkerställa strikt SDK-efterlevnad
|
||||
- **Rollnormalisering** — Konverterar `developer` → `system` för icke-OpenAI-mål; slår samman `system` → `user` för modeller som avvisar systemrollen (GLM, ERNIE)
|
||||
- **Tänk taggextraktion** — Parsar `<think>...</think>` block från innehåll till fältet `reasoning_content`
|
||||
- **Structured output** — Konverterar OpenAI `response_format.json_schema` till Gemini's `responseMimeType` + `responseSchema`
|
||||
|
||||
## API-slutpunkter som stöds
|
||||
|
||||
| Slutpunkt | Format | Handlare |
|
||||
| -------------------------------------------------- | ------------------- | --------------------------------------------------------- |
|
||||
| `POST /v1/chat/completions` | OpenAI Chat | `src/sse/handlers/chat.ts` |
|
||||
| `POST /v1/messages` | Claude Meddelanden | Samma hanterare (automatiskt upptäckt) |
|
||||
| `POST /v1/responses` | OpenAI-svar | `open-sse/handlers/responsesHandler.ts` |
|
||||
| `POST /v1/embeddings` | OpenAI Inbäddningar | `open-sse/handlers/embeddings.ts` |
|
||||
| `GET /v1/embeddings` | Modelllista | API-rutt |
|
||||
| `POST /v1/images/generations` | OpenAI bilder | `open-sse/handlers/imageGeneration.ts` |
|
||||
| `GET /v1/images/generations` | Modelllista | API-rutt |
|
||||
| `POST /v1/providers/{provider}/chat/completions` | OpenAI Chat | Dedikerad per leverantör med modellvalidering |
|
||||
| `POST /v1/providers/{provider}/embeddings` | OpenAI Inbäddningar | Dedikerad per leverantör med modellvalidering |
|
||||
| `POST /v1/providers/{provider}/images/generations` | OpenAI bilder | Dedikerad per leverantör med modellvalidering |
|
||||
| `POST /v1/messages/count_tokens` | Claude Token Count | API-rutt |
|
||||
| `GET /v1/models` | OpenAI-modelllista | API-rutt (chatt + inbäddning + bild + anpassade modeller) |
|
||||
| `GET /api/models/catalog` | Katalog | Alla modeller grupperade efter leverantör + typ |
|
||||
| `POST /v1beta/models/*:streamGenerateContent` | Tvillinginfödd | API-rutt |
|
||||
| `GET/PUT/DELETE /api/settings/proxy` | Proxykonfiguration | Nätverksproxykonfiguration |
|
||||
| `POST /api/settings/proxy/test` | Proxyanslutning | Proxy hälsa/anslutningstest slutpunkt |
|
||||
| `GET/POST/DELETE /api/provider-models` | Anpassade modeller | Anpassad modellhantering per leverantör |
|
||||
|
||||
## Bypass-hanterare
|
||||
|
||||
Bypass-hanteraren (`open-sse/utils/bypassHandler.ts`) fångar upp kända "kastningsförfrågningar" från Claude CLI – uppvärmningsping, titelextraktioner och tokenräkningar – och returnerar ett **falskt svar** utan att konsumera uppströmsleverantörstokens. Detta utlöses endast när `User-Agent` innehåller `claude-cli`.
|
||||
|
||||
## Begär Logger Pipeline
|
||||
|
||||
Begäranloggaren (`open-sse/utils/requestLogger.ts`) tillhandahåller en 7-stegs felsökningsloggningspipeline, inaktiverad som standard, aktiverad via `ENABLE_REQUEST_LOGS=true`:
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
Filer skrivs till `<repo>/logs/<session>/` för varje begäranssession.
|
||||
|
||||
## Fellägen och motståndskraft
|
||||
|
||||
## 1) Tillgänglighet för konto/leverantör
|
||||
|
||||
- Nedkylning av leverantörskonto på övergående/hastighets-/auth-fel
|
||||
- reservkonto innan begäran misslyckas
|
||||
- kombimodell fallback när nuvarande modell/leverantörsväg är uttömd
|
||||
|
||||
## 2) Tokens utgång
|
||||
|
||||
- Förkontroll och uppdatera med ett nytt försök för uppdateringsbara leverantörer
|
||||
- 401/403 försök igen efter uppdateringsförsök i kärnvägen
|
||||
|
||||
## 3) Strömsäkerhet
|
||||
|
||||
- frånkopplingsmedveten strömkontroller
|
||||
- översättningsström med end-of-stream-spolning och `[DONE]`-hantering
|
||||
- användningsuppskattning fallback när leverantörens användningsmetadata saknas
|
||||
|
||||
## 4) Molnsynkroniseringsförsämring
|
||||
|
||||
- Synkroniseringsfel dyker upp men den lokala körtiden fortsätter
|
||||
- Schemaläggaren har logik som kan försöka igen, men periodisk exekvering anropar för närvarande synkronisering med ett enda försök som standard
|
||||
|
||||
## 5) Dataintegritet
|
||||
|
||||
- DB-formmigrering/reparation för saknade nycklar
|
||||
- korrupta JSON-återställningsskydd för localDb och usageDb
|
||||
|
||||
## Observerbarhet och operativa signaler
|
||||
|
||||
Källor för synlighet vid körning:
|
||||
|
||||
- konsolloggar från `src/sse/utils/logger.ts`
|
||||
- användningsaggregat per begäran i `usage.json`
|
||||
- textförfrågan status logga in `log.txt`
|
||||
- valfria djupa förfrågningar/översättningsloggar under `logs/` när `ENABLE_REQUEST_LOGS=true`
|
||||
- slutpunkter för användning av instrumentpanelen (`/api/usage/*`) för användargränssnittsförbrukning
|
||||
|
||||
## Säkerhetskänsliga gränser
|
||||
|
||||
- JWT-hemlighet (`JWT_SECRET`) säkrar verifiering/signering av cookies på instrumentpanelen
|
||||
- Initialt reservlösenord (`INITIAL_PASSWORD`, standard `123456`) måste åsidosättas i verkliga distributioner
|
||||
- API-nyckel HMAC-hemlighet (`API_KEY_SECRET`) säkrar genererat lokalt API-nyckelformat
|
||||
- Leverantörshemligheter (API-nycklar/tokens) finns kvar i lokal DB och bör skyddas på filsystemnivå
|
||||
- Slutpunkter för molnsynkronisering är beroende av API-nyckelbehörighet + maskin-id-semantik
|
||||
|
||||
## Miljö- och körtidsmatris
|
||||
|
||||
Miljövariabler som används aktivt av kod:
|
||||
|
||||
- App/auth: `JWT_SECRET`, `INITIAL_PASSWORD`
|
||||
- Lagring: `DATA_DIR`
|
||||
- Kompatibelt nodbeteende: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- Valfri åsidosättning av lagringsbas (Linux/macOS när `DATA_DIR` inte är inställd): `XDG_CONFIG_HOME`
|
||||
- Säkerhetshashing: `API_KEY_SECRET`, `MACHINE_ID_SALT`
|
||||
- Loggning: `ENABLE_REQUEST_LOGS`
|
||||
- Synkronisera/molnwebbadress: `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Utgående proxy: `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` och varianter med små bokstäver
|
||||
- SOCKS5-funktionsflaggor: `ENABLE_SOCKS5_PROXY`, `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- Plattforms-/runtime-hjälpare (inte appspecifik konfiguration): `APPDATA`, `NODE_ENV`, `PORT`, `HOSTNAME`
|
||||
|
||||
## Kända arkitektoniska anteckningar
|
||||
|
||||
1. `usageDb` och `localDb` delar nu samma baskatalogpolicy (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) med äldre filmigrering.
|
||||
2. `/api/v1/route.ts` returnerar en statisk modelllista och är inte den huvudsakliga modellkällan som används av `/v1/models`.
|
||||
3. Request logger skriver fullständiga rubriker/text när den är aktiverad; behandla loggkatalogen som känslig.
|
||||
4. Molnets beteende beror på korrekt `NEXT_PUBLIC_BASE_URL` och molnets slutpunkts tillgänglighet.
|
||||
5. Katalogen `open-sse/` publiceras som `@omniroute/open-sse` **npm workspace-paketet**. Källkoden importerar den via `@omniroute/open-sse/...` (löst av Next.js `transpilePackages`). Filsökvägar i det här dokumentet använder fortfarande katalognamnet `open-sse/` för konsekvens.
|
||||
6. Diagram i instrumentpanelen använder **Recharts** (SVG-baserad) för tillgängliga, interaktiva analysvisualiseringar (stapeldiagram för modellanvändning, leverantörsuppdelningstabeller med framgångsfrekvenser).
|
||||
7. E2E-tester använder **dramatiker** (`tests/e2e/`), körs via `npm run test:e2e`. Enhetstester använder **Node.js testrunner** (`tests/unit/`), körs via `npm run test:plan3`. Källkoden under `src/` är **TypeScript** (`.ts`/`.tsx`); arbetsytan `open-sse/` förblir JavaScript (`.js`).
|
||||
8. Inställningssidan är organiserad i 5 flikar: Säkerhet, Routing (6 globala strategier: fill-first, round-robin, p2c, slumpmässig, minst använda, kostnadsoptimerad), Resiliens (redigerbara hastighetsgränser, strömbrytare, policyer), AI (tänkande budget, systemprompt, promptcache), Advanced (proxy).
|
||||
|
||||
## Checklista för operativ verifiering
|
||||
|
||||
- Bygg från källa: `npm run build`
|
||||
- Bygg Docker-bild: `docker build -t omniroute .`
|
||||
- Starta tjänsten och verifiera:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- CLI-målbasadressen ska vara `http://<host>:20128/v1` när `PORT=20128`
|
||||
589
docs/i18n/sv/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/sv/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# omniroute — Kodbasdokumentation
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](../pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](../es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](../fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](../it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](../ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](../zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](../de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](../in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](../th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](../uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](../ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](../ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](../vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](../bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](../da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](../fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](../he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](../hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](../id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](../ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](../ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](../nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](../no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](../pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](../ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](../pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](../sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](../sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](../phi/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> En omfattande, nybörjarvänlig guide till **omniroute** AI-proxyrouter med flera leverantörer.
|
||||
|
||||
---
|
||||
|
||||
## 1. Vad är omniroute?
|
||||
|
||||
omniroute är en **proxyrouter** som sitter mellan AI-klienter (Claude CLI, Codex, Cursor IDE, etc.) och AI-leverantörer (Anthropic, Google, OpenAI, AWS, GitHub, etc.). Det löser ett stort problem:
|
||||
|
||||
> **Olika AI-klienter talar olika "språk" (API-format), och olika AI-leverantörer förväntar sig också olika "språk".** omniroute översätter mellan dem automatiskt.
|
||||
|
||||
Tänk på det som en universell översättare vid Förenta Nationerna - vilken delegat som helst kan tala vilket språk som helst, och översättaren konverterar det till vilken annan delegat som helst.
|
||||
|
||||
---
|
||||
|
||||
## 2. Arkitekturöversikt
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### Kärnprincip: Översättning av nav och eker
|
||||
|
||||
All formatöversättning går genom **OpenAI-formatet som navet**:
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
Det betyder att du bara behöver **N översättare** (en per format) istället för **N²** (varje par).
|
||||
|
||||
---
|
||||
|
||||
## 3. Projektets struktur
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Uppdelning av modul för modul
|
||||
|
||||
### 4.1 Config (`open-sse/config/`)
|
||||
|
||||
Den **enda källan till sanning** för alla leverantörskonfigurationer.
|
||||
|
||||
| Arkiv | Syfte |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `constants.ts` | `PROVIDERS` objekt med bas-URL:er, OAuth-referenser (standard), rubriker och standardsystemuppmaningar för varje leverantör. Definierar även `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG` och `SKIP_PATTERNS`. |
|
||||
| `credentialLoader.ts` | Laddar externa referenser från `data/provider-credentials.json` och slår samman dem över de hårdkodade standardinställningarna i `PROVIDERS`. Håller hemligheter utom källans kontroll samtidigt som bakåtkompatibiliteten bibehålls. |
|
||||
| `providerModels.ts` | Centralt modellregister: kartleverantörsalias → modell-ID:n. Funktioner som `getModels()`, `getProviderByAlias()`. |
|
||||
| `codexInstructions.ts` | Systeminstruktioner injicerade i Codex-förfrågningar (redigeringsbegränsningar, sandlåderegler, godkännandepolicyer). |
|
||||
| `defaultThinkingSignature.ts` | Standard "tänkande" signaturer för Claude och Gemini modeller. |
|
||||
| `ollamaModels.ts` | Schemadefinition för lokala Ollama-modeller (namn, storlek, familj, kvantisering). |
|
||||
|
||||
#### Behörighetsladdningsflöde
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Exekutorer (`open-sse/executors/`)
|
||||
|
||||
Exekutorer kapslar in **leverantörsspecifik logik** med hjälp av **Strategy Pattern**. Varje executor åsidosätter basmetoder efter behov.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
| Exekutor | Leverantör | Nyckelspecialiseringar |
|
||||
| ---------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `base.ts` | — | Abstrakt bas: URL-byggnad, rubriker, logik för försök igen, uppdatering av autentiseringsuppgifter |
|
||||
| `default.ts` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Generisk OAuth-tokenuppdatering för standardleverantörer |
|
||||
| `antigravity.ts` | Google Cloud Code | Generering av projekt-/sessions-ID, reserv för flera webbadresser, anpassad försök att analysera igen från felmeddelanden ("återställ efter 2h7m23s") |
|
||||
| `cursor.ts` | Markör IDE | **Mest komplex**: SHA-256 kontrollsummaauth, Protobuf-begärankodning, binär EventStream → SSE-svarsanalys |
|
||||
| `codex.ts` | OpenAI Codex | Injicerar systeminstruktioner, hanterar tankenivåer, tar bort parametrar som inte stöds |
|
||||
| `gemini-cli.ts` | Google Gemini CLI | Byggande av anpassad webbadress (`streamGenerateContent`), uppdatering av Google OAuth-token |
|
||||
| `github.ts` | GitHub Copilot | Dubbla tokensystem (GitHub OAuth + Copilot-token), VSCode-huvudhärmare |
|
||||
| `kiro.ts` | AWS CodeWhisperer | AWS EventStream binär analys, AMZN-händelseramar, tokenuppskattning |
|
||||
| `index.ts` | — | Fabrik: maps provider name → executor class, with default fallback |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Hanterare (`open-sse/handlers/`)
|
||||
|
||||
**orkestreringsskiktet** — koordinerar översättning, exekvering, streaming och felhantering.
|
||||
|
||||
| Arkiv | Syfte |
|
||||
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `chatCore.ts` | **Centralorkester** (~600 rader). Hanterar hela begärans livscykel: formatdetektering → översättning → exekutorutskick → strömmande/icke-strömmande svar → tokenuppdatering → felhantering → användningsloggning. |
|
||||
| `responsesHandler.ts` | Adapter för OpenAI:s Responses API: konverterar svarsformat → Chattavslut → skickar till `chatCore` → konverterar SSE tillbaka till svarsformat. |
|
||||
| `embeddings.ts` | Inbäddningsgenereringshanterare: löser inbäddningsmodell → leverantör, skickar till leverantörs API, returnerar OpenAI-kompatibelt inbäddningssvar. Stöder 6+ leverantörer. |
|
||||
| `imageGeneration.ts` | Bildgenereringshanterare: löser bildmodell → leverantör, stöder OpenAI-kompatibla, Gemini-bild (Antigravity) och reservläge (Nebius). Returnerar base64- eller URL-bilder. |
|
||||
|
||||
#### Begär livscykel (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Tjänster (`open-sse/services/`)
|
||||
|
||||
Affärslogik som stödjer hanterarna och utförarna.
|
||||
|
||||
| Arkiv | Syfte |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `provider.ts` | **Formatdetektering** (`detectFormat`): analyserar begäran om kroppsstruktur för att identifiera Claude/OpenAI/Gemini/Antigravity/Responses-format (inkluderar `max_tokens` heuristik för Claude). Dessutom: URL-byggande, header-byggande, normalisering av tankekonfiguration. Stöder `openai-compatible-*` och `anthropic-compatible-*` dynamiska leverantörer. |
|
||||
| `model.ts` | Modellsträngsanalys (`claude/model-name` → `{provider: "claude", model: "model-name"}`), aliasupplösning med kollisionsdetektering, ingångssanering (avvisar vägövergång/kontrolltecken) och modellinformationsupplösning med stöd för asynkront alias getter. |
|
||||
| `accountFallback.ts` | Hantering av hastighetsgränser: exponentiell backoff (1s → 2s → 4s → max 2min), hantering av kontonedkylning, felklassificering (vilka fel utlöser fallback kontra inte). |
|
||||
| `tokenRefresh.ts` | OAuth-tokenuppdatering för **alla leverantörer**: Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth). Inkluderar löftesdedupliceringscache under flygning och försök igen med exponentiell backoff. |
|
||||
| `combo.ts` | **Kombomodeller**: kedjor av reservmodeller. Om modell A misslyckas med ett fallback-berättigat fel, prova modell B, sedan C osv. Returnerar faktiska uppströmsstatuskoder. |
|
||||
| `usage.ts` | Hämtar kvot/användningsdata från leverantörens API:er (GitHub Copilot-kvoter, Antigravity-modellkvoter, Codex-hastighetsgränser, Kiro-användningsuppdelningar, Claude-inställningar). |
|
||||
| `accountSelector.ts` | Smart kontoval med poängalgoritm: tar hänsyn till prioritet, hälsostatus, round-robin-position och nedkylningsläge för att välja det optimala kontot för varje begäran. |
|
||||
| `contextManager.ts` | Begär kontext livscykelhantering: skapar och spårar per begäran kontextobjekt med metadata (begäran ID, tidsstämplar, leverantörsinformation) för felsökning och loggning. |
|
||||
| `ipFilter.ts` | IP-baserad åtkomstkontroll: stöder tillstånds- och blockeringslägen. Validerar klient-IP mot konfigurerade regler innan API-förfrågningar behandlas. |
|
||||
| `sessionManager.ts` | Sessionsspårning med klientfingeravtryck: spårar aktiva sessioner med hashade klientidentifierare, övervakar antalet begäranden och tillhandahåller sessionsstatistik. |
|
||||
| `signatureCache.ts` | Begär signaturbaserad dedupliceringscache: förhindrar dubbletter av begäranden genom att cachelagra senaste begäransignaturer och returnera cachade svar för identiska förfrågningar inom ett tidsfönster. |
|
||||
| `systemPrompt.ts` | Global systempromptinjektion: lägger till eller lägger till en konfigurerbar systemprompt till alla förfrågningar, med kompatibilitetshantering per leverantör. |
|
||||
| `thinkingBudget.ts` | Hantering av resonerande tokenbudget: stöder passthrough, auto (strip thinking config), anpassade (fast budget) och adaptiva (komplexitetsskalade) lägen för att kontrollera tänkande/resonemangstokens. |
|
||||
| `wildcardRouter.ts` | Jokerteckenmodellmönsterrouting: löser jokerteckenmönster (t.ex. `*/claude-*`) till konkreta leverantör/modellpar baserat på tillgänglighet och prioritet. |
|
||||
|
||||
#### Token Refresh Deduplication
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### Konto reservtillståndsmaskin
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### Kombinerad modellkedja
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Översättare (`open-sse/translator/`)
|
||||
|
||||
**formatöversättningsmotorn** använder ett självregistrerande pluginsystem.
|
||||
|
||||
#### Arkitektur
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
| Katalog | Filer | Beskrivning |
|
||||
| ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `request/` | 8 översättare | Konvertera begärandekroppar mellan format. Varje fil självregistreras via `register(from, to, fn)` vid import. |
|
||||
| `response/` | 7 översättare | Konvertera strömmande svarsbitar mellan format. Hanterar SSE-händelsetyper, tankeblock, verktygsanrop. |
|
||||
| `helpers/` | 6 hjälpare | Delade verktyg: `claudeHelper` (extrahering av systemprompt, tankekonfiguration), `geminiHelper` (mappning av delar/innehåll), `openaiHelper` (formatfiltrering), `toolCallHelper` (ID-generering, injektion av svar saknas), **OMNI\_**TO, **OMNI\_**TO,\_\_\_KEN. |
|
||||
| `index.ts` | — | Översättningsmotor: `translateRequest()`, `translateResponse()`, statlig ledning, register. |
|
||||
| `formats.ts` | — | Formatkonstanter: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
|
||||
|
||||
#### Nyckeldesign: Självregistrerande plugins
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 Utils (`open-sse/utils/`)
|
||||
|
||||
| Arkiv | Syfte |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `error.ts` | Byggande av felsvar (OpenAI-kompatibelt format), uppströms felanalys, Antigravity-återförsöksextraktion från felmeddelanden, SSE-felströmning. |
|
||||
| `stream.ts` | **SSE Transform Stream** — kärnan för streaming. Två lägen: `TRANSLATE` (översättning i fullformat) och `PASSTHROUGH` (normalisera + extrahera användning). Hanterar chunkbuffring, användningsuppskattning, spårning av innehållslängd. Encoder/decoder-instanser per ström undviker delat tillstånd. |
|
||||
| `streamHelpers.ts` | SSE-verktyg på låg nivå: `parseSSELine` (tolerant för blanksteg), `hasValuableContent` (filtrerar tomma bitar för OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (formatmedveten SSE-serialisering med ***OMNI_4-rensning med ***OMNI_4). |
|
||||
| `usageTracking.ts` | Extrahering av tokenanvändning från valfritt format (Claude/OpenAI/Gemini/Responses), uppskattning med separata verktyg/meddelande-char-per-token-förhållanden, bufferttillägg (säkerhetsmarginal för 2000 tokens), formatspecifik fältfiltrering, konsolloggning med ANSI-färger. |
|
||||
| `requestLogger.ts` | Filbaserad förfrågningsloggning (opt-in via `ENABLE_REQUEST_LOGS=true`). Skapar sessionsmappar med numrerade filer: `1_req_client.json` → `7_res_client.txt`. All I/O är asynkron (eld-och-glöm). Maskerar känsliga rubriker. |
|
||||
| `bypassHandler.ts` | Fångar upp specifika mönster från Claude CLI (titelextraktion, uppvärmning, räkning) och returnerar falska svar utan att ringa någon leverantör. Stöder både streaming och icke-streaming. Avsiktligt begränsad till Claude CLI omfattning. |
|
||||
| `networkProxy.ts` | Löser utgående proxy-URL för en given leverantör med prioritet: leverantörsspecifik konfiguration → global konfiguration → miljövariabler (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Stöder `NO_PROXY` undantag. Caches konfiguration för 30s. |
|
||||
|
||||
#### SSE Streaming Pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### Begär Logger Session Struktur
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 Application Layer (`src/`)
|
||||
|
||||
| Katalog | Syfte |
|
||||
| ------------- | -------------------------------------------------------------------------------------- |
|
||||
| `src/app/` | Webbgränssnitt, API-rutter, Express-mellanprogramvara, OAuth-återuppringningshanterare |
|
||||
| `src/lib/` | Databasåtkomst (`localDb.ts`, `usageDb.ts`), autentisering, delad |
|
||||
| `src/mitm/` | Man-in-the-middle-proxyverktyg för att avlyssna leverantörstrafik |
|
||||
| `src/models/` | Databasmodelldefinitioner |
|
||||
| `src/shared/` | Omslag runt öppna-sse-funktioner (leverantör, stream, fel, etc.) |
|
||||
| `src/sse/` | SSE-slutpunktshanterare som kopplar open-sse-biblioteket till Express-rutter |
|
||||
| `src/store/` | Tillståndshantering för applikationer |
|
||||
|
||||
#### Anmärkningsvärda API-rutter
|
||||
|
||||
| Rutt | Metoder | Syfte |
|
||||
| --------------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `/api/provider-models` | GET/POSTA/RADERA | CRUD för anpassade modeller per leverantör |
|
||||
| `/api/models/catalog` | FÅ | Aggregerad katalog över alla modeller (chatt, inbäddning, bild, anpassad) grupperade efter leverantör |
|
||||
| `/api/settings/proxy` | GET/PUT/DELETE | Hierarkisk utgående proxykonfiguration (`global/providers/combos/keys`) |
|
||||
| `/api/settings/proxy/test` | POST | Validerar proxyanslutning och returnerar offentlig IP/latency |
|
||||
| `/v1/providers/[provider]/chat/completions` | POST | Dedikerade chattkompletteringar per leverantör med modellvalidering |
|
||||
| `/v1/providers/[provider]/embeddings` | POST | Dedikerade inbäddningar per leverantör med modellvalidering |
|
||||
| `/v1/providers/[provider]/images/generations` | POST | Dedikerad bildgenerering per leverantör med modellvalidering |
|
||||
| `/api/settings/ip-filter` | GET/PUT | Hantering av IP-tillståndslistor/blockeringslistor |
|
||||
| `/api/settings/thinking-budget` | GET/PUT | Resonemangstokens budgetkonfiguration (passthrough/auto/custom/adaptive) |
|
||||
| `/api/settings/system-prompt` | GET/PUT | Global systeminjektion för alla förfrågningar |
|
||||
| `/api/sessions` | FÅ | Aktiv sessionsspårning och mätvärden |
|
||||
| `/api/rate-limits` | FÅ | Räntegränsstatus per konto |
|
||||
|
||||
---
|
||||
|
||||
## 5. Nyckeldesignmönster
|
||||
|
||||
### 5.1 Hub-and-Speake-översättning
|
||||
|
||||
Alla format översätts genom **OpenAI-formatet som navet**. Att lägga till en ny leverantör kräver bara att man skriver **ett par** översättare (till/från OpenAI), inte N par.
|
||||
|
||||
### 5.2 Exekutorstrategimönster
|
||||
|
||||
Varje leverantör har en dedikerad executor-klass som ärver från `BaseExecutor`. Fabriken i `executors/index.ts` väljer rätt vid körning.
|
||||
|
||||
### 5.3 Självregistrerande pluginsystem
|
||||
|
||||
Översättningsmoduler registrerar sig själva vid import via `register()`. Att lägga till en ny översättare är bara att skapa en fil och importera den.
|
||||
|
||||
### 5.4 Kontoåtgång med exponentiell backoff
|
||||
|
||||
När en leverantör returnerar 429/401/500 kan systemet byta till nästa konto genom att tillämpa exponentiell nedkylning (1s → 2s → 4s → max 2min).
|
||||
|
||||
### 5.5 Combo modellkedjor
|
||||
|
||||
En "combo" grupperar flera `provider/model`-strängar. Om den första misslyckas, återgå automatiskt till nästa.
|
||||
|
||||
### 5.6 Stateful Streaming Translation
|
||||
|
||||
Svarsöversättning upprätthåller tillstånd över SSE-bitar (tänkeblockspårning, verktygsanropsackumulering, innehållsblockindexering) via mekanismen `initState()`.
|
||||
|
||||
### 5.7 Användningssäkerhetsbuffert
|
||||
|
||||
En buffert på 2000 token läggs till rapporterad användning för att förhindra att klienter når kontextfönstergränser på grund av overhead från systemuppmaningar och formatöversättning.
|
||||
|
||||
---
|
||||
|
||||
## 6. Format som stöds
|
||||
|
||||
| Format | Riktning | Identifierare |
|
||||
| --------------------- | ----------- | ------------------ |
|
||||
| OpenAI Chat Slutförda | källa + mål | `openai` |
|
||||
| OpenAI Responses API | källa + mål | `openai-responses` |
|
||||
| Antropisk Claude | källa + mål | `claude` |
|
||||
| Google Tvillingarna | källa + mål | `gemini` |
|
||||
| Google Gemini CLI | endast mål | `gemini-cli` |
|
||||
| Antigravitation | källa + mål | `antigravity` |
|
||||
| AWS Kiro | endast mål | `kiro` |
|
||||
| Markör | endast mål | `cursor` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Leverantörer som stöds
|
||||
|
||||
| Leverantör | Auth Method | Exekutor | Viktiga anmärkningar |
|
||||
| ------------------------ | ------------------------------ | --------------- | --------------------------------------------------------------------- |
|
||||
| Antropisk Claude | API-nyckel eller OAuth | Standard | Använder `x-api-key` header |
|
||||
| Google Tvillingarna | API-nyckel eller OAuth | Standard | Använder `x-goog-api-key` header |
|
||||
| Google Gemini CLI | OAuth | GeminiCLI | Använder `streamGenerateContent` slutpunkt |
|
||||
| Antigravitation | OAuth | Antigravitation | Alternativ för flera webbadresser, anpassad försök att analysera igen |
|
||||
| OpenAI | API-nyckel | Standard | Standardbärare auth |
|
||||
| Codex | OAuth | Codex | Injicerar systeminstruktioner, hanterar tänkande |
|
||||
| GitHub Copilot | OAuth + Copilot-token | Github | Dubbla token, VSCode-huvudhärmar |
|
||||
| Kiro (AWS) | AWS SSO OIDC eller Social | Kiro | Binär EventStream-analys |
|
||||
| Markör IDE | Kontrollsumma auth | Markör | Protobuf-kodning, SHA-256 kontrollsummor |
|
||||
| Qwen | OAuth | Standard | Standardauth |
|
||||
| iFlow | OAuth (Grundläggande + Bärare) | Standard | Dubbla autentiseringshuvud |
|
||||
| OpenRouter | API-nyckel | Standard | Standardbärare auth |
|
||||
| GLM, Kimi, MiniMax | API-nyckel | Standard | Claude-kompatibel, använd `x-api-key` |
|
||||
| `openai-compatible-*` | API-nyckel | Standard | Dynamisk: alla OpenAI-kompatibla slutpunkter |
|
||||
| `anthropic-compatible-*` | API-nyckel | Standard | Dynamisk: valfri Claude-kompatibel slutpunkt |
|
||||
|
||||
---
|
||||
|
||||
## 8. Dataflödessammanfattning
|
||||
|
||||
### Strömningsförfrågan
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### Begäran om icke-streaming
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### Bypass Flow (Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
77
docs/i18n/sv/FEATURES.md
Normal file
77
docs/i18n/sv/FEATURES.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OmniRoute — Dashboard Funktionsgalleri
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../FEATURES.md) | 🇧🇷 [Português (Brasil)](../pt-BR/FEATURES.md) | 🇪🇸 [Español](../es/FEATURES.md) | 🇫🇷 [Français](../fr/FEATURES.md) | 🇮🇹 [Italiano](../it/FEATURES.md) | 🇷🇺 [Русский](../ru/FEATURES.md) | 🇨🇳 [中文 (简体)](../zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](../de/FEATURES.md) | 🇮🇳 [हिन्दी](../in/FEATURES.md) | 🇹🇭 [ไทย](../th/FEATURES.md) | 🇺🇦 [Українська](../uk-UA/FEATURES.md) | 🇸🇦 [العربية](../ar/FEATURES.md) | 🇯🇵 [日本語](../ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](../vi/FEATURES.md) | 🇧🇬 [Български](../bg/FEATURES.md) | 🇩🇰 [Dansk](../da/FEATURES.md) | 🇫🇮 [Suomi](../fi/FEATURES.md) | 🇮🇱 [עברית](../he/FEATURES.md) | 🇭🇺 [Magyar](../hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](../id/FEATURES.md) | 🇰🇷 [한국어](../ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](../ms/FEATURES.md) | 🇳🇱 [Nederlands](../nl/FEATURES.md) | 🇳🇴 [Norsk](../no/FEATURES.md) | 🇵🇹 [Português (Portugal)](../pt/FEATURES.md) | 🇷🇴 [Română](../ro/FEATURES.md) | 🇵🇱 [Polski](../pl/FEATURES.md) | 🇸🇰 [Slovenčina](../sk/FEATURES.md) | 🇸🇪 [Svenska](../sv/FEATURES.md) | 🇵🇭 [Filipino](../phi/FEATURES.md)
|
||||
|
||||
Visuell guide till varje avsnitt av OmniRoute-instrumentpanelen.
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Leverantörer
|
||||
|
||||
Hantera AI-leverantörsanslutningar: OAuth-leverantörer (Claude Code, Codex, Gemini CLI), API-nyckelleverantörer (Groq, DeepSeek, OpenRouter) och gratisleverantörer (iFlow, Qwen, Kiro).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 Combos
|
||||
|
||||
Skapa modell routing-kombinationer med 6 strategier: fyll först, round-robin, kraft-av-två-val, slumpmässig, minst använda och kostnadsoptimerad. Varje combo kedjer flera modeller med automatisk reserv.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 Analytics
|
||||
|
||||
Omfattande användningsanalys med tokenförbrukning, kostnadsberäkningar, aktivitetsvärmekartor, veckofördelningsdiagram och uppdelningar per leverantör.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 Systemhälsa
|
||||
|
||||
Realtidsövervakning: drifttid, minne, version, latenspercentiler (p50/p95/p99), cachestatistik och leverantörs strömbrytartillstånd.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Översättarlekplats
|
||||
|
||||
Fyra lägen för att felsöka API-översättningar: **Lekplats** (formatomvandlare), **Chatttestare** (liveförfrågningar), **Testbänk** (batchtester) och **Live Monitor** (strömning i realtid).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Inställningar
|
||||
|
||||
Allmänna inställningar, systemlagring, säkerhetskopieringshantering (export/import-databas), utseende (mörkt/ljusläge), säkerhet (inkluderar API-ändpunktsskydd och anpassad leverantörsblockering), routing, motståndskraft och avancerad konfiguration.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 CLI-verktyg
|
||||
|
||||
Konfiguration med ett klick för AI-kodningsverktyg: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code och Antigravity.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📝 Begärloggar
|
||||
|
||||
Loggning av förfrågningar i realtid med filtrering efter leverantör, modell, konto och API-nyckel. Visar statuskoder, tokenanvändning, latens och svarsdetaljer.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 API-slutpunkt
|
||||
|
||||
Din enhetliga API-slutpunkt med kapacitetsuppdelning: Chattavslut, inbäddningar, bildgenerering, omrankning, ljudtranskription och registrerade API-nycklar.
|
||||
|
||||

|
||||
219
docs/i18n/sv/TROUBLESHOOTING.md
Normal file
219
docs/i18n/sv/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Felsökning
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](../pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](../es/TROUBLESHOOTING.md) | 🇫🇷 [Français](../fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](../it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](../ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](../zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](../de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](../in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](../th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](../uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](../ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](../ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](../vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](../bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](../da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](../fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](../he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](../hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](../id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](../ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](../ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](../nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](../no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](../pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](../ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](../pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](../sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](../sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](../phi/TROUBLESHOOTING.md)
|
||||
|
||||
Vanliga problem och lösningar för OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Snabbfixar
|
||||
|
||||
| Problem | Lösning |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| Första inloggningen fungerar inte | Markera `INITIAL_PASSWORD` i `.env` (standard: `123456`) |
|
||||
| Instrumentpanelen öppnas vid fel port | Ställ in `PORT=20128` och `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| Inga förfrågningsloggar under `logs/` | Set `ENABLE_REQUEST_LOGS=true` |
|
||||
| EACCES: tillstånd nekad | Ställ in `DATA_DIR=/path/to/writable/dir` för att åsidosätta `~/.omniroute` |
|
||||
| Routingstrategi sparas inte | Uppdatering till v1.4.11+ (Zod-schemafix för inställningsbeständighet) |
|
||||
|
||||
---
|
||||
|
||||
## Leverantörsproblem
|
||||
|
||||
### "Språkmodellen gav inga meddelanden"
|
||||
|
||||
**Orsak:** Leverantörskvoten är slut.
|
||||
|
||||
**Åtgärda:**
|
||||
|
||||
1. Kontrollera instrumentpanelens kvotspårare
|
||||
2. Använd en kombination med reservnivåer
|
||||
3. Byt till billigare/gratis nivå
|
||||
|
||||
### Prisbegränsande
|
||||
|
||||
**Orsak:** Prenumerationskvoten är slut.
|
||||
|
||||
**Åtgärda:**
|
||||
|
||||
- Lägg till reserv: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- Använd GLM/MiniMax som billig backup
|
||||
|
||||
### OAuth-token har löpt ut
|
||||
|
||||
OmniRoute uppdaterar automatiskt tokens. Om problemen kvarstår:
|
||||
|
||||
1. Instrumentpanel → Leverantör → Återanslut
|
||||
2. Ta bort och lägg till leverantörsanslutningen igen
|
||||
|
||||
---
|
||||
|
||||
## Molnproblem
|
||||
|
||||
### Cloud Sync-fel
|
||||
|
||||
1. Verifiera att `BASE_URL` pekar på din löpinstans (t.ex. `http://localhost:20128`)
|
||||
2. Verifiera `CLOUD_URL` punkter till din molnslutpunkt (t.ex. `https://omniroute.dev`)
|
||||
3. Håll `NEXT_PUBLIC_*`-värdena i linje med värden på serversidan
|
||||
|
||||
### Cloud `stream=false` Returnerar 500
|
||||
|
||||
**Symptom:** `Unexpected token 'd'...` på molnets slutpunkt för icke-strömmande samtal.
|
||||
|
||||
**Orsak:** Uppströms returnerar SSE-nyttolast medan klienten förväntar sig JSON.
|
||||
|
||||
**Lösning:** Använd `stream=true` för direkta molnsamtal. Lokal körtid inkluderar SSE→JSON reserv.
|
||||
|
||||
### Cloud säger ansluten men "Ogiltig API-nyckel"
|
||||
|
||||
1. Skapa en ny nyckel från den lokala instrumentpanelen (`/api/keys`)
|
||||
2. Kör molnsynkronisering: Aktivera moln → Synkronisera nu
|
||||
3. Gamla/icke-synkroniserade nycklar kan fortfarande returnera `401` på molnet
|
||||
|
||||
---
|
||||
|
||||
## Docker-problem
|
||||
|
||||
### CLI-verktyget visar inte installerat
|
||||
|
||||
1. Kontrollera körtidsfält: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. För portabelt läge: använd bildmål `runner-cli` (buntade CLI)
|
||||
3. För värdmonteringsläge: ställ in `CLI_EXTRA_PATHS` och montera host bin-katalogen som skrivskyddad
|
||||
4. Om `installed=true` och `runnable=false`: binär hittades men misslyckades med hälsokontrollen
|
||||
|
||||
### Snabb körtidsvalidering
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kostnadsfrågor
|
||||
|
||||
### Höga kostnader
|
||||
|
||||
1. Kontrollera användningsstatistik i Dashboard → Användning
|
||||
2. Byt primärmodell till GLM/MiniMax
|
||||
3. Använd gratis nivå (Gemini CLI, iFlow) för icke-kritiska uppgifter
|
||||
4. Ställ in kostnadsbudgetar per API-nyckel: Dashboard → API-nycklar → Budget
|
||||
|
||||
---
|
||||
|
||||
## Felsökning
|
||||
|
||||
### Aktivera förfrågningsloggar
|
||||
|
||||
Ställ in `ENABLE_REQUEST_LOGS=true` i din `.env`-fil. Loggar visas under katalogen `logs/`.
|
||||
|
||||
### Kontrollera leverantörens hälsa
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### Runtime Storage
|
||||
|
||||
- Huvudstatus: `${DATA_DIR}/db.json` (leverantörer, kombinationer, alias, nycklar, inställningar)
|
||||
- Användning: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- Begäran loggar: `<repo>/logs/...` (när `ENABLE_REQUEST_LOGS=true`)
|
||||
|
||||
---
|
||||
|
||||
## Problem med strömbrytare
|
||||
|
||||
### Leverantören har fastnat i ÖPPET läge
|
||||
|
||||
När en leverantörs strömbrytare är ÖPPEN, blockeras förfrågningar tills nedkylningen går ut.
|
||||
|
||||
**Åtgärda:**
|
||||
|
||||
1. Gå till **Dashboard → Inställningar → Resilience**
|
||||
2. Kontrollera strömbrytarkortet för den berörda leverantören
|
||||
3. Klicka på **Återställ alla** för att rensa alla brytare, eller vänta tills nedkylningen löper ut
|
||||
4. Kontrollera att leverantören faktiskt är tillgänglig innan du återställer
|
||||
|
||||
### Leverantören löser ut strömbrytaren hela tiden
|
||||
|
||||
Om en leverantör upprepade gånger går in i ÖPPET läge:
|
||||
|
||||
1. Kontrollera **Dashboard → Health → Provider Health** för felmönstret
|
||||
2. Gå till **Inställningar → Resiliens → Leverantörsprofiler** och höj feltröskeln
|
||||
3. Kontrollera om leverantören har ändrat API-gränser eller kräver omautentisering
|
||||
4. Granska latenstelemetri — hög latens kan orsaka timeoutbaserade fel
|
||||
|
||||
---
|
||||
|
||||
## Ljudtranskriptionsproblem
|
||||
|
||||
### Felet "Modellen stöds inte".
|
||||
|
||||
- Se till att du använder rätt prefix: `deepgram/nova-3` eller `assemblyai/best`
|
||||
- Kontrollera att leverantören är ansluten i **Dashboard → Leverantörer**
|
||||
|
||||
### Transkription returnerar tom eller misslyckas
|
||||
|
||||
- Kontrollera ljudformat som stöds: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
- Kontrollera att filstorleken ligger inom leverantörens gränser (vanligtvis < 25 MB)
|
||||
- Kontrollera giltigheten av leverantörens API-nyckel i leverantörskortet
|
||||
|
||||
---
|
||||
|
||||
## Översättarfelsökning
|
||||
|
||||
Använd **Dashboard → Översättare** för att felsöka formatöversättningsproblem:
|
||||
|
||||
| Läge | När ska man använda |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| **Lekplats** | Jämför in-/utdataformat sida vid sida — klistra in en misslyckad begäran för att se hur den översätts |
|
||||
| **Chatttestare** | Skicka livemeddelanden och inspektera hela nyttolasten för begäran/svar inklusive rubriker |
|
||||
| **Testbänk** | Kör batchtester över formatkombinationer för att hitta vilka översättningar som är trasiga |
|
||||
| **Live Monitor** | Se förfrågningsflödet i realtid för att fånga intermittenta översättningsproblem |
|
||||
|
||||
### Vanliga formatproblem
|
||||
|
||||
- **Tänketaggar visas inte** — Kontrollera om målleverantören stöder tänkande och inställningen av tänkande budget
|
||||
- **Verktygsanrop avbryts** — Vissa formatöversättningar kan ta bort fält som inte stöds; verifiera i Playground-läge
|
||||
- **Systemprompt saknas** — Claude och Gemini hanterar systemprompter på olika sätt; kontrollera översättningsutdata
|
||||
- **SDK returnerar obearbetad sträng istället för objekt** — Fixat i v1.1.0: Response Sanizer tar nu bort icke-standardiserade fält (`x_groq`, `usage_breakdown`, etc.) som orsakar OpenAI SDK Pydantic valideringsfel
|
||||
- **GLM/ERNIE avvisar rollen `system`** — Fixat i v1.1.0: rollnormaliseraren slår automatiskt samman systemmeddelanden till användarmeddelanden för inkompatibla modeller
|
||||
- **`developer` roll inte igenkänd** — Fixad i v1.1.0: konverteras automatiskt till `system` för icke-OpenAI-leverantörer
|
||||
- **`json_schema` fungerar inte med Gemini** — Fixat i v1.1.0: `response_format` har nu konverterats till Geminis `responseMimeType` + `responseSchema`
|
||||
|
||||
---
|
||||
|
||||
## Resiliensinställningar
|
||||
|
||||
### Den automatiska hastighetsgränsen utlöses inte
|
||||
|
||||
- Automatisk hastighetsgräns gäller endast API-nyckelleverantörer (inte OAuth/prenumeration)
|
||||
- Verifiera att **Inställningar → Motståndskraft → Leverantörsprofiler** har aktiverat automatisk hastighetsgräns
|
||||
- Kontrollera om leverantören returnerar `429` statuskoder eller `Retry-After` rubriker
|
||||
|
||||
### Tuning exponentiell backoff
|
||||
|
||||
Leverantörsprofiler stöder dessa inställningar:
|
||||
|
||||
- **Basfördröjning** — Initial väntetid efter första fel (standard: 1 s)
|
||||
- **Max fördröjning** — Maximalt väntetidstak (standard: 30s)
|
||||
- **Multiplikator** — Hur mycket ska fördröjningen öka per på varandra följande fel (standard: 2x)
|
||||
|
||||
### Anti-dundrande flock
|
||||
|
||||
När många samtidiga förfrågningar träffar en hastighetsbegränsad leverantör, använder OmniRoute mutex + automatisk hastighetsbegränsning för att serialisera förfrågningar och förhindra kaskadfel. Detta är automatiskt för API-nyckelleverantörer.
|
||||
|
||||
---
|
||||
|
||||
## Fortfarande fast?
|
||||
|
||||
- **GitHub-problem**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **Arkitektur**: Se [**OMNI_TOKEN_55**](ARCHITECTURE.md) för interna detaljer
|
||||
- **API-referens**: Se [**OMNI_TOKEN_56**](API_REFERENCE.md) för alla slutpunkter
|
||||
- **Hälsa Dashboard**: Kontrollera **Dashboard → Health** för systemstatus i realtid
|
||||
- **Översättare**: Använd **Dashboard → Översättare** för att felsöka formatproblem
|
||||
698
docs/i18n/sv/USER_GUIDE.md
Normal file
698
docs/i18n/sv/USER_GUIDE.md
Normal file
@@ -0,0 +1,698 @@
|
||||
# Användarhandbok
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](../es/USER_GUIDE.md) | 🇫🇷 [Français](../fr/USER_GUIDE.md) | 🇮🇹 [Italiano](../it/USER_GUIDE.md) | 🇷🇺 [Русский](../ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](../de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](../in/USER_GUIDE.md) | 🇹🇭 [ไทย](../th/USER_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](../ar/USER_GUIDE.md) | 🇯🇵 [日本語](../ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/USER_GUIDE.md) | 🇧🇬 [Български](../bg/USER_GUIDE.md) | 🇩🇰 [Dansk](../da/USER_GUIDE.md) | 🇫🇮 [Suomi](../fi/USER_GUIDE.md) | 🇮🇱 [עברית](../he/USER_GUIDE.md) | 🇭🇺 [Magyar](../hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/USER_GUIDE.md) | 🇰🇷 [한국어](../ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](../nl/USER_GUIDE.md) | 🇳🇴 [Norsk](../no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/USER_GUIDE.md) | 🇷🇴 [Română](../ro/USER_GUIDE.md) | 🇵🇱 [Polski](../pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/USER_GUIDE.md) | 🇸🇪 [Svenska](../sv/USER_GUIDE.md) | 🇵🇭 [Filipino](../phi/USER_GUIDE.md)
|
||||
|
||||
Komplett guide för att konfigurera leverantörer, skapa kombinationer, integrera CLI-verktyg och distribuera OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Innehållsförteckning
|
||||
|
||||
- [Pricing at a Glance](#-pricing-at-a-glance)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Provider Setup](#-provider-setup)
|
||||
- [CLI Integration](#-cli-integration)
|
||||
- [Deployment](#-deployment)
|
||||
- [Available Models](#-available-models)
|
||||
- [Advanced Features](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 Prissättning i en överblick
|
||||
|
||||
| Nivå | Leverantör | Kostnad | Kvotåterställning | Bäst för |
|
||||
| -------------------- | ----------------- | --------------------- | ------------------------ | -------------------------- |
|
||||
| **💳 PRENUMERATION** | Claude Code (Pro) | 20 USD/månad | 5h + veckovis | Har redan prenumererat |
|
||||
| | Codex (Plus/Pro) | 20-200 USD/månad | 5h + veckovis | OpenAI-användare |
|
||||
| | Gemini CLI | **GRATIS** | 180K/månad + 1K/dag | Alla! |
|
||||
| | GitHub Copilot | 10-19 USD/månad | Månatlig | GitHub-användare |
|
||||
| **🔑 API-NYCKEL** | DeepSeek | Betala per användning | Inga | Billigt resonemang |
|
||||
| | Groq | Betala per användning | Inga | Ultrasnabb slutledning |
|
||||
| | xAI (Grok) | Betala per användning | Inga | Grok 4 resonemang |
|
||||
| | Mistral | Betala per användning | Inga | EU-värdade modeller |
|
||||
| | Förvirring | Betala per användning | Inga | Sökförstärkt |
|
||||
| | Tillsammans AI | Betala per användning | Inga | Modeller med öppen källkod |
|
||||
| | Fireworks AI | Betala per användning | Inga | Fast FLUX bilder |
|
||||
| | Cerebras | Betala per användning | Inga | Wafer-skala hastighet |
|
||||
| | Sammanhålla | Betala per användning | Inga | Kommando R+ RAG |
|
||||
| | NVIDIA NIM | Betala per användning | Inga | Företagsmodeller |
|
||||
| **💰 BILLIGT** | GLM-4.7 | $0,6/1M | Dagligen 10:00 | Budget backup |
|
||||
| | MiniMax M2.1 | $0,2/1M | 5-timmars rullande | Billigaste alternativet |
|
||||
| | Kimi K2 | 9 USD/mån lägenhet | 10 miljoner tokens/månad | Förutsägbar kostnad |
|
||||
| **🆓 GRATIS** | iFlow | $0 | Obegränsad | 8 modeller gratis |
|
||||
| | Qwen | $0 | Obegränsad | 3 modeller gratis |
|
||||
| | Kiro | $0 | Obegränsad | Claude gratis |
|
||||
|
||||
**💡 Proffstips:** Börja med Gemini CLI (180K gratis/månad) + iFlow (obegränsat gratis) combo = $0 kostnad!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Användningsfall
|
||||
|
||||
### Fall 1: "Jag har Claude Pro-abonnemang"
|
||||
|
||||
**Problem:** Kvoten går ut oanvänd, hastighetsgränser under tung kodning
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Fall 2: "Jag vill ha noll kostnad"
|
||||
|
||||
**Problem:** Har inte råd med prenumerationer, behöver pålitlig AI-kodning
|
||||
|
||||
```
|
||||
Combo: "free-forever"
|
||||
1. gc/gemini-3-flash (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited free)
|
||||
3. qw/qwen3-coder-plus (unlimited free)
|
||||
|
||||
Monthly cost: $0
|
||||
Quality: Production-ready models
|
||||
```
|
||||
|
||||
### Fall 3: "Jag behöver kodning dygnet runt, inga avbrott"
|
||||
|
||||
**Problem:** Deadlines, har inte råd med driftstopp
|
||||
|
||||
```
|
||||
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. minimax/MiniMax-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)
|
||||
```
|
||||
|
||||
### Fall 4: "Jag vill ha GRATIS AI i OpenClaw"
|
||||
|
||||
**Problem:** Behöver AI-assistent i meddelandeappar, helt gratis
|
||||
|
||||
```
|
||||
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...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Leverantörsinställningar
|
||||
|
||||
### 🔐 Prenumerationsleverantörer
|
||||
|
||||
#### 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
|
||||
```
|
||||
|
||||
**Proffstips:** Använd Opus för komplexa uppgifter, Sonnet för snabbhet. OmniRoute spårar kvot per modell!
|
||||
|
||||
#### OpenAI Codex (Plus/Pro)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/gpt-5.2-codex
|
||||
cx/gpt-5.1-codex-max
|
||||
```
|
||||
|
||||
#### Gemini CLI (GRATIS 180K/månad!)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Gemini CLI
|
||||
→ Google OAuth
|
||||
→ 180K completions/month + 1K/day
|
||||
|
||||
Models:
|
||||
gc/gemini-3-flash-preview
|
||||
gc/gemini-2.5-pro
|
||||
```
|
||||
|
||||
**Bäst värde:** Enorma gratis nivå! Använd detta före betalda nivåer.
|
||||
|
||||
#### GitHub Copilot
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ Monthly reset (1st of month)
|
||||
|
||||
Models:
|
||||
gh/gpt-5
|
||||
gh/claude-4.5-sonnet
|
||||
gh/gemini-3-pro
|
||||
```
|
||||
|
||||
### 💰 Billiga leverantörer
|
||||
|
||||
#### GLM-4.7 (Daglig återställning, $0,6/1M)
|
||||
|
||||
1. Registrera dig: [Zhipu AI](https://open.bigmodel.cn/)
|
||||
2. Hämta API-nyckel från Coding Plan
|
||||
3. Instrumentpanel → Lägg till API-nyckel: Leverantör: `glm`, API-nyckel: `your-key`
|
||||
|
||||
**Användning:** `glm/glm-4.7` — **Proffstips:** Coding Plan erbjuder 3× kvot till 1/7 kostnad! Återställ dagligen 10:00.
|
||||
|
||||
#### MiniMax M2.1 (5 timmars återställning, $0,20/1M)
|
||||
|
||||
1. Registrera dig: [MiniMax](https://www.minimax.io/)
|
||||
2. Hämta API-nyckel → Dashboard → Lägg till API-nyckel
|
||||
|
||||
**Använd:** `minimax/MiniMax-M2.1` — **Proffstips:** Billigaste alternativet för långa sammanhang (1M tokens)!
|
||||
|
||||
#### Kimi K2 ($9/månad platt)
|
||||
|
||||
1. Prenumerera: [Moonshot AI](https://platform.moonshot.ai/)
|
||||
2. Hämta API-nyckel → Dashboard → Lägg till API-nyckel
|
||||
|
||||
**Användning:** `kimi/kimi-latest` — **Proffstips:** Fast $9/månad för 10 miljoner tokens = $0,90/1 miljon effektiv kostnad!
|
||||
|
||||
### 🆓 GRATIS leverantörer
|
||||
|
||||
#### iFlow (8 GRATIS modeller)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect 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 GRATIS modeller)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Qwen → Device code auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### Kiro (Claude FREE)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
Models: kr/claude-sonnet-4.5, kr/claude-haiku-4.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Combos
|
||||
|
||||
### Exempel 1: Maximera prenumeration → Billig backup
|
||||
|
||||
```
|
||||
Dashboard → Combos → Create New
|
||||
|
||||
Name: premium-coding
|
||||
Models:
|
||||
1. cc/claude-opus-4-6 (Subscription primary)
|
||||
2. glm/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### Exempel 2: Endast gratis (noll kostnad)
|
||||
|
||||
```
|
||||
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-integration
|
||||
|
||||
### Markör IDE
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### Claude Code
|
||||
|
||||
Redigera `~/.claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### OpenClaw
|
||||
|
||||
Redigera `~/.openclaw/openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Eller använd Dashboard:** CLI Tools → OpenClaw → Auto-config
|
||||
|
||||
### Cline / Fortsätt / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Implementering
|
||||
|
||||
### VPS-distribution
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### Hamnarbetare
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
För värdintegrerat läge med CLI-binärer, se Docker-sektionen i huvuddokumenten.
|
||||
|
||||
### Miljövariabler
|
||||
|
||||
| Variabel | Standard | Beskrivning |
|
||||
| --------------------- | ------------------------------------ | ------------------------------------------------------ |
|
||||
| `JWT_SECRET` | `omniroute-default-secret-change-me` | JWT-signeringshemlighet (**förändring i produktion**) |
|
||||
| `INITIAL_PASSWORD` | `123456` | Första inloggningslösenordet |
|
||||
| `DATA_DIR` | `~/.omniroute` | Datakatalog (db, användning, loggar) |
|
||||
| `PORT` | ram standard | Serviceport (`20128` i exempel) |
|
||||
| `HOSTNAME` | ram standard | Bind värd (Docker har som standard `0.0.0.0`) |
|
||||
| `NODE_ENV` | runtime default | Ställ in `production` för distribution |
|
||||
| `BASE_URL` | `http://localhost:20128` | Intern bas-URL på serversidan |
|
||||
| `CLOUD_URL` | `https://omniroute.dev` | Bas-URL för molnsynkroniseringsslutpunkt |
|
||||
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | HMAC-hemlighet för genererade API-nycklar |
|
||||
| `REQUIRE_API_KEY` | `false` | Framtvinga Bearer API-nyckel på `/v1/*` |
|
||||
| `ENABLE_REQUEST_LOGS` | `false` | Aktiverar förfrågnings-/svarsloggar |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | Tvinga `Secure` auth-cookie (bakom HTTPS omvänd proxy) |
|
||||
|
||||
För den fullständiga referensen till miljövariabeln, se [README](../README.md).
|
||||
|
||||
---
|
||||
|
||||
## 📊 Tillgängliga modeller
|
||||
|
||||
<details>
|
||||
<summary><b>Visa alla tillgängliga modeller</b></summary>
|
||||
|
||||
**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/`)** — GRATIS: `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: `minimax/MiniMax-M2.1`
|
||||
|
||||
**iFlow (`if/`)** — GRATIS: `if/kimi-k2-thinking`, `if/qwen3-coder-plus`, `if/deepseek-r1`
|
||||
|
||||
**Qwen (`qw/`)** — GRATIS: `qw/qwen3-coder-plus`, `qw/qwen3-coder-flash`
|
||||
|
||||
**Kiro (`kr/`)** — GRATIS: `kr/claude-sonnet-4.5`, `kr/claude-haiku-4.5`
|
||||
|
||||
**DeepSeek (`ds/`)**: `ds/deepseek-chat`, `ds/deepseek-reasoner`
|
||||
|
||||
**Groq (`groq/`)**: `groq/llama-3.3-70b-versatile`, `groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI (`xai/`)**: `xai/grok-4`, `xai/grok-4-0709-fast-reasoning`, `xai/grok-code-mini`
|
||||
|
||||
**Mistral (`mistral/`)**: `mistral/mistral-large-2501`, `mistral/codestral-2501`
|
||||
|
||||
**Förvirring (`pplx/`)**: `pplx/sonar-pro`, `pplx/sonar`
|
||||
|
||||
**Together AI (`together/`)**: `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**Fireworks AI (`fireworks/`)**: `fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**Cerebras (`cerebras/`)**: `cerebras/llama-3.3-70b`
|
||||
|
||||
**Kohere (`cohere/`)**: `cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM (`nvidia/`)**: `nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Avancerade funktioner
|
||||
|
||||
### Anpassade modeller
|
||||
|
||||
Lägg till valfritt modell-ID till valfri leverantör utan att vänta på en appuppdatering:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
Eller använd Dashboard: **Leverantörer → [Leverantör] → Anpassade modeller**.
|
||||
|
||||
### Dedikerade leverantörsrutter
|
||||
|
||||
Ruttförfrågningar direkt till en specifik leverantör med modellvalidering:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
Providerprefixet läggs till automatiskt om det saknas. Omatchade modeller returnerar `400`.
|
||||
|
||||
### Nätverksproxykonfiguration
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**Tillrang:** Nyckelspecifik → Kombinationsspecifik → Leverantörsspecifik → Global → Miljö.
|
||||
|
||||
### Model Catalog API
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
Returnerar modeller grupperade efter leverantör med typer (`chat`, `embedding`, `image`).
|
||||
|
||||
### Cloud Sync
|
||||
|
||||
- Synkronisera leverantörer, kombinationer och inställningar mellan enheter
|
||||
- Automatisk bakgrundssynkronisering med timeout + felsnabb
|
||||
- Föredrar serversidan `BASE_URL`/`CLOUD_URL` i produktion
|
||||
|
||||
### LLM Gateway Intelligence (fas 9)
|
||||
|
||||
- **Semantisk cache** — Autocachar icke-strömmande, temperatur=0 svar (förbikoppla med `X-OmniRoute-No-Cache: true`)
|
||||
- **Begär idempotens** — Avduplicerar förfrågningar inom 5s via `Idempotency-Key` eller `X-Request-Id` header
|
||||
- **Förloppsspårning** — Opt-in SSE `event: progress`-händelser via `X-OmniRoute-Progress: true` header
|
||||
|
||||
---
|
||||
|
||||
### Översättarlekplats
|
||||
|
||||
Åtkomst via **Dashboard → Översättare**. Felsöka och visualisera hur OmniRoute översätter API-förfrågningar mellan leverantörer.
|
||||
|
||||
| Läge | Syfte |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------- |
|
||||
| **Lekplats** | Välj käll-/målformat, klistra in en begäran och se den översatta utdata direkt |
|
||||
| **Chatttestare** | Skicka livechattmeddelanden via proxyn och inspektera hela begäran/svarscykeln |
|
||||
| **Testbänk** | Kör batchtester över flera formatkombinationer för att verifiera översättningens korrekthet |
|
||||
| **Live Monitor** | Se översättningar i realtid när förfrågningar flödar genom proxyn |
|
||||
|
||||
**Användningsfall:**
|
||||
|
||||
- Felsök varför en specifik kombination av klient/leverantör misslyckas
|
||||
- Verifiera att tanketaggar, verktygsanrop och systemuppmaningar översätts korrekt
|
||||
- Jämför formatskillnader mellan OpenAI, Claude, Gemini och Responses API-format
|
||||
|
||||
---
|
||||
|
||||
### Routingstrategier
|
||||
|
||||
Konfigurera via **Dashboard → Inställningar → Routing**.
|
||||
|
||||
| Strategi | Beskrivning |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| **Fyll först** | Använder konton i prioritetsordning – primärt konto hanterar alla förfrågningar tills det inte är tillgängligt |
|
||||
| **Round Robin** | Går igenom alla konton med en konfigurerbar sticky limit (standard: 3 samtal per konto) |
|
||||
| **P2C (Power of Two Choices)** | Väljer 2 slumpmässiga konton och vägar till det friskare — balanserar belastning med medvetenhet om hälsa |
|
||||
| **Slumpmässig** | Väljer slumpmässigt ett konto för varje begäran med Fisher-Yates shuffle |
|
||||
| **Minst använda** | Rutter till kontot med den äldsta `lastUsedAt` tidsstämpeln, fördelar trafiken jämnt |
|
||||
| **Kostnadsoptimerad** | Rutter till kontot med lägst prioritetsvärde, optimerar för lägsta kostnadsleverantörer |
|
||||
|
||||
#### Modelalias med jokertecken
|
||||
|
||||
Skapa jokerteckenmönster för att mappa om modellnamn:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
Jokertecken stöder `*` (alla tecken) och `?` (enkeltecken).
|
||||
|
||||
#### Reservkedjor
|
||||
|
||||
Definiera globala reservkedjor som gäller för alla förfrågningar:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Motståndskraft och effektbrytare
|
||||
|
||||
Konfigurera via **Dashboard → Inställningar → Resilience**.
|
||||
|
||||
OmniRoute implementerar motståndskraft på leverantörsnivå med fyra komponenter:
|
||||
|
||||
1. **Provider Profiles** — Konfiguration per leverantör för:
|
||||
- Feltröskel (hur många fel före öppning)
|
||||
- Nedkylningstid
|
||||
- Känslighet för detektering av hastighetsgräns
|
||||
- Exponentiell backoff-parametrar
|
||||
|
||||
2. **Redigerbara hastighetsgränser** — Standardinställningar på systemnivå som kan konfigureras i instrumentpanelen:
|
||||
- **Requests Per Minute (RPM)** — Maximalt antal förfrågningar per minut och konto
|
||||
- **Minsta tid mellan förfrågningar** — Minsta mellanrum i millisekunder mellan förfrågningar
|
||||
- **Max samtidiga förfrågningar** — Maximalt antal samtidiga förfrågningar per konto
|
||||
- Klicka på **Redigera** för att ändra och sedan på **Spara** eller **Avbryt**. Värden kvarstår via resilience API.
|
||||
|
||||
3. **Circuit Breaker** — Spårar fel per leverantör och öppnar automatiskt kretsen när ett tröskelvärde nås:
|
||||
- **STÄNGD** (frisk) — Begäran flyter normalt
|
||||
- **ÖPPEN** — Leverantören är tillfälligt blockerad efter upprepade fel
|
||||
- **HALF_OPEN** — Testar om leverantören har återhämtat sig
|
||||
|
||||
4. **Policy & Locked Identifiers** — Visar strömbrytarens status och låsta identifierare med tvångsupplåsning.
|
||||
|
||||
5. **Rate Limit Auto-Detection** — Övervakar `429` och `Retry-After` rubriker för att proaktivt undvika att nå leverantörshastighetsgränser.
|
||||
|
||||
**Proffstips:** Använd knappen **Återställ alla** för att rensa alla strömbrytare och nedkylningar när en leverantör återhämtar sig efter ett avbrott.
|
||||
|
||||
---
|
||||
|
||||
### Databasexport/import
|
||||
|
||||
Hantera säkerhetskopiering av databas i **Dashboard → Inställningar → System och lagring**.
|
||||
|
||||
| Åtgärd | Beskrivning |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Exportera databas** | Laddar ned den aktuella SQLite-databasen som en `.sqlite`-fil |
|
||||
| **Exportera alla (.tar.gz)** | Laddar ner ett fullständigt säkerhetskopieringsarkiv inklusive: databas, inställningar, kombinationer, leverantörsanslutningar (inga referenser), API-nyckelmetadata |
|
||||
| **Importera databas** | Ladda upp en `.sqlite` fil för att ersätta den aktuella databasen. En säkerhetskopia före import skapas automatiskt |
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**Importvalidering:** Den importerade filen är validerad för integritet (SQLite pragmakontroll), obligatoriska tabeller (`provider_connections`, `provider_nodes`, `combos`, `api_keys`) och storlek (max 100 MB).
|
||||
|
||||
**Användningsfall:**
|
||||
|
||||
- Migrera OmniRoute mellan maskiner
|
||||
- Skapa externa säkerhetskopior för katastrofåterställning
|
||||
- Dela konfigurationer mellan teammedlemmar (exportera alla → dela arkiv)
|
||||
|
||||
---
|
||||
|
||||
### Inställningar Dashboard
|
||||
|
||||
Inställningssidan är organiserad i 5 flikar för enkel navigering:
|
||||
|
||||
| Tab | Innehåll |
|
||||
| ------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| **Säkerhet** | Inställningar för inloggning/lösenord, IP-åtkomstkontroll, API-auth för `/models` och leverantörsblockering |
|
||||
| **Ruttning** | Global routingstrategi (6 alternativ), jokerteckenmodellalias, reservkedjor, kombinationsstandarder |
|
||||
| **Resiliens** | Leverantörsprofiler, redigerbara hastighetsgränser, strömbrytarstatus, policyer och låsta identifierare |
|
||||
| **AI** | Tänkande budgetkonfiguration, global systempromptinjektion, promptcachestatistik |
|
||||
| **Avancerat** | Global proxykonfiguration (HTTP/SOCKS5) |
|
||||
|
||||
---
|
||||
|
||||
### Kostnader och budgethantering
|
||||
|
||||
Åtkomst via **Dashboard → Kostnader**.
|
||||
|
||||
| Tab | Syfte |
|
||||
| ---------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **Budget** | Ställ in utgiftsgränser per API-nyckel med dagliga/veckovisa/månatliga budgetar och realtidsspårning |
|
||||
| **Priser** | Visa och redigera modellprisposter — kostnad per 1000 in-/utdata-tokens per leverantör |
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**Kostnadsspårning:** Varje begäran loggar tokenanvändning och beräknar kostnaden med hjälp av pristabellen. Visa uppdelningar i **Dashboard → Användning** efter leverantör, modell och API-nyckel.
|
||||
|
||||
---
|
||||
|
||||
### Ljudtranskription
|
||||
|
||||
OmniRoute stöder ljudtranskription via den OpenAI-kompatibla slutpunkten:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
Tillgängliga leverantörer: **Deepgram** (`deepgram/`), **AssemblyAI** (`assemblyai/`).
|
||||
|
||||
Ljudformat som stöds: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
### Kombinerade balanseringsstrategier
|
||||
|
||||
Konfigurera balansering per kombination i **Dashboard → Kombinationer → Skapa/Redigera → Strategi**.
|
||||
|
||||
| Strategi | Beskrivning |
|
||||
| --------------------- | -------------------------------------------------------------------------------------- |
|
||||
| **Round-Robin** | Roterar genom modeller sekventiellt |
|
||||
| **Prioritet** | Försöker alltid den första modellen; faller tillbaka endast på fel |
|
||||
| **Slumpmässig** | Väljer en slumpmässig modell från kombinationen för varje begäran |
|
||||
| **Viktad** | Rutter proportionellt baserade på tilldelade vikter per modell |
|
||||
| **Minst använda** | Rutter till modellen med de minsta senaste förfrågningarna (använder kombinationsmått) |
|
||||
| **Kostnadsoptimerad** | Rutter till den billigaste tillgängliga modellen (använder pristabell) |
|
||||
|
||||
Globala kombinationsstandarder kan ställas in i **Dashboard → Inställningar → Routing → Combo Defaults**.
|
||||
|
||||
---
|
||||
|
||||
### Health Dashboard
|
||||
|
||||
Åtkomst via **Dashboard → Hälsa**. Systemhälsoöversikt i realtid med 6 kort:
|
||||
|
||||
| Kort | Vad den visar |
|
||||
| -------------------- | ------------------------------------------------------------------ |
|
||||
| **Systemstatus** | Drifttid, version, minnesanvändning, datakatalog |
|
||||
| **Providers hälsa** | Tillstånd för strömbrytare per leverantör (stängd/öppen/halvöppen) |
|
||||
| **Taxegränser** | Aktiva nedkylningar per konto med återstående tid |
|
||||
| **Aktiva låsningar** | Leverantörer tillfälligt blockerade av lockoutpolicyn |
|
||||
| **Signaturcache** | Dedupliceringscachestatistik (aktiva nycklar, träffhastighet) |
|
||||
| **Latens-telemetri** | p50/p95/p99 latensaggregation per leverantör |
|
||||
|
||||
**Proffstips:** Hälsosidan uppdateras automatiskt var tionde sekund. Använd strömbrytarkortet för att identifiera vilka leverantörer som har problem.
|
||||
441
docs/i18n/th/API_REFERENCE.md
Normal file
441
docs/i18n/th/API_REFERENCE.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# การอ้างอิง API
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
|
||||
|
||||
ข้อมูลอ้างอิงที่สมบูรณ์สำหรับตำแหน่งข้อมูล OmniRoute API ทั้งหมด
|
||||
|
||||
---
|
||||
|
||||
## สารบัญ
|
||||
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Semantic Cache](#semantic-cache)
|
||||
- [Dashboard & Management](#dashboard--management)
|
||||
- [Request Processing](#request-processing)
|
||||
- [Authentication](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## เสร็จสิ้นการแชท
|
||||
|
||||
```bash
|
||||
POST /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
|
||||
}
|
||||
```
|
||||
|
||||
### ส่วนหัวที่กำหนดเอง
|
||||
|
||||
| ส่วนหัว | ทิศทาง | คำอธิบาย |
|
||||
| ------------------------ | ------- | ------------------------------------------- |
|
||||
| `X-OmniRoute-No-Cache` | ขอ | ตั้งค่าเป็น `true` เพื่อข้ามแคช |
|
||||
| `X-OmniRoute-Progress` | ขอ | ตั้งค่าเป็น `true` สำหรับกิจกรรมความคืบหน้า |
|
||||
| `Idempotency-Key` | ขอ | ปุ่ม Dedup (หน้าต่าง 5s) |
|
||||
| `X-Request-Id` | ขอ | คีย์สำรองสำรอง |
|
||||
| `X-OmniRoute-Cache` | ตอบกลับ | `HIT` หรือ `MISS` (ไม่ใช่สตรีมมิ่ง) |
|
||||
| `X-OmniRoute-Idempotent` | ตอบกลับ | `true` หากขจัดข้อมูลซ้ำซ้อน |
|
||||
| `X-OmniRoute-Progress` | ตอบกลับ | `enabled` หากติดตามความคืบหน้าบน |
|
||||
|
||||
---
|
||||
|
||||
## การฝัง
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
ผู้ให้บริการที่มีอยู่: Nebius, OpenAI, Mistral, Together AI, ดอกไม้ไฟ, NVIDIA
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## การสร้างภาพ
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
ผู้ให้บริการที่มีอยู่: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## รายการรุ่น
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## จุดสิ้นสุดความเข้ากันได้
|
||||
|
||||
| วิธีการ | เส้นทาง | รูปแบบ |
|
||||
| ------- | --------------------------- | --------------------- |
|
||||
| โพสต์ | `/v1/chat/completions` | OpenAI |
|
||||
| โพสต์ | `/v1/messages` | มานุษยวิทยา |
|
||||
| โพสต์ | `/v1/responses` | การตอบสนองของ OpenAI |
|
||||
| โพสต์ | `/v1/embeddings` | OpenAI |
|
||||
| โพสต์ | `/v1/images/generations` | OpenAI |
|
||||
| รับ | `/v1/models` | OpenAI |
|
||||
| โพสต์ | `/v1/messages/count_tokens` | มานุษยวิทยา |
|
||||
| รับ | `/v1beta/models` | ราศีเมถุน |
|
||||
| โพสต์ | `/v1beta/models/{...path}` | ราศีเมถุนสร้างเนื้อหา |
|
||||
| โพสต์ | `/v1/api/chat` | โอลามา |
|
||||
|
||||
### เส้นทางของผู้ให้บริการเฉพาะ
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
คำนำหน้าผู้ให้บริการจะถูกเพิ่มอัตโนมัติหากไม่มี โมเดลที่ไม่ตรงกันส่งคืน `400`
|
||||
|
||||
---
|
||||
|
||||
## แคชความหมาย
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
ตัวอย่างการตอบกลับ:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## แดชบอร์ดและการจัดการ
|
||||
|
||||
### การรับรองความถูกต้อง
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ----------------------------- | ------- | ---------------------- |
|
||||
| `/api/auth/login` | โพสต์ | เข้าสู่ระบบ |
|
||||
| `/api/auth/logout` | โพสต์ | ออกจากระบบ |
|
||||
| `/api/settings/require-login` | รับ/ใส่ | ต้องสลับการเข้าสู่ระบบ |
|
||||
|
||||
### การจัดการผู้ให้บริการ
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ---------------------------- | ------------ | ------------------------------ |
|
||||
| `/api/providers` | รับ/โพสต์ | รายชื่อ / สร้างผู้ให้บริการ |
|
||||
| `/api/providers/[id]` | รับ/วาง/ลบ | จัดการผู้ให้บริการ |
|
||||
| `/api/providers/[id]/test` | โพสต์ | การเชื่อมต่อผู้ให้บริการทดสอบ |
|
||||
| `/api/providers/[id]/models` | รับ | รายชื่อรุ่นของผู้ให้บริการ |
|
||||
| `/api/providers/validate` | โพสต์ | ตรวจสอบการกำหนดค่าผู้ให้บริการ |
|
||||
| `/api/provider-nodes*` | ต่างๆ | การจัดการโหนดผู้ให้บริการ |
|
||||
| `/api/provider-models` | รับ/โพสต์/ลบ | โมเดลที่กำหนดเอง |
|
||||
|
||||
### กระแส OAuth
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| -------------------------------- | ------- | ----------------------- |
|
||||
| `/api/oauth/[provider]/[action]` | ต่างๆ | OAuth เฉพาะผู้ให้บริการ |
|
||||
|
||||
### การกำหนดเส้นทางและการกำหนดค่า
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------------- | --------- | ------------------------------- |
|
||||
| `/api/models/alias` | รับ/โพสต์ | นามแฝงโมเดล |
|
||||
| `/api/models/catalog` | รับ | ทุกรุ่นตามผู้ให้บริการ + ประเภท |
|
||||
| `/api/combos*` | ต่างๆ | การจัดการคำสั่งผสม |
|
||||
| `/api/keys*` | ต่างๆ | การจัดการคีย์ API |
|
||||
| `/api/pricing` | รับ | ราคารุ่น |
|
||||
|
||||
### การใช้งานและการวิเคราะห์
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------------------- | ------- | ------------------------ |
|
||||
| `/api/usage/history` | รับ | ประวัติการใช้งาน |
|
||||
| `/api/usage/logs` | รับ | บันทึกการใช้งาน |
|
||||
| `/api/usage/request-logs` | รับ | บันทึกระดับคำขอ |
|
||||
| `/api/usage/[connectionId]` | รับ | การใช้งานต่อการเชื่อมต่อ |
|
||||
|
||||
### การตั้งค่า
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ------------------------------- | ------- | ------------------------------- |
|
||||
| `/api/settings` | รับ/ใส่ | การตั้งค่าทั่วไป |
|
||||
| `/api/settings/proxy` | รับ/ใส่ | การกำหนดค่าพร็อกซีเครือข่าย |
|
||||
| `/api/settings/proxy/test` | โพสต์ | ทดสอบการเชื่อมต่อพร็อกซี |
|
||||
| `/api/settings/ip-filter` | รับ/ใส่ | รายการ IP ที่อนุญาต/รายการบล็อก |
|
||||
| `/api/settings/thinking-budget` | รับ/ใส่ | งบประมาณโทเค็นการใช้เหตุผล |
|
||||
| `/api/settings/system-prompt` | รับ/ใส่ | พร้อมท์ระบบโกลบอล |
|
||||
|
||||
### การตรวจสอบ
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ------------------------ | ------- | ---------------------------- |
|
||||
| `/api/sessions` | รับ | การติดตามเซสชันที่ใช้งานอยู่ |
|
||||
| `/api/rate-limits` | รับ | ขีดจำกัดอัตราต่อบัญชี |
|
||||
| `/api/monitoring/health` | รับ | ตรวจสุขภาพ |
|
||||
| `/api/cache` | รับ/ลบ | สถิติแคช / ล้าง |
|
||||
|
||||
### สำรองและส่งออก/นำเข้า
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------------------- | ------- | ------------------------------------------- |
|
||||
| `/api/db-backups` | รับ | แสดงรายการข้อมูลสำรองที่มีอยู่ |
|
||||
| `/api/db-backups` | ใส่ | สร้างการสำรองข้อมูลด้วยตนเอง |
|
||||
| `/api/db-backups` | โพสต์ | กู้คืนจากข้อมูลสำรองเฉพาะ |
|
||||
| `/api/db-backups/export` | รับ | ดาวน์โหลดฐานข้อมูลเป็นไฟล์ .sqlite |
|
||||
| `/api/db-backups/import` | โพสต์ | อัปโหลดไฟล์ .sqlite เพื่อแทนที่ฐานข้อมูล |
|
||||
| `/api/db-backups/exportAll` | รับ | ดาวน์โหลดข้อมูลสำรองแบบเต็มเป็นไฟล์ .tar.gz |
|
||||
|
||||
### คลาวด์ซิงค์
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ---------------------- | ------- | ------------------------- |
|
||||
| `/api/sync/cloud` | ต่างๆ | การดำเนินการซิงค์บนคลาวด์ |
|
||||
| `/api/sync/initialize` | โพสต์ | เริ่มต้นการซิงค์ |
|
||||
| `/api/cloud/*` | ต่างๆ | การจัดการคลาวด์ |
|
||||
|
||||
### เครื่องมือ CLI
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ---------------------------------- | ------- | ------------------ |
|
||||
| `/api/cli-tools/claude-settings` | รับ | สถานะ Claude CLI |
|
||||
| `/api/cli-tools/codex-settings` | รับ | สถานะ Codex CLI |
|
||||
| `/api/cli-tools/droid-settings` | รับ | สถานะ Droid CLI |
|
||||
| `/api/cli-tools/openclaw-settings` | รับ | สถานะ OpenClaw CLI |
|
||||
| `/api/cli-tools/runtime/[toolId]` | รับ | รันไทม์ CLI ทั่วไป |
|
||||
|
||||
การตอบกลับของ CLI ได้แก่: `installed`, `runnable`, `command`, `commandPath`, `runtimeMode`, `reason`
|
||||
|
||||
### ความยืดหยุ่นและขีดจำกัดอัตรา
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ----------------------- | ------- | ------------------------------- |
|
||||
| `/api/resilience` | รับ/ใส่ | รับ/อัปเดตโปรไฟล์ความยืดหยุ่น |
|
||||
| `/api/resilience/reset` | โพสต์ | รีเซ็ตเบรกเกอร์วงจร |
|
||||
| `/api/rate-limits` | รับ | สถานะขีดจำกัดอัตราต่อบัญชี |
|
||||
| `/api/rate-limit` | รับ | การกำหนดค่าขีดจำกัดอัตราทั่วโลก |
|
||||
|
||||
### เอวาลส์
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| ------------ | --------- | --------------------------------------- |
|
||||
| `/api/evals` | รับ/โพสต์ | แสดงรายการชุด eval / ดำเนินการประเมินผล |
|
||||
|
||||
### นโยบาย
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------- | ------------ | --------------------------- |
|
||||
| `/api/policies` | รับ/โพสต์/ลบ | จัดการนโยบายการกำหนดเส้นทาง |
|
||||
|
||||
### การปฏิบัติตาม
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------------------- | ------- | ------------------------------------------------- |
|
||||
| `/api/compliance/audit-log` | รับ | บันทึกการตรวจสอบการปฏิบัติตามข้อกำหนด (N สุดท้าย) |
|
||||
|
||||
### v1beta (เข้ากันได้กับราศีเมถุน)
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| -------------------------- | ------- | ----------------------------------- |
|
||||
| `/v1beta/models` | รับ | รายการรุ่นในรูปแบบราศีเมถุน |
|
||||
| `/v1beta/models/{...path}` | โพสต์ | ราศีเมถุน `generateContent` ปลายทาง |
|
||||
|
||||
ตำแหน่งข้อมูลเหล่านี้สะท้อนรูปแบบ API ของ Gemini สำหรับไคลเอนต์ที่คาดหวังความเข้ากันได้ของ Gemini SDK ดั้งเดิม
|
||||
|
||||
### API ภายใน / ระบบ
|
||||
|
||||
| จุดสิ้นสุด | วิธีการ | คำอธิบาย |
|
||||
| --------------- | ------- | ------------------------------------------------------ |
|
||||
| `/api/init` | รับ | การตรวจสอบการเริ่มต้นแอปพลิเคชัน (ใช้ในการรันครั้งแรก) |
|
||||
| `/api/tags` | รับ | แท็กโมเดลที่เข้ากันได้กับ Ollama (สำหรับลูกค้า Ollama) |
|
||||
| `/api/restart` | โพสต์ | ทริกเกอร์การรีสตาร์ทเซิร์ฟเวอร์อย่างสง่างาม |
|
||||
| `/api/shutdown` | โพสต์ | ทริกเกอร์การปิดระบบเซิร์ฟเวอร์อย่างสง่างาม |
|
||||
|
||||
> **หมายเหตุ:** ตำแหน่งข้อมูลเหล่านี้ถูกใช้ภายในโดยระบบหรือเพื่อความเข้ากันได้กับไคลเอ็นต์ Ollama โดยทั่วไปแล้วจะไม่ถูกเรียกโดยผู้ใช้ปลายทาง
|
||||
|
||||
---
|
||||
|
||||
## การถอดเสียง
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
ถอดเสียงไฟล์เสียงโดยใช้ Deepgram หรือ AssemblyAI
|
||||
|
||||
**คำขอ:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**คำตอบ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
**ผู้ให้บริการที่รองรับ:** `deepgram/nova-3`, `assemblyai/best`
|
||||
|
||||
**รูปแบบที่รองรับ:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
|
||||
---
|
||||
|
||||
## ความเข้ากันได้ของ Ollama
|
||||
|
||||
สำหรับลูกค้าที่ใช้รูปแบบ API ของ Ollama:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
คำขอจะได้รับการแปลโดยอัตโนมัติระหว่าง Ollama และรูปแบบภายใน
|
||||
|
||||
---
|
||||
|
||||
## มาตรระยะไกล
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**คำตอบ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## งบประมาณ
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## รุ่นที่มีจำหน่าย
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## คำขอดำเนินการ
|
||||
|
||||
1. ลูกค้าส่งคำขอไปที่ `/v1/*`
|
||||
2. ตัวจัดการเส้นทางเรียก `handleChat`, `handleEmbedding`, `handleAudioTranscription` หรือ `handleImageGeneration`
|
||||
3. โมเดลได้รับการแก้ไขแล้ว (ผู้ให้บริการโดยตรง/โมเดลหรือนามแฝง/คอมโบ)
|
||||
4. ข้อมูลรับรองที่เลือกจากฐานข้อมูลท้องถิ่นพร้อมการกรองความพร้อมใช้งานของบัญชี
|
||||
5. สำหรับการแชท: `handleChatCore` — การตรวจจับรูปแบบ การแปล การตรวจสอบแคช การตรวจสอบค่าเดิม
|
||||
6. ผู้ดำเนินการของผู้ให้บริการส่งคำขออัปสตรีม
|
||||
7. การตอบสนองถูกแปลกลับเป็นรูปแบบไคลเอนต์ (แชท) หรือส่งคืนตามสภาพ (การฝัง/รูปภาพ/เสียง)
|
||||
8. บันทึกการใช้งาน/การบันทึก
|
||||
9. การใช้ทางเลือกสำรองจะมีผลกับข้อผิดพลาดตามกฎคอมโบ
|
||||
|
||||
การอ้างอิงสถาปัตยกรรมแบบเต็ม: [**OMNI_TOKEN_119**](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## การรับรองความถูกต้อง
|
||||
|
||||
- เส้นทางแดชบอร์ด (`/dashboard/*`) ใช้คุกกี้ `auth_token`
|
||||
- การเข้าสู่ระบบใช้แฮชรหัสผ่านที่บันทึกไว้ สำรองไปที่ `INITIAL_PASSWORD`
|
||||
- `requireLogin` สลับได้ผ่าน `/api/settings/require-login`
|
||||
- เส้นทาง `/v1/*` เป็นทางเลือกที่ต้องใช้คีย์ Bearer API เมื่อ `REQUIRE_API_KEY=true`
|
||||
781
docs/i18n/th/ARCHITECTURE.md
Normal file
781
docs/i18n/th/ARCHITECTURE.md
Normal file
@@ -0,0 +1,781 @@
|
||||
# สถาปัตยกรรม OmniRoute
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
|
||||
|
||||
_อัพเดตล่าสุด: 2026-02-18_
|
||||
|
||||
## บทสรุปผู้บริหาร
|
||||
|
||||
OmniRoute เป็นเกตเวย์การกำหนดเส้นทาง AI ในพื้นที่และแดชบอร์ดที่สร้างขึ้นบน Next.js
|
||||
โดยให้จุดสิ้นสุดที่เข้ากันได้กับ OpenAI จุดเดียว (`/v1/*`) และกำหนดเส้นทางการรับส่งข้อมูลผ่านผู้ให้บริการอัปสตรีมหลายรายพร้อมการแปล ทางเลือกสำรอง การรีเฟรชโทเค็น และการติดตามการใช้งาน
|
||||
|
||||
ความสามารถหลัก:
|
||||
|
||||
- พื้นผิว API ที่เข้ากันได้กับ OpenAI สำหรับ CLI/เครื่องมือ (ผู้ให้บริการ 28 ราย)
|
||||
- การแปลคำขอ/ตอบกลับในรูปแบบต่างๆ ของผู้ให้บริการ
|
||||
- ทางเลือกคำสั่งผสมโมเดล (ลำดับหลายรุ่น)
|
||||
- ทางเลือกระดับบัญชี (หลายบัญชีต่อผู้ให้บริการ)
|
||||
- การจัดการการเชื่อมต่อผู้ให้บริการ OAuth + API-key
|
||||
- การสร้างการฝังผ่าน `/v1/embeddings` (ผู้ให้บริการ 6 ราย, 9 โมเดล)
|
||||
- การสร้างภาพผ่าน `/v1/images/generations` (ผู้ให้บริการ 4 ราย, 9 รุ่น)
|
||||
- คิดว่าการแยกวิเคราะห์แท็ก (`<think>...</think>`) สำหรับโมเดลการให้เหตุผล
|
||||
- การตอบสนองการฆ่าเชื้อสำหรับความเข้ากันได้ของ OpenAI SDK ที่เข้มงวด
|
||||
- การปรับบทบาทให้เป็นมาตรฐาน (ผู้พัฒนา → ระบบ, ระบบ → ผู้ใช้) เพื่อความเข้ากันได้ระหว่างผู้ให้บริการ
|
||||
- การแปลงเอาต์พุตที่มีโครงสร้าง (json_schema → Gemini responseSchema)
|
||||
- ความคงอยู่ในท้องถิ่นสำหรับผู้ให้บริการ คีย์ นามแฝง คอมโบ การตั้งค่า การกำหนดราคา
|
||||
- การติดตามการใช้งาน/ต้นทุน และขอบันทึก
|
||||
- ตัวเลือกการซิงค์บนคลาวด์สำหรับการซิงค์หลายอุปกรณ์/สถานะ
|
||||
- รายการที่อนุญาต/รายการบล็อก IP สำหรับการควบคุมการเข้าถึง API
|
||||
- คิดการจัดการงบประมาณ (ส่งผ่าน/อัตโนมัติ/กำหนดเอง/ปรับเปลี่ยน)
|
||||
- ระบบฉีดพร้อมท์ทั่วโลก
|
||||
- การติดตามเซสชันและการพิมพ์ลายนิ้วมือ
|
||||
- การจำกัดอัตราการปรับปรุงต่อบัญชีด้วยโปรไฟล์เฉพาะของผู้ให้บริการ
|
||||
- รูปแบบเซอร์กิตเบรกเกอร์เพื่อความยืดหยุ่นของผู้ให้บริการ
|
||||
- ป้องกันฝูงฟ้าผ่าพร้อมระบบล็อค mutex
|
||||
- แคชการขจัดข้อมูลซ้ำซ้อนของคำขอตามลายเซ็น
|
||||
- เลเยอร์โดเมน: ความพร้อมใช้งานของโมเดล กฎต้นทุน นโยบายทางเลือก นโยบายการล็อก
|
||||
- การคงอยู่ของสถานะโดเมน (แคชการเขียนผ่าน SQLite สำหรับทางเลือกสำรอง งบประมาณ การล็อคเอาต์ เซอร์กิตเบรกเกอร์)
|
||||
- กลไกนโยบายสำหรับการประเมินคำขอแบบรวมศูนย์ (ล็อค → งบประมาณ → ทางเลือก)
|
||||
- ขอการตรวจวัดทางไกลด้วยการรวมเวลาแฝง p50/p95/p99
|
||||
- Correlation ID (X-Request-Id) สำหรับการติดตามจากต้นทางถึงปลายทาง
|
||||
- การบันทึกการตรวจสอบการปฏิบัติตามข้อกำหนดโดยเลือกไม่ใช้ต่อคีย์ API
|
||||
- กรอบการประเมินสำหรับการประกันคุณภาพ LLM
|
||||
- แดชบอร์ด UI ความยืดหยุ่นพร้อมสถานะเบรกเกอร์แบบเรียลไทม์
|
||||
- ผู้ให้บริการ OAuth แบบโมดูลาร์ (12 โมดูลแต่ละโมดูลภายใต้ `src/lib/oauth/providers/`)
|
||||
|
||||
โมเดลรันไทม์หลัก:
|
||||
|
||||
- เส้นทางแอป Next.js ภายใต้ `src/app/api/*` ใช้ทั้ง API แดชบอร์ดและ API ที่เข้ากันได้
|
||||
- SSE/แกนการกำหนดเส้นทางที่ใช้ร่วมกันใน `src/sse/*` + `open-sse/*` จัดการการดำเนินการของผู้ให้บริการ การแปล การสตรีม ทางเลือกสำรอง และการใช้งาน
|
||||
|
||||
## ขอบเขตและขอบเขต
|
||||
|
||||
### ในขอบเขต
|
||||
|
||||
- รันไทม์เกตเวย์ท้องถิ่น
|
||||
- API การจัดการแดชบอร์ด
|
||||
- การรับรองความถูกต้องของผู้ให้บริการและการรีเฟรชโทเค็น
|
||||
- ขอการแปลและการสตรีม SSE
|
||||
- สภาพท้องถิ่น + ความคงทนในการใช้งาน
|
||||
- การประสานการซิงค์บนคลาวด์เสริม
|
||||
|
||||
### อยู่นอกขอบเขต
|
||||
|
||||
- การใช้งานบริการคลาวด์เบื้องหลัง `NEXT_PUBLIC_CLOUD_URL`
|
||||
- SLA ของผู้ให้บริการ/ระนาบควบคุมอยู่นอกกระบวนการท้องถิ่น
|
||||
- ไบนารี CLI ภายนอกเอง (Claude CLI, Codex CLI ฯลฯ )
|
||||
|
||||
## บริบทของระบบระดับสูง
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(db.json)]
|
||||
UDB[(usage.json + log.txt)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## ส่วนประกอบรันไทม์หลัก
|
||||
|
||||
## 1) API และเลเยอร์การกำหนดเส้นทาง (เส้นทางแอป Next.js)
|
||||
|
||||
ไดเรกทอรีหลัก:
|
||||
|
||||
- `src/app/api/v1/*` และ `src/app/api/v1beta/*` สำหรับ API ที่เข้ากันได้
|
||||
- `src/app/api/*` สำหรับ API การจัดการ/การกำหนดค่า
|
||||
- เขียนใหม่ครั้งต่อไปใน `next.config.mjs` แผนที่ `/v1/*` ถึง `/api/v1/*`
|
||||
|
||||
เส้นทางความเข้ากันได้ที่สำคัญ:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — รวมโมเดลที่กำหนดเองด้วย `custom: true`
|
||||
- `src/app/api/v1/embeddings/route.ts` — การสร้างการฝัง (ผู้ให้บริการ 6 ราย)
|
||||
- `src/app/api/v1/images/generations/route.ts` — การสร้างภาพ (ผู้ให้บริการ 4+ รายรวม Antigravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — แชทเฉพาะต่อผู้ให้บริการ
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — การฝังต่อผู้ให้บริการโดยเฉพาะ
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — อิมเมจต่อผู้ให้บริการโดยเฉพาะ
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
โดเมนการจัดการ:
|
||||
|
||||
- การรับรองความถูกต้อง/การตั้งค่า: `src/app/api/auth/*`, `src/app/api/settings/*`
|
||||
- ผู้ให้บริการ/การเชื่อมต่อ: `src/app/api/providers*`
|
||||
- โหนดผู้ให้บริการ: `src/app/api/provider-nodes*`
|
||||
- โมเดลที่กำหนดเอง: `src/app/api/provider-models` (GET/POST/DELETE)
|
||||
- แคตตาล็อกรุ่น: `src/app/api/models/catalog` (GET)
|
||||
- การกำหนดค่าพร็อกซี: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth: `src/app/api/oauth/*`
|
||||
- คีย์/นามแฝง/คอมโบ/ราคา: `src/app/api/keys*`, `src/app/api/models/alias`, `src/app/api/combos*`, `src/app/api/pricing`
|
||||
- การใช้งาน: `src/app/api/usage/*`
|
||||
- ซิงค์/คลาวด์: `src/app/api/sync/*`, `src/app/api/cloud/*`
|
||||
- ผู้ช่วยเครื่องมือ CLI: `src/app/api/cli-tools/*`
|
||||
- ตัวกรอง IP: `src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- งบประมาณการคิด: `src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- ระบบแจ้ง: `src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- เซสชัน: `src/app/api/sessions` (GET)
|
||||
- ขีดจำกัดอัตรา: `src/app/api/rate-limits` (GET)
|
||||
- ความยืดหยุ่น: `src/app/api/resilience` (GET/PATCH) — โปรไฟล์ผู้ให้บริการ, เซอร์กิตเบรกเกอร์, สถานะขีดจำกัดอัตรา
|
||||
- รีเซ็ตความยืดหยุ่น: `src/app/api/resilience/reset` (POST) — รีเซ็ตเบรกเกอร์ + คูลดาวน์
|
||||
- สถิติแคช: `src/app/api/cache/stats` (GET/DELETE)
|
||||
- ความพร้อมของรุ่น: `src/app/api/models/availability` (GET/POST)
|
||||
- การวัดและส่งข้อมูลทางไกล: `src/app/api/telemetry/summary` (GET)
|
||||
- งบประมาณ: `src/app/api/usage/budget` (GET/POST)
|
||||
- เชนทางเลือก: `src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- การตรวจสอบการปฏิบัติตามข้อกำหนด: `src/app/api/compliance/audit-log` (GET)
|
||||
- คะแนน: `src/app/api/evals` (GET/POST), `src/app/api/evals/[suiteId]` (GET)
|
||||
- นโยบาย: `src/app/api/policies` (GET/POST)
|
||||
|
||||
## 2) SSE + แกนการแปล
|
||||
|
||||
โมดูลการไหลหลัก:
|
||||
|
||||
- รายการ: `src/sse/handlers/chat.ts`
|
||||
- การประสานหลัก: `open-sse/handlers/chatCore.ts`
|
||||
- อะแดปเตอร์การดำเนินการของผู้ให้บริการ: `open-sse/executors/*`
|
||||
- รูปแบบการตรวจจับ/การกำหนดค่าผู้ให้บริการ: `open-sse/services/provider.ts`
|
||||
- โมเดลแยกวิเคราะห์/แก้ไข: `src/sse/services/model.ts`, `open-sse/services/model.ts`
|
||||
- ตรรกะทางเลือกของบัญชี: `open-sse/services/accountFallback.ts`
|
||||
- รีจิสทรีการแปล: `open-sse/translator/index.ts`
|
||||
- การแปลงสตรีม: `open-sse/utils/stream.ts`, `open-sse/utils/streamHandler.ts`
|
||||
- การแยกการใช้งาน/การทำให้เป็นมาตรฐาน: `open-sse/utils/usageTracking.ts`
|
||||
- คิดว่าตัวแยกวิเคราะห์แท็ก: `open-sse/utils/thinkTagParser.ts`
|
||||
- ตัวจัดการการฝัง: `open-sse/handlers/embeddings.ts`
|
||||
- การลงทะเบียนผู้ให้บริการการฝัง: `open-sse/config/embeddingRegistry.ts`
|
||||
- ตัวจัดการการสร้างอิมเมจ: `open-sse/handlers/imageGeneration.ts`
|
||||
- รีจิสทรีของผู้ให้บริการอิมเมจ: `open-sse/config/imageRegistry.ts`
|
||||
- การตอบสนองการฆ่าเชื้อ: `open-sse/handlers/responseSanitizer.ts`
|
||||
- การทำให้บทบาทเป็นมาตรฐาน: `open-sse/services/roleNormalizer.ts`
|
||||
|
||||
บริการ (ตรรกะทางธุรกิจ):
|
||||
|
||||
- การเลือกบัญชี/การให้คะแนน: `open-sse/services/accountSelector.ts`
|
||||
- การจัดการวงจรชีวิตบริบท: `open-sse/services/contextManager.ts`
|
||||
- การบังคับใช้ตัวกรอง IP: `open-sse/services/ipFilter.ts`
|
||||
- การติดตามเซสชัน: `open-sse/services/sessionManager.ts`
|
||||
- ขอการขจัดข้อมูลซ้ำซ้อน: `open-sse/services/signatureCache.ts`
|
||||
- ระบบพร้อมท์การฉีด: `open-sse/services/systemPrompt.ts`
|
||||
- คิดการจัดการงบประมาณ: `open-sse/services/thinkingBudget.ts`
|
||||
- การกำหนดเส้นทางโมเดลตัวแทน: `open-sse/services/wildcardRouter.ts`
|
||||
- การจัดการขีดจำกัดอัตรา: `open-sse/services/rateLimitManager.ts`
|
||||
- เบรกเกอร์: `open-sse/services/circuitBreaker.ts`
|
||||
|
||||
โมดูลเลเยอร์โดเมน:
|
||||
|
||||
- รุ่นที่มีวางจำหน่าย: `src/lib/domain/modelAvailability.ts`
|
||||
- กฎต้นทุน/งบประมาณ: `src/lib/domain/costRules.ts`
|
||||
- นโยบายสำรอง: `src/lib/domain/fallbackPolicy.ts`
|
||||
- ตัวแก้ไขคำสั่งผสม: `src/lib/domain/comboResolver.ts`
|
||||
- นโยบายการล็อก: `src/lib/domain/lockoutPolicy.ts`
|
||||
- กลไกนโยบาย: `src/domain/policyEngine.ts` — การล็อคแบบรวมศูนย์ → งบประมาณ → การประเมินทางเลือก
|
||||
- แค็ตตาล็อกรหัสข้อผิดพลาด: `src/lib/domain/errorCodes.ts`
|
||||
- รหัสคำขอ: `src/lib/domain/requestId.ts`
|
||||
- หมดเวลาดึงข้อมูล: `src/lib/domain/fetchTimeout.ts`
|
||||
- ขอการตรวจวัดระยะไกล: `src/lib/domain/requestTelemetry.ts`
|
||||
- การปฏิบัติตามข้อกำหนด/การตรวจสอบ: `src/lib/domain/compliance/index.ts`
|
||||
- นักวิ่งประเมิน: `src/lib/domain/evalRunner.ts`
|
||||
- การคงอยู่ของสถานะโดเมน: `src/lib/db/domainState.ts` — SQLite CRUD สำหรับเชนสำรอง งบประมาณ ประวัติต้นทุน สถานะการล็อกเอาต์ เซอร์กิตเบรกเกอร์
|
||||
|
||||
โมดูลผู้ให้บริการ OAuth (12 ไฟล์แต่ละไฟล์ภายใต้ `src/lib/oauth/providers/`):
|
||||
|
||||
- ดัชนีรีจิสทรี: `src/lib/oauth/providers/index.ts`
|
||||
- ผู้ให้บริการส่วนบุคคล: `claude.ts`, `codex.ts`, `gemini.ts`, `antigravity.ts`, `iflow.ts`, `qwen.ts`, `kimi-coding.ts`, `github.ts`, `kiro.ts`, `cursor.ts`, `kilocode.ts`, `cline.ts`
|
||||
- กระดาษห่อแบบบาง: `src/lib/oauth/providers.ts` — ส่งออกซ้ำจากแต่ละโมดูล
|
||||
|
||||
## 3) เลเยอร์การคงอยู่
|
||||
|
||||
ฐานข้อมูลสถานะหลัก:
|
||||
|
||||
- `src/lib/localDb.ts`
|
||||
- ไฟล์: `${DATA_DIR}/db.json` (หรือ `$XDG_CONFIG_HOME/omniroute/db.json` เมื่อตั้งค่า มิฉะนั้น `~/.omniroute/db.json`)
|
||||
- เอนทิตี: providerConnections, providerNodes, modelAliases, คอมโบ, apiKeys, การตั้งค่า, การกำหนดราคา, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
|
||||
|
||||
ฐานข้อมูลการใช้งาน:
|
||||
|
||||
- `src/lib/usageDb.ts`
|
||||
- ไฟล์: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- เป็นไปตามนโยบายไดเรกทอรีฐานเดียวกันกับ `localDb` (`DATA_DIR` จากนั้น `XDG_CONFIG_HOME/omniroute` เมื่อตั้งค่า)
|
||||
- แบ่งออกเป็นโมดูลย่อยที่เน้น: `migrations.ts`, `usageHistory.ts`, `costCalculator.ts`, `usageStats.ts`, `callLogs.ts`
|
||||
|
||||
ฐานข้อมูลสถานะโดเมน (SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — การดำเนินการ CRUD สำหรับสถานะโดเมน
|
||||
- ตาราง (สร้างใน `src/lib/db/core.ts`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
|
||||
- รูปแบบแคชการเขียนผ่าน: แผนที่ในหน่วยความจำเชื่อถือได้ ณ รันไทม์ การกลายพันธุ์จะถูกเขียนพร้อมกันกับ SQLite; สถานะถูกกู้คืนจาก DB เมื่อสตาร์ทขณะเย็น
|
||||
|
||||
## 4) การรับรองความถูกต้อง + พื้นผิวการรักษาความปลอดภัย
|
||||
|
||||
- การตรวจสอบคุกกี้แดชบอร์ด: `src/proxy.ts`, `src/app/api/auth/login/route.ts`
|
||||
- การสร้าง/การตรวจสอบคีย์ API: `src/shared/utils/apiKey.ts`
|
||||
- ข้อมูลลับของผู้ให้บริการยังคงอยู่ในรายการ `providerConnections`
|
||||
- รองรับพร็อกซีขาออกผ่าน `open-sse/utils/proxyFetch.ts` (env vars) และ `open-sse/utils/networkProxy.ts` (กำหนดค่าได้ต่อผู้ให้บริการหรือทั่วโลก)
|
||||
|
||||
## 5) การซิงค์บนคลาวด์
|
||||
|
||||
- เริ่มต้นตัวกำหนดเวลา: `src/lib/initCloudSync.ts`, `src/shared/services/initializeCloudSync.ts`
|
||||
- งานประจำ: `src/shared/services/cloudSyncScheduler.ts`
|
||||
- เส้นทางควบคุม: `src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## ระยะเวลาคำขอ (`/v1/chat/completions`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## Combo + ขั้นตอนทางเลือกของบัญชี
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
การตัดสินใจทางเลือกถูกขับเคลื่อนโดย `open-sse/services/accountFallback.ts` โดยใช้รหัสสถานะและการวิเคราะห์พฤติกรรมข้อความแสดงข้อผิดพลาด
|
||||
|
||||
## การเริ่มต้นใช้งาน OAuth และวงจรการรีเฟรชโทเค็น
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
การรีเฟรชระหว่างการรับส่งข้อมูลสดจะดำเนินการภายใน `open-sse/handlers/chatCore.ts` ผ่านตัวดำเนินการ `refreshCredentials()`
|
||||
|
||||
## วงจรการใช้งาน Cloud Sync (เปิดใช้งาน / ซิงค์ / ปิดใช้งาน)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
การซิงค์เป็นระยะจะถูกทริกเกอร์โดย `CloudSyncScheduler` เมื่อเปิดใช้งานระบบคลาวด์
|
||||
|
||||
## แบบจำลองข้อมูลและแผนที่การจัดเก็บ
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
ไฟล์จัดเก็บข้อมูลทางกายภาพ:
|
||||
|
||||
- สถานะหลัก: `${DATA_DIR}/db.json` (หรือ `$XDG_CONFIG_HOME/omniroute/db.json` เมื่อตั้งค่า มิฉะนั้น `~/.omniroute/db.json`)
|
||||
- สถิติการใช้งาน: `${DATA_DIR}/usage.json`
|
||||
- ขอบรรทัดบันทึก: `${DATA_DIR}/log.txt`
|
||||
- ตัวเลือกนักแปล/ร้องขอเซสชันการแก้ไขข้อบกพร่อง: `<repo>/logs/...`
|
||||
|
||||
## โทโพโลยีการปรับใช้
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(db.json)]
|
||||
UsageDB[(usage.json/log.txt)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## การทำแผนที่โมดูล (การตัดสินใจที่สำคัญ)
|
||||
|
||||
### เส้นทางและโมดูล API
|
||||
|
||||
- `src/app/api/v1/*`, `src/app/api/v1beta/*`: API ความเข้ากันได้
|
||||
- `src/app/api/v1/providers/[provider]/*`: เส้นทางเฉพาะต่อผู้ให้บริการ (แชท การฝัง รูปภาพ)
|
||||
- `src/app/api/providers*`: ผู้ให้บริการ CRUD, การตรวจสอบความถูกต้อง, การทดสอบ
|
||||
- `src/app/api/provider-nodes*`: การจัดการโหนดที่เข้ากันได้แบบกำหนดเอง
|
||||
- `src/app/api/provider-models`: การจัดการโมเดลแบบกำหนดเอง (CRUD)
|
||||
- `src/app/api/models/catalog`: API แคตตาล็อกโมเดลแบบเต็ม (ทุกประเภทจัดกลุ่มตามผู้ให้บริการ)
|
||||
- `src/app/api/oauth/*`: การไหลของ OAuth/รหัสอุปกรณ์
|
||||
- `src/app/api/keys*`: วงจรการใช้งานคีย์ API ภายในเครื่อง
|
||||
- `src/app/api/models/alias`: การจัดการนามแฝง
|
||||
- `src/app/api/combos*`: การจัดการคอมโบทางเลือก
|
||||
- `src/app/api/pricing`: แทนที่การกำหนดราคาสำหรับการคำนวณต้นทุน
|
||||
- `src/app/api/settings/proxy`: การกำหนดค่าพร็อกซี (GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test`: การทดสอบการเชื่อมต่อพร็อกซีขาออก (POST)
|
||||
- `src/app/api/usage/*`: การใช้งานและบันทึก API
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*`: การซิงค์บนคลาวด์และผู้ช่วยเหลือบนคลาวด์
|
||||
- `src/app/api/cli-tools/*`: ตัวเขียน/ตัวตรวจสอบการกำหนดค่า CLI ในเครื่อง
|
||||
- `src/app/api/settings/ip-filter`: รายการ IP ที่อนุญาต/รายการบล็อก (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget`: คิดการกำหนดค่างบประมาณโทเค็น (GET/PUT)
|
||||
- `src/app/api/settings/system-prompt`: พร้อมท์ระบบทั่วโลก (GET/PUT)
|
||||
- `src/app/api/sessions`: รายการเซสชันที่ใช้งานอยู่ (GET)
|
||||
- `src/app/api/rate-limits`: สถานะขีดจำกัดอัตราต่อบัญชี (GET)
|
||||
|
||||
### แกนการกำหนดเส้นทางและการดำเนินการ
|
||||
|
||||
- `src/sse/handlers/chat.ts`: คำขอแยกวิเคราะห์ การจัดการคำสั่งผสม วนรอบการเลือกบัญชี
|
||||
- `open-sse/handlers/chatCore.ts`: การแปล การดำเนินการจัดส่ง การจัดการลองใหม่/รีเฟรช การตั้งค่าสตรีม
|
||||
- `open-sse/executors/*`: เครือข่ายเฉพาะผู้ให้บริการและพฤติกรรมรูปแบบ
|
||||
|
||||
### รีจิสทรีการแปลและตัวแปลงรูปแบบ
|
||||
|
||||
- `open-sse/translator/index.ts`: ทะเบียนนักแปลและเรียบเรียง
|
||||
- ขอนักแปล: `open-sse/translator/request/*`
|
||||
- ผู้แปลคำตอบ: `open-sse/translator/response/*`
|
||||
- รูปแบบค่าคงที่: `open-sse/translator/formats.ts`
|
||||
|
||||
### ความคงอยู่
|
||||
|
||||
- `src/lib/localDb.ts`: การกำหนดค่า/สถานะแบบถาวร
|
||||
- `src/lib/usageDb.ts`: ประวัติการใช้งานและบันทึกคำขอแบบต่อเนื่อง
|
||||
|
||||
## ความครอบคลุมของผู้ให้บริการ (รูปแบบกลยุทธ์)
|
||||
|
||||
ผู้ให้บริการแต่ละรายมีตัวดำเนินการเฉพาะที่ขยาย `BaseExecutor` (ใน `open-sse/executors/base.ts`) ซึ่งจัดเตรียมการสร้าง URL การสร้างส่วนหัว การลองอีกครั้งด้วย Exponential Backoff ฮุคการรีเฟรชข้อมูลประจำตัว และวิธีการประสาน `execute()`
|
||||
|
||||
| ผู้ดำเนินการ | ผู้ให้บริการ | การจัดการพิเศษ |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||
| `DefaultExecutor` | OpenAI, Claude, Gemini, Qwen, iFlow, OpenRouter, GLM, Kimi, MiniMax, DeepSeek, Groq, xAI, Mistral, ความฉงนสนเท่ห์, Together, ดอกไม้ไฟ, Cerebras, Cohere, NVIDIA | URL แบบไดนามิก/การกำหนดค่าส่วนหัวต่อผู้ให้บริการ |
|
||||
| `AntigravityExecutor` | Google ต้านแรงโน้มถ่วง | รหัสโปรเจ็กต์/เซสชันแบบกำหนดเอง ลองอีกครั้งหลังจากแยกวิเคราะห์ |
|
||||
| `CodexExecutor` | OpenAI Codex | แทรกคำสั่งของระบบ บังคับใช้ความพยายามในการให้เหตุผล |
|
||||
| `CursorExecutor` | เคอร์เซอร์ IDE | โปรโตคอล ConnectRPC, การเข้ารหัส Protobuf, ขอการลงนามผ่านเช็คซัม |
|
||||
| `GithubExecutor` | นักบิน GitHub | การรีเฟรชโทเค็น Copilot ส่วนหัวการเลียนแบบ VSCode |
|
||||
| `KiroExecutor` | AWS CodeWhisperer/Kiro | รูปแบบไบนารี AWS EventStream → การแปลง SSE |
|
||||
| `GeminiCLIExecutor` | ราศีเมถุน CLI | วงจรการรีเฟรชโทเค็น Google OAuth |
|
||||
|
||||
ผู้ให้บริการรายอื่นทั้งหมด (รวมถึงโหนดที่เข้ากันได้แบบกำหนดเอง) ใช้ `DefaultExecutor`
|
||||
|
||||
## เมทริกซ์ความเข้ากันได้ของผู้ให้บริการ
|
||||
|
||||
| ผู้ให้บริการ | รูปแบบ | รับรองความถูกต้อง | สตรีม | ไม่ใช่สตรีม | รีเฟรชโทเค็น | API การใช้งาน |
|
||||
| ------------------- | --------------- | ---------------------- | ---------------- | ----------- | ------------ | --------------------- |
|
||||
| คลอดด์ | คลอด | คีย์ API / OAuth | ✅ | ✅ | ✅ | ⚠️เฉพาะแอดมินเท่านั้น |
|
||||
| ราศีเมถุน | ราศีเมถุน | คีย์ API / OAuth | ✅ | ✅ | ✅ | ⚠️ คลาวด์คอนโซล |
|
||||
| ราศีเมถุน CLI | ราศีเมถุน-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ คลาวด์คอนโซล |
|
||||
| ต้านแรงโน้มถ่วง | ต้านแรงโน้มถ่วง | OAuth | ✅ | ✅ | ✅ | ✅ API โควต้าเต็ม |
|
||||
| OpenAI | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| โคเด็กซ์ | openai ตอบกลับ | OAuth | ✅บังคับ | ❌ | ✅ | ✅ ขีดจำกัดอัตรา |
|
||||
| นักบิน GitHub | เปิดใจ | OAuth + โทเค็น Copilot | ✅ | ✅ | ✅ | ✅ สแนปชอตโควต้า |
|
||||
| เคอร์เซอร์ | เคอร์เซอร์ | เช็คซัมแบบกำหนดเอง | ✅ | ✅ | ❌ | ❌ |
|
||||
| คิโระ | คิโระ | AWS SSO OIDC | ✅ (EventStream) | ❌ | ✅ | ✅ ขีดจำกัดการใช้งาน |
|
||||
| ควีน | เปิดใจ | OAuth | ✅ | ✅ | ✅ | ⚠️ตามคำขอ |
|
||||
| ไอโฟลว์ | เปิดใจ | OAuth (พื้นฐาน) | ✅ | ✅ | ✅ | ⚠️ตามคำขอ |
|
||||
| OpenRouter | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| GLM/คิมิ/มินิแม็กซ์ | คลอด | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| DeepSeek | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| กรอค | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| xAI (โกรก) | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| มิสทรัล | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| ความฉงนสนเท่ห์ | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| ร่วมกัน AI | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| ดอกไม้ไฟ AI | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| สมอง | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| เชื่อมโยง | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| NVIDIA NIM | เปิดใจ | คีย์ API | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
## รูปแบบความครอบคลุมการแปล
|
||||
|
||||
รูปแบบแหล่งที่มาที่ตรวจพบ ได้แก่:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
รูปแบบเป้าหมายได้แก่:
|
||||
|
||||
- แชท / ตอบกลับ OpenAI
|
||||
- คลอดด์
|
||||
- Gemini/Gemini-CLI/ซองต้านแรงโน้มถ่วง
|
||||
- คิโระ
|
||||
- เคอร์เซอร์
|
||||
|
||||
การแปลใช้ **OpenAI เป็นรูปแบบฮับ** — การแปลงทั้งหมดผ่าน OpenAI เป็นตัวกลาง:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
การแปลจะถูกเลือกแบบไดนามิกตามรูปร่างเพย์โหลดต้นทางและรูปแบบเป้าหมายของผู้ให้บริการ
|
||||
|
||||
เลเยอร์การประมวลผลเพิ่มเติมในไปป์ไลน์การแปล:
|
||||
|
||||
- **การฆ่าเชื้อการตอบสนอง** — ตัดช่องที่ไม่ได้มาตรฐานออกจากการตอบสนองในรูปแบบ OpenAI (ทั้งแบบสตรีมมิ่งและไม่ใช่สตรีมมิ่ง) เพื่อให้มั่นใจว่าสอดคล้องกับ SDK ที่เข้มงวด
|
||||
- **การปรับบทบาทให้เป็นมาตรฐาน** — แปลง `developer` → `system` สำหรับเป้าหมายที่ไม่ใช่ OpenAI ผสาน `system` → `user` สำหรับโมเดลที่ปฏิเสธบทบาทของระบบ (GLM, ERNIE)
|
||||
- **ลองแยกแท็ก** — แยกวิเคราะห์บล็อก `<think>...</think>` จากเนื้อหาลงในฟิลด์ `reasoning_content`
|
||||
- **เอาต์พุตที่มีโครงสร้าง** — แปลง OpenAI `response_format.json_schema` เป็น `responseMimeType` + `responseSchema` ของ Gemini
|
||||
|
||||
## จุดสิ้นสุด API ที่รองรับ
|
||||
|
||||
| จุดสิ้นสุด | Format | ตัวจัดการ |
|
||||
| -------------------------------------------------- | --------------------- | ------------------------------------------------------ |
|
||||
| `POST /v1/chat/completions` | แชท OpenAI | `src/sse/handlers/chat.ts` |
|
||||
| `POST /v1/messages` | ข้อความของคลอดด์ | ตัวจัดการเดียวกัน (ตรวจพบอัตโนมัติ) |
|
||||
| `POST /v1/responses` | การตอบสนองของ OpenAI | `open-sse/handlers/responsesHandler.ts` |
|
||||
| `POST /v1/embeddings` | การฝัง OpenAI | `open-sse/handlers/embeddings.ts` |
|
||||
| `GET /v1/embeddings` | รายการรุ่น | เส้นทาง API |
|
||||
| `POST /v1/images/generations` | รูปภาพ OpenAI | `open-sse/handlers/imageGeneration.ts` |
|
||||
| `GET /v1/images/generations` | รายการรุ่น | เส้นทาง API |
|
||||
| `POST /v1/providers/{provider}/chat/completions` | แชท OpenAI | เฉพาะต่อผู้ให้บริการพร้อมการตรวจสอบโมเดล |
|
||||
| `POST /v1/providers/{provider}/embeddings` | การฝัง OpenAI | เฉพาะต่อผู้ให้บริการพร้อมการตรวจสอบโมเดล |
|
||||
| `POST /v1/providers/{provider}/images/generations` | รูปภาพ OpenAI | เฉพาะต่อผู้ให้บริการพร้อมการตรวจสอบโมเดล |
|
||||
| `POST /v1/messages/count_tokens` | จำนวนโทเค็นของ Claude | เส้นทาง API |
|
||||
| `GET /v1/models` | รายการโมเดล OpenAI | เส้นทาง API (แชท + การฝัง + รูปภาพ + โมเดลที่กำหนดเอง) |
|
||||
| `GET /api/models/catalog` | แคตตาล็อก | ทุกรุ่นจัดกลุ่มตามผู้ให้บริการ + ประเภท |
|
||||
| `POST /v1beta/models/*:streamGenerateContent` | ชาวราศีเมถุนพื้นเมือง | เส้นทาง API |
|
||||
| `GET/PUT/DELETE /api/settings/proxy` | การกำหนดค่าพร็อกซี | การกำหนดค่าพร็อกซีเครือข่าย |
|
||||
| `POST /api/settings/proxy/test` | การเชื่อมต่อพร็อกซี | จุดสิ้นสุดการทดสอบความสมบูรณ์ของพร็อกซี/การเชื่อมต่อ |
|
||||
| `GET/POST/DELETE /api/provider-models` | โมเดลที่กำหนดเอง | การจัดการโมเดลแบบกำหนดเองต่อผู้ให้บริการ |
|
||||
|
||||
## บายพาสตัวจัดการ
|
||||
|
||||
ตัวจัดการบายพาส (`open-sse/utils/bypassHandler.ts`) สกัดกั้นคำขอ "ทิ้ง" ที่รู้จักจาก Claude CLI - การปิงอุ่นเครื่อง การแยกชื่อ และจำนวนโทเค็น - และส่งคืน **การตอบกลับปลอม** โดยไม่ต้องใช้โทเค็นของผู้ให้บริการอัปสตรีม สิ่งนี้จะถูกทริกเกอร์เฉพาะเมื่อ `User-Agent` มี `claude-cli`
|
||||
|
||||
## ขอไปป์ไลน์ Logger
|
||||
|
||||
ตัวบันทึกคำขอ (`open-sse/utils/requestLogger.ts`) จัดเตรียมไปป์ไลน์การบันทึกการดีบัก 7 ขั้นตอน ซึ่งปิดใช้งานโดยค่าเริ่มต้น เปิดใช้งานผ่าน `ENABLE_REQUEST_LOGS=true`:
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
ไฟล์ถูกเขียนไปที่ `<repo>/logs/<session>/` สำหรับแต่ละเซสชันคำขอ
|
||||
|
||||
## โหมดความล้มเหลวและความยืดหยุ่น
|
||||
|
||||
## 1) ความพร้อมใช้งานของบัญชี/ผู้ให้บริการ
|
||||
|
||||
- คูลดาวน์บัญชีผู้ให้บริการเกี่ยวกับข้อผิดพลาดชั่วคราว/อัตรา/การตรวจสอบสิทธิ์
|
||||
- ทางเลือกบัญชีก่อนที่จะล้มเหลวในการร้องขอ
|
||||
- ทางเลือกของโมเดลคอมโบเมื่อพาธของโมเดล/ผู้ให้บริการปัจจุบันหมดลง
|
||||
|
||||
## 2) โทเค็นหมดอายุ
|
||||
|
||||
- ตรวจสอบล่วงหน้าและรีเฟรชด้วยการลองอีกครั้งสำหรับผู้ให้บริการที่รีเฟรชได้
|
||||
- 401/403 ลองอีกครั้งหลังจากพยายามรีเฟรชในเส้นทางหลัก
|
||||
|
||||
## 3) ความปลอดภัยของสตรีม
|
||||
|
||||
- ตัวควบคุมสตรีมที่รับรู้การตัดการเชื่อมต่อ
|
||||
- สตรีมการแปลพร้อมฟลัชปลายสตรีมและการจัดการ `[DONE]`
|
||||
- การประมาณการใช้งานสำรองเมื่อข้อมูลเมตาการใช้งานของผู้ให้บริการหายไป
|
||||
|
||||
## 4) การเสื่อมสภาพของ Cloud Sync
|
||||
|
||||
- ข้อผิดพลาดในการซิงค์ปรากฏขึ้น แต่รันไทม์ในเครื่องยังคงดำเนินต่อไป
|
||||
- ตัวกำหนดตารางเวลามีตรรกะที่สามารถลองใหม่ได้ แต่การดำเนินการตามระยะเวลาในปัจจุบันจะเรียกการซิงค์แบบพยายามครั้งเดียวตามค่าเริ่มต้น
|
||||
|
||||
## 5) ความสมบูรณ์ของข้อมูล
|
||||
|
||||
- การโยกย้าย / ซ่อมแซมรูปร่าง DB สำหรับคีย์ที่หายไป
|
||||
- การป้องกันการรีเซ็ต JSON ที่เสียหายสำหรับ localDb และการใช้งานDb
|
||||
|
||||
## ความสามารถในการสังเกตและสัญญาณการปฏิบัติงาน
|
||||
|
||||
แหล่งที่มาของการมองเห็นรันไทม์:
|
||||
|
||||
- บันทึกคอนโซลจาก `src/sse/utils/logger.ts`
|
||||
- รวมการใช้งานต่อคำขอใน `usage.json`
|
||||
- บันทึกสถานะคำขอที่เป็นข้อความใน `log.txt`
|
||||
- บันทึกคำขอ/การแปลเชิงลึกเพิ่มเติมภายใต้ `logs/` เมื่อ `ENABLE_REQUEST_LOGS=true`
|
||||
- จุดสิ้นสุดการใช้งานแดชบอร์ด (`/api/usage/*`) สำหรับการใช้ UI
|
||||
|
||||
## ขอบเขตที่ละเอียดอ่อนด้านความปลอดภัย
|
||||
|
||||
- ความลับ JWT (`JWT_SECRET`) รักษาความปลอดภัยการตรวจสอบ / การลงนามคุกกี้เซสชันแดชบอร์ด
|
||||
- ทางเลือกรหัสผ่านเริ่มต้น (`INITIAL_PASSWORD`, ค่าเริ่มต้น `123456`) จะต้องถูกแทนที่ในการปรับใช้จริง
|
||||
- คีย์ API ความลับ HMAC (`API_KEY_SECRET`) รักษาความปลอดภัยรูปแบบคีย์ API ในเครื่องที่สร้างขึ้น
|
||||
- ความลับของผู้ให้บริการ (คีย์/โทเค็น API) ยังคงอยู่ในฐานข้อมูลในเครื่องและควรได้รับการปกป้องในระดับระบบไฟล์
|
||||
- จุดสิ้นสุดการซิงค์บนคลาวด์อาศัยการตรวจสอบสิทธิ์คีย์ API + ซีแมนทิกส์รหัสเครื่อง
|
||||
|
||||
## สภาพแวดล้อมและเมทริกซ์รันไทม์
|
||||
|
||||
ตัวแปรสภาพแวดล้อมที่ใช้งานโดยโค้ด:
|
||||
|
||||
- แอป/การรับรองความถูกต้อง: `JWT_SECRET`, `INITIAL_PASSWORD`
|
||||
- ที่เก็บข้อมูล: `DATA_DIR`
|
||||
- ลักษณะการทำงานของโหนดที่เข้ากันได้: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- การแทนที่ฐานจัดเก็บข้อมูลเสริม (Linux/macOS เมื่อ `DATA_DIR` ไม่ได้ตั้งค่า): `XDG_CONFIG_HOME`
|
||||
- การรักษาความปลอดภัย: `API_KEY_SECRET`, `MACHINE_ID_SALT`
|
||||
- การบันทึก: `ENABLE_REQUEST_LOGS`
|
||||
- การซิงโครไนซ์/คลาวด์ URL: `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_CLOUD_URL`
|
||||
- พร็อกซีขาออก: `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` และรูปแบบตัวพิมพ์เล็ก
|
||||
- ธงคุณลักษณะ SOCKS5: `ENABLE_SOCKS5_PROXY`, `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- ตัวช่วยแพลตฟอร์ม/รันไทม์ (ไม่ใช่การกำหนดค่าเฉพาะแอป): `APPDATA`, `NODE_ENV`, `PORT`, `HOSTNAME`
|
||||
|
||||
## หมายเหตุทางสถาปัตยกรรมที่เป็นที่รู้จัก
|
||||
|
||||
1. `usageDb` และ `localDb` แชร์นโยบายไดเรกทอรีฐานเดียวกัน (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) ด้วยการย้ายไฟล์แบบเดิม
|
||||
2. `/api/v1/route.ts` ส่งคืนรายการโมเดลแบบคงที่ และไม่ใช่แหล่งที่มาของโมเดลหลักที่ใช้โดย `/v1/models`
|
||||
3. ตัวบันทึกคำขอเขียนส่วนหัว/เนื้อหาแบบเต็มเมื่อเปิดใช้งาน ถือว่าไดเร็กทอรีบันทึกมีความละเอียดอ่อน
|
||||
4. พฤติกรรมของคลาวด์ขึ้นอยู่กับ `NEXT_PUBLIC_BASE_URL` ที่ถูกต้องและความสามารถในการเข้าถึงจุดสิ้นสุดของคลาวด์
|
||||
5. ไดเร็กทอรี `open-sse/` ได้รับการเผยแพร่เป็น `@omniroute/open-sse` **แพ็กเกจพื้นที่ทำงาน npm** ซอร์สโค้ดนำเข้าผ่าน `@omniroute/open-sse/...` (แก้ไขโดย Next.js `transpilePackages`) พาธของไฟล์ในเอกสารนี้ยังคงใช้ชื่อไดเร็กทอรี `open-sse/` เพื่อความสอดคล้องกัน
|
||||
6. แผนภูมิในแดชบอร์ดใช้ **แผนภูมิใหม่** (อิงตาม SVG) สำหรับการแสดงภาพการวิเคราะห์เชิงโต้ตอบที่เข้าถึงได้ (แผนภูมิแท่งการใช้งานโมเดล ตารางแจกแจงผู้ให้บริการพร้อมอัตราความสำเร็จ)
|
||||
7. การทดสอบ E2E ใช้ **นักเขียนบทละคร** (`tests/e2e/`) รันผ่าน `npm run test:e2e` การทดสอบหน่วยใช้ **ตัวดำเนินการทดสอบ Node.js** (`tests/unit/`) รันผ่าน `npm run test:plan3` ซอร์สโค้ดภายใต้ `src/` คือ **TypeScript** (`.ts`/`.tsx`); เวิร์กสเปซ `open-sse/` ยังคงเป็น JavaScript (`.js`)
|
||||
8. หน้าการตั้งค่าแบ่งออกเป็น 5 แท็บ: ความปลอดภัย การกำหนดเส้นทาง (6 กลยุทธ์ระดับโลก: เติมก่อน ปัดเศษ p2c สุ่ม ใช้น้อยที่สุด ปรับต้นทุนให้เหมาะสม) ความยืดหยุ่น (จำกัดอัตราที่แก้ไขได้ เซอร์กิตเบรกเกอร์ นโยบาย) AI (การคิดงบประมาณ พรอมต์ของระบบ แคชพร้อมต์) ขั้นสูง (พร็อกซี)
|
||||
|
||||
## รายการตรวจสอบการตรวจสอบการปฏิบัติงาน
|
||||
|
||||
- สร้างจากแหล่งที่มา: `npm run build`
|
||||
- สร้างอิมเมจนักเทียบท่า: `docker build -t omniroute .`
|
||||
- เริ่มบริการและตรวจสอบ:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- URL ฐานเป้าหมาย CLI ควรเป็น `http://<host>:20128/v1` เมื่อ `PORT=20128`
|
||||
589
docs/i18n/th/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/th/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# omniroute — เอกสาร Codebase
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](../pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](../es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](../fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](../it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](../ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](../zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](../de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](../in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](../th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](../uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](../ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](../ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](../vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](../bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](../da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](../fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](../he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](../hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](../id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](../ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](../ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](../nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](../no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](../pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](../ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](../pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](../sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](../sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](../phi/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> คู่มือที่ครอบคลุมและเหมาะสำหรับผู้เริ่มต้นสำหรับเราเตอร์พร็อกซี AI ของผู้ให้บริการหลายราย **omniroute**
|
||||
|
||||
---
|
||||
|
||||
## 1. Omniroute คืออะไร?
|
||||
|
||||
Omniroute คือ **เราเตอร์พร็อกซี** ที่อยู่ระหว่างไคลเอนต์ AI (Claude CLI, Codex, Cursor IDE ฯลฯ) และผู้ให้บริการ AI (Anthropic, Google, OpenAI, AWS, GitHub ฯลฯ) มันแก้ปัญหาใหญ่อย่างหนึ่ง:
|
||||
|
||||
> **ไคลเอนต์ AI ต่างกันพูด "ภาษา" ที่แตกต่างกัน (รูปแบบ API) และผู้ให้บริการ AI ต่างคาดหวัง "ภาษา" ที่แตกต่างกันเช่นกัน** การแปลทุกเส้นทางระหว่างกันโดยอัตโนมัติ
|
||||
|
||||
ลองคิดดูว่าสิ่งนี้เหมือนกับนักแปลสากลขององค์การสหประชาชาติ ผู้แทนทุกคนสามารถพูดภาษาใดก็ได้ และผู้แปลจะแปลงภาษาดังกล่าวให้กับผู้แทนคนอื่นๆ
|
||||
|
||||
---
|
||||
|
||||
## 2. ภาพรวมสถาปัตยกรรม
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### หลักการสำคัญ: การแปลแบบ Hub-and-Spoke
|
||||
|
||||
การแปลทุกรูปแบบผ่าน **รูปแบบ OpenAI เป็นศูนย์กลาง**:
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
ซึ่งหมายความว่าคุณต้องการเพียง **N ตัวแปล** (หนึ่งตัวต่อรูปแบบ) แทนที่จะเป็น **N²** (ทุกคู่)
|
||||
|
||||
---
|
||||
|
||||
## 3. โครงสร้างโครงการ
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. การแยกย่อยแบบโมดูลต่อโมดูล
|
||||
|
||||
### 4.1 การกำหนดค่า (`open-sse/config/`)
|
||||
|
||||
**แหล่งความจริงแหล่งเดียว** สำหรับการกำหนดค่าของผู้ให้บริการทั้งหมด
|
||||
|
||||
| ไฟล์ | วัตถุประสงค์ |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `constants.ts` | `PROVIDERS` ออบเจ็กต์ที่มี URL พื้นฐาน ข้อมูลประจำตัว OAuth (ค่าเริ่มต้น) ส่วนหัว และระบบแจ้งเริ่มต้นสำหรับผู้ให้บริการทุกราย ยังกำหนด `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG` และ `SKIP_PATTERNS` อีกด้วย |
|
||||
| `credentialLoader.ts` | โหลดข้อมูลรับรองภายนอกจาก `data/provider-credentials.json` และรวมเข้ากับค่าเริ่มต้นที่ฮาร์ดโค้ดใน `PROVIDERS` เก็บความลับไว้นอกเหนือการควบคุมของแหล่งที่มาในขณะที่ยังคงความเข้ากันได้แบบย้อนหลัง |
|
||||
| `providerModels.ts` | การลงทะเบียนโมเดลส่วนกลาง: นามแฝงของผู้ให้บริการแผนที่ → รหัสโมเดล ฟังก์ชันเช่น `getModels()`, `getProviderByAlias()` |
|
||||
| `codexInstructions.ts` | คำแนะนำของระบบที่แทรกเข้าไปในคำขอ Codex (การแก้ไขข้อจำกัด กฎแซนด์บ็อกซ์ นโยบายการอนุมัติ) |
|
||||
| `defaultThinkingSignature.ts` | ลายเซ็น "การคิด" เริ่มต้นสำหรับโมเดล Claude และ Gemini |
|
||||
| `ollamaModels.ts` | คำจำกัดความของสคีมาสำหรับโมเดล Ollama ท้องถิ่น (ชื่อ ขนาด ตระกูล การหาปริมาณ) |
|
||||
|
||||
#### ขั้นตอนการโหลดข้อมูลรับรอง
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 ผู้ดำเนินการ (`open-sse/executors/`)
|
||||
|
||||
ผู้ดำเนินการสรุป **ตรรกะเฉพาะของผู้ให้บริการ** โดยใช้ **รูปแบบกลยุทธ์** ตัวดำเนินการแต่ละตัวจะแทนที่วิธีพื้นฐานตามความจำเป็น
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
| ผู้ดำเนินการ | ผู้ให้บริการ | ความเชี่ยวชาญพิเศษที่สำคัญ |
|
||||
| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `base.ts` | — | ฐานบทคัดย่อ: การสร้าง URL, ส่วนหัว, ตรรกะการลองใหม่, การรีเฟรชข้อมูลรับรอง |
|
||||
| `default.ts` | Claude, เมถุน, OpenAI, GLM, Kimi, MiniMax | การรีเฟรชโทเค็น OAuth ทั่วไปสำหรับผู้ให้บริการมาตรฐาน |
|
||||
| `antigravity.ts` | รหัส Google Cloud | การสร้างรหัสโปรเจ็กต์/เซสชัน, ทางเลือกหลาย URL, ลองแยกวิเคราะห์ข้อความแสดงข้อผิดพลาดแบบกำหนดเองอีกครั้ง ("รีเซ็ตหลังจาก 2 ชม. 7 นาที 23 วินาที") |
|
||||
| `cursor.ts` | เคอร์เซอร์ IDE | **ซับซ้อนที่สุด**: การตรวจสอบสิทธิ์การตรวจสอบ SHA-256, การเข้ารหัสคำขอ Protobuf, ไบนารี EventStream → การแยกวิเคราะห์การตอบสนอง SSE |
|
||||
| `codex.ts` | OpenAI Codex | ใส่คำสั่งของระบบ จัดการระดับการคิด ลบพารามิเตอร์ที่ไม่รองรับ |
|
||||
| `gemini-cli.ts` | Google ราศีเมถุน CLI | การสร้าง URL ที่กำหนดเอง (`streamGenerateContent`), การรีเฟรชโทเค็น Google OAuth |
|
||||
| `github.ts` | นักบิน GitHub | ระบบโทเค็นคู่ (โทเค็น GitHub OAuth + โทเค็น Copilot) การเลียนแบบส่วนหัว VSCode |
|
||||
| `kiro.ts` | AWS CodeWhisperer | การแยกวิเคราะห์ไบนารี AWS EventStream, เฟรมเหตุการณ์ AMZN, การประมาณโทเค็น |
|
||||
| `index.ts` | — | โรงงาน: ชื่อผู้ให้บริการแผนที่ → คลาสผู้ดำเนินการ โดยมีค่าเริ่มต้นสำรอง |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 ตัวจัดการ (`open-sse/handlers/`)
|
||||
|
||||
**เลเยอร์การเรียบเรียง** — ประสานงานการแปล การดำเนินการ การสตรีม และการจัดการข้อผิดพลาด
|
||||
|
||||
| ไฟล์ | วัตถุประสงค์ |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `chatCore.ts` | **ผู้เรียบเรียงกลาง** (~600 บรรทัด) จัดการวงจรคำขอที่สมบูรณ์: การตรวจจับรูปแบบ → การแปล → การส่งตัวดำเนินการ → การตอบสนองแบบสตรีมมิ่ง/ไม่สตรีมมิ่ง → การรีเฟรชโทเค็น → การจัดการข้อผิดพลาด → การบันทึกการใช้งาน |
|
||||
| `responsesHandler.ts` | อะแดปเตอร์สำหรับ Responses API ของ OpenAI: แปลงรูปแบบการตอบกลับ → การแชทเสร็จสิ้น → ส่งไปที่ `chatCore` → แปลง SSE กลับเป็นรูปแบบการตอบกลับ |
|
||||
| `embeddings.ts` | ตัวจัดการการสร้างการฝัง: แก้ไขโมเดลการฝัง → ผู้ให้บริการ, ส่งไปยัง API ของผู้ให้บริการ, ส่งคืนการตอบสนองการฝังที่เข้ากันได้กับ OpenAI รองรับผู้ให้บริการ 6+ ราย |
|
||||
| `imageGeneration.ts` | ตัวจัดการการสร้างรูปภาพ: แก้ไขโมเดลรูปภาพ → ผู้ให้บริการ รองรับโหมดที่เข้ากันได้กับ OpenAI, Gemini-image (ต้านแรงโน้มถ่วง) และโหมดทางเลือก (Nebius) ส่งกลับภาพ base64 หรือ URL |
|
||||
|
||||
#### ระยะเวลาคำขอ (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 บริการ (`open-sse/services/`)
|
||||
|
||||
ตรรกะทางธุรกิจที่สนับสนุนตัวจัดการและผู้ดำเนินการ
|
||||
|
||||
| ไฟล์ | วัตถุประสงค์ |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `provider.ts` | **การตรวจจับรูปแบบ** (`detectFormat`): วิเคราะห์โครงสร้างคำขอเพื่อระบุรูปแบบ Claude/OpenAI/Gemini/Antigravity/Responses (รวมถึง `max_tokens` heuristic สำหรับ Claude) นอกจากนี้: การสร้าง URL, การสร้างส่วนหัว, การคิดการกำหนดค่าให้เป็นมาตรฐาน รองรับผู้ให้บริการแบบไดนามิก `openai-compatible-*` และ `anthropic-compatible-*` |
|
||||
| `model.ts` | การแยกวิเคราะห์สตริงโมเดล (`claude/model-name` → `{provider: "claude", model: "model-name"}`), การแก้ไขนามแฝงด้วยการตรวจจับการชนกัน, การดูแลอินพุต (ปฏิเสธอักขระการแวะผ่านพาธ/อักขระควบคุม) และการแก้ไขข้อมูลโมเดลด้วยการสนับสนุน getter นามแฝง async |
|
||||
| `accountFallback.ts` | การจัดการขีดจำกัดอัตรา: การถอยกลับแบบเอ็กซ์โปเนนเชียล (1 วินาที → 2 วินาที → 4 วินาที → สูงสุด 2 นาที), การจัดการคูลดาวน์บัญชี, การจัดหมวดหมู่ข้อผิดพลาด (ซึ่งข้อผิดพลาดทำให้เกิดทางเลือกเทียบกับไม่) |
|
||||
| `tokenRefresh.ts` | การรีเฟรชโทเค็น OAuth สำหรับ **ผู้ให้บริการทุกราย**: Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth) รวมแคชการขจัดความซ้ำซ้อนตามสัญญาในเที่ยวบิน และลองอีกครั้งโดยใช้การแบ็คออฟแบบเอ็กซ์โปเนนเชียล |
|
||||
| `combo.ts` | **โมเดลคอมโบ**: เชนของโมเดลสำรอง หากโมเดล A ล้มเหลวโดยมีข้อผิดพลาดที่มีสิทธิ์ใช้ทางเลือก ให้ลองใช้โมเดล B จากนั้นตามด้วย C ฯลฯ ส่งกลับรหัสสถานะอัปสตรีมจริง |
|
||||
| `usage.ts` | ดึงข้อมูลโควต้า/การใช้งานจาก API ของผู้ให้บริการ (โควต้า GitHub Copilot, โควต้าโมเดล Antigravity, ขีดจำกัดอัตรา Codex, การแจกแจงการใช้งาน Kiro, การตั้งค่า Claude) |
|
||||
| `accountSelector.ts` | การเลือกบัญชีอัจฉริยะพร้อมอัลกอริธึมการให้คะแนน: พิจารณาลำดับความสำคัญ สถานะสุขภาพ ตำแหน่งการวนซ้ำ และสถานะคูลดาวน์ เพื่อเลือกบัญชีที่เหมาะสมที่สุดสำหรับคำขอแต่ละรายการ |
|
||||
| `contextManager.ts` | การจัดการวงจรชีวิตของคำขอ: สร้างและติดตามออบเจ็กต์บริบทต่อคำขอด้วยข้อมูลเมตา (ID คำขอ การประทับเวลา ข้อมูลผู้ให้บริการ) สำหรับการดีบักและการบันทึก |
|
||||
| `ipFilter.ts` | การควบคุมการเข้าถึงตาม IP: รองรับโหมดรายการที่อนุญาตและรายการที่บล็อก ตรวจสอบ IP ไคลเอ็นต์กับกฎที่กำหนดค่าไว้ก่อนที่จะประมวลผลคำขอ API |
|
||||
| `sessionManager.ts` | การติดตามเซสชันด้วยการพิมพ์ลายนิ้วมือไคลเอ็นต์: ติดตามเซสชันที่ใช้งานอยู่โดยใช้ตัวระบุไคลเอ็นต์แบบแฮช ตรวจสอบจำนวนคำขอ และจัดเตรียมตัววัดเซสชัน |
|
||||
| `signatureCache.ts` | ขอแคชการขจัดข้อมูลซ้ำซ้อนตามลายเซ็น: ป้องกันคำขอที่ซ้ำกันโดยการแคชลายเซ็นคำขอล่าสุด และส่งคืนการตอบกลับที่แคชไว้สำหรับคำขอที่เหมือนกันภายในกรอบเวลา |
|
||||
| `systemPrompt.ts` | การแทรกพร้อมท์ของระบบทั่วโลก: เพิ่มหรือต่อท้ายพรอมต์ระบบที่กำหนดค่าได้สำหรับคำขอทั้งหมด โดยมีการจัดการความเข้ากันได้ต่อผู้ให้บริการ |
|
||||
| `thinkingBudget.ts` | การจัดการงบประมาณโทเค็นการใช้เหตุผล: รองรับโหมดส่งผ่าน, อัตโนมัติ (กำหนดค่าการคิดแบบสตริป), กำหนดเอง (งบประมาณคงที่) และโหมดการปรับตัว (ปรับขนาดความซับซ้อน) สำหรับการควบคุมโทเค็นการคิด/การใช้เหตุผล |
|
||||
| `wildcardRouter.ts` | การกำหนดเส้นทางรูปแบบโมเดลไวด์การ์ด: แก้ไขรูปแบบไวด์การ์ด (เช่น `*/claude-*`) ให้เป็นคู่ผู้ให้บริการ/โมเดลที่เป็นรูปธรรมโดยขึ้นอยู่กับความพร้อมใช้งานและลำดับความสำคัญ |
|
||||
|
||||
#### การรีเฟรชโทเค็นซ้ำซ้อน
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### เครื่องสถานะสำรองบัญชี
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### โซ่โมเดลคอมโบ
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 นักแปล (`open-sse/translator/`)
|
||||
|
||||
**เครื่องมือแปลรูปแบบ** ใช้ระบบปลั๊กอินที่ลงทะเบียนด้วยตนเอง
|
||||
|
||||
#### สถาปัตยกรรม
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
| ไดเรกทอรี | ไฟล์ | คำอธิบาย |
|
||||
| ------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `request/` | นักแปล 8 คน | แปลงเนื้อหาคำขอระหว่างรูปแบบ แต่ละไฟล์ลงทะเบียนด้วยตนเองผ่าน `register(from, to, fn)` เมื่อนำเข้า |
|
||||
| `response/` | นักแปล 7 คน | แปลงส่วนการตอบสนองการสตรีมระหว่างรูปแบบ จัดการประเภทเหตุการณ์ SSE, บล็อกการคิด, การเรียกใช้เครื่องมือ |
|
||||
| `helpers/` | 6 ตัวช่วย | ยูทิลิตี้ที่ใช้ร่วมกัน: `claudeHelper` (การแยกพร้อมท์ของระบบ, การกำหนดค่าการคิด), `geminiHelper` (การแมปชิ้นส่วน/เนื้อหา), `openaiHelper` (การกรองรูปแบบ), `toolCallHelper` (การสร้าง ID, การแทรกการตอบสนองที่ขาดหายไป), `maxTokensHelper`, `responsesApiHelper` |
|
||||
| `index.ts` | — | เครื่องมือการแปล: `translateRequest()`, `translateResponse()`, การจัดการสถานะ, การลงทะเบียน |
|
||||
| `formats.ts` | — | รูปแบบค่าคงที่: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES` |
|
||||
|
||||
#### การออกแบบหลัก: ปลั๊กอินที่ลงทะเบียนด้วยตนเอง
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 การใช้งาน (`open-sse/utils/`)
|
||||
|
||||
| ไฟล์ | วัตถุประสงค์ |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `error.ts` | การสร้างการตอบสนองข้อผิดพลาด (รูปแบบที่เข้ากันได้กับ OpenAI), การแยกวิเคราะห์ข้อผิดพลาดอัปสตรีม, การแยกเวลาลองต้านแรงโน้มถ่วงอีกครั้งจากข้อความแสดงข้อผิดพลาด, การสตรีมข้อผิดพลาด SSE |
|
||||
| `stream.ts` | **SSE Transform Stream** — ไปป์ไลน์การสตรีมหลัก สองโหมด: `TRANSLATE` (การแปลรูปแบบเต็ม) และ `PASSTHROUGH` (ทำให้เป็นมาตรฐาน + แยกการใช้งาน) จัดการการบัฟเฟอร์แบบก้อน การประมาณการใช้งาน การติดตามความยาวของเนื้อหา อินสแตนซ์ตัวเข้ารหัส/ตัวถอดรหัสต่อสตรีมหลีกเลี่ยงสถานะที่ใช้ร่วมกัน |
|
||||
| `streamHelpers.ts` | ยูทิลิตี้ SSE ระดับต่ำ: `parseSSELine` (ทนต่อช่องว่าง), `hasValuableContent` (กรองส่วนว่างสำหรับ OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (การจัดลำดับ SSE ที่รับรู้รูปแบบด้วยการล้างข้อมูล `perf_metrics`) |
|
||||
| `usageTracking.ts` | การแยกการใช้โทเค็นจากรูปแบบใดๆ (Claude/OpenAI/Gemini/Responses) การประมาณค่าด้วยอัตราส่วนเครื่องมือ/ข้อความที่แยกจากกันต่ออักขระ การเพิ่มบัฟเฟอร์ (อัตราความปลอดภัยของโทเค็น 2,000 โทเค็น) การกรองฟิลด์เฉพาะรูปแบบ การบันทึกคอนโซลด้วยสี ANSI |
|
||||
| `requestLogger.ts` | การบันทึกคำขอตามไฟล์ (เลือกใช้ผ่าน `ENABLE_REQUEST_LOGS=true`) สร้างโฟลเดอร์เซสชันด้วยไฟล์ที่มีหมายเลขกำกับ: `1_req_client.json` → `7_res_client.txt` I/O ทั้งหมดเป็นแบบอะซิงโครนัส (fire-and-forget) มาสก์ส่วนหัวที่ละเอียดอ่อน |
|
||||
| `bypassHandler.ts` | สกัดกั้นรูปแบบเฉพาะจาก Claude CLI (การแยกชื่อ การอุ่นเครื่อง การนับ) และส่งคืนการตอบกลับปลอมโดยไม่ต้องโทรหาผู้ให้บริการใดๆ รองรับทั้งสตรีมมิ่งและไม่สตรีมมิ่ง จำกัดโดยเจตนาไว้ที่ขอบเขตของ Claude CLI |
|
||||
| `networkProxy.ts` | แก้ไข URL พร็อกซีขาออกสำหรับผู้ให้บริการที่กำหนดโดยมีความสำคัญ: การกำหนดค่าเฉพาะผู้ให้บริการ → การกำหนดค่าส่วนกลาง → ตัวแปรสภาพแวดล้อม (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`) รองรับการยกเว้น `NO_PROXY` กำหนดค่าแคชเป็นเวลา 30 วินาที |
|
||||
|
||||
#### ไปป์ไลน์สตรีมมิ่ง SSE
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### โครงสร้างเซสชันตัวบันทึกคำขอ
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 ชั้นแอปพลิเคชัน (`src/`)
|
||||
|
||||
| ไดเรกทอรี | วัตถุประสงค์ |
|
||||
| ------------- | ------------------------------------------------------------------------------------- |
|
||||
| `src/app/` | UI ของเว็บ, เส้นทาง API, มิดเดิลแวร์ด่วน, ตัวจัดการการเรียกกลับ OAuth |
|
||||
| `src/lib/` | การเข้าถึงฐานข้อมูล (`localDb.ts`, `usageDb.ts`), การรับรองความถูกต้อง, ที่ใช้ร่วมกัน |
|
||||
| `src/mitm/` | ยูทิลิตี้พร็อกซีแบบ Man-in-the-middle สำหรับการสกัดกั้นการรับส่งข้อมูลของผู้ให้บริการ |
|
||||
| `src/models/` | คำจำกัดความของโมเดลฐานข้อมูล |
|
||||
| `src/shared/` | Wrappers รอบฟังก์ชัน open-sse (ผู้ให้บริการ สตรีม ข้อผิดพลาด ฯลฯ) |
|
||||
| `src/sse/` | ตัวจัดการตำแหน่งข้อมูล SSE ที่เชื่อมต่อไลบรารี open-sse ไปยังเส้นทางด่วน |
|
||||
| `src/store/` | การจัดการสถานะแอปพลิเคชัน |
|
||||
|
||||
#### เส้นทาง API ที่โดดเด่น
|
||||
|
||||
| เส้นทาง | วิธีการ | วัตถุประสงค์ |
|
||||
| --------------------------------------------- | ------------ | ----------------------------------------------------------------------------- | ------- |
|
||||
| `/api/provider-models` | รับ/โพสต์/ลบ | CRUD สำหรับโมเดลที่กำหนดเองต่อผู้ให้บริการ |
|
||||
| `/api/models/catalog` | รับ | แค็ตตาล็อกรวมของทุกรุ่น (แชท การฝัง รูปภาพ กำหนดเอง) จัดกลุ่มตามผู้ให้บริการ |
|
||||
| `/api/settings/proxy` | รับ/วาง/ลบ | การกำหนดค่าพร็อกซีขาออกแบบลำดับชั้น (`global/providers/combos/keys`) |
|
||||
| `/api/settings/proxy/test` | โพสต์ | ตรวจสอบการเชื่อมต่อพร็อกซีและส่งคืน IP/เวลาแฝง | สาธารณะ |
|
||||
| `/v1/providers/[provider]/chat/completions` | โพสต์ | การแชทต่อผู้ให้บริการโดยเฉพาะพร้อมการตรวจสอบความถูกต้องของโมเดล |
|
||||
| `/v1/providers/[provider]/embeddings` | โพสต์ | การฝังต่อผู้ให้บริการโดยเฉพาะพร้อมการตรวจสอบความถูกต้องของโมเดล |
|
||||
| `/v1/providers/[provider]/images/generations` | โพสต์ | การสร้างอิมเมจต่อผู้ให้บริการโดยเฉพาะพร้อมการตรวจสอบโมเดล |
|
||||
| `/api/settings/ip-filter` | รับ/ใส่ | การจัดการรายการ IP ที่อนุญาต/รายการบล็อก |
|
||||
| `/api/settings/thinking-budget` | รับ/ใส่ | การกำหนดค่างบประมาณโทเค็นการให้เหตุผล (ส่งผ่าน/อัตโนมัติ/กำหนดเอง/แบบปรับได้) |
|
||||
| `/api/settings/system-prompt` | รับ/ใส่ | ระบบ Global พร้อมฉีดสำหรับทุกคำขอ |
|
||||
| `/api/sessions` | รับ | การติดตามเซสชันและตัวชี้วัดที่ใช้งานอยู่ |
|
||||
| `/api/rate-limits` | รับ | สถานะขีดจำกัดอัตราต่อบัญชี |
|
||||
|
||||
---
|
||||
|
||||
## 5. รูปแบบการออกแบบที่สำคัญ
|
||||
|
||||
### 5.1 การแปลแบบ Hub และ Spoke
|
||||
|
||||
ทุกรูปแบบแปลผ่าน **รูปแบบ OpenAI เป็นศูนย์กลาง** การเพิ่มผู้ให้บริการใหม่จำเป็นต้องมีการเขียนนักแปล **หนึ่งคู่** (ถึง/จาก OpenAI) ไม่ใช่ N คู่
|
||||
|
||||
### 5.2 รูปแบบกลยุทธ์ผู้บริหาร
|
||||
|
||||
ผู้ให้บริการแต่ละรายมีคลาสตัวดำเนินการเฉพาะที่สืบทอดมาจาก `BaseExecutor` โรงงานใน `executors/index.ts` เลือกโรงงานที่เหมาะสมขณะรันไทม์
|
||||
|
||||
### 5.3 ระบบปลั๊กอินลงทะเบียนด้วยตนเอง
|
||||
|
||||
โมดูลนักแปลลงทะเบียนตัวเองในการนำเข้าผ่าน `register()` การเพิ่มนักแปลใหม่เป็นเพียงการสร้างไฟล์และนำเข้าเท่านั้น
|
||||
|
||||
### 5.4 บัญชีสำรองพร้อม Exponential Backoff
|
||||
|
||||
เมื่อผู้ให้บริการส่งคืน 429/401/500 ระบบสามารถสลับไปยังบัญชีถัดไป โดยใช้คูลดาวน์แบบเอ็กซ์โพเนนเชียล (1 วินาที → 2 วินาที → 4 วินาที → สูงสุด 2 นาที)
|
||||
|
||||
### 5.5 โซ่รุ่นคอมโบ
|
||||
|
||||
"คำสั่งผสม" จัดกลุ่มสตริง `provider/model` หลายรายการ หากรายการแรกล้มเหลว ให้ถอยกลับไปยังรายการถัดไปโดยอัตโนมัติ
|
||||
|
||||
### 5.6 การแปลสตรีมมิ่งแบบ stateful
|
||||
|
||||
การแปลการตอบสนองจะรักษาสถานะทั่วทั้งกลุ่ม SSE (การติดตามบล็อกความคิด การสะสมการเรียกเครื่องมือ การทำดัชนีบล็อกเนื้อหา) ผ่านกลไก `initState()`
|
||||
|
||||
### 5.7 บัฟเฟอร์ความปลอดภัยในการใช้งาน
|
||||
|
||||
มีการเพิ่มบัฟเฟอร์ 2,000 โทเค็นในการใช้งานที่รายงาน เพื่อป้องกันไม่ให้ไคลเอ็นต์เข้าถึงขีดจำกัดหน้าต่างบริบท เนื่องจากโอเวอร์เฮดจากการแจ้งเตือนของระบบและการแปลรูปแบบ
|
||||
|
||||
---
|
||||
|
||||
## 6. รูปแบบที่รองรับ
|
||||
|
||||
| รูปแบบ | ทิศทาง | ตัวระบุ |
|
||||
| ------------------------ | --------------------- | ------------------ |
|
||||
| การแชท OpenAI เสร็จสิ้น | แหล่งที่มา + เป้าหมาย | `openai` |
|
||||
| API การตอบสนองของ OpenAI | แหล่งที่มา + เป้าหมาย | `openai-responses` |
|
||||
| มานุษยวิทยาคลอด | แหล่งที่มา + เป้าหมาย | `claude` |
|
||||
| Google ราศีเมถุน | แหล่งที่มา + เป้าหมาย | `gemini` |
|
||||
| Google ราศีเมถุน CLI | กำหนดเป้าหมายเท่านั้น | `gemini-cli` |
|
||||
| ต้านแรงโน้มถ่วง | แหล่งที่มา + เป้าหมาย | `antigravity` |
|
||||
| AWS Kiro | กำหนดเป้าหมายเท่านั้น | `kiro` |
|
||||
| เคอร์เซอร์ | กำหนดเป้าหมายเท่านั้น | `cursor` |
|
||||
|
||||
---
|
||||
|
||||
## 7. ผู้ให้บริการที่รองรับ
|
||||
|
||||
| ผู้ให้บริการ | วิธีการรับรองความถูกต้อง | ผู้ดำเนินการ | หมายเหตุสำคัญ |
|
||||
| ------------------------ | ------------------------ | --------------- | ---------------------------------------------------- |
|
||||
| มานุษยวิทยาคลอด | คีย์ API หรือ OAuth | ค่าเริ่มต้น | ใช้ `x-api-key` ส่วนหัว |
|
||||
| Google ราศีเมถุน | คีย์ API หรือ OAuth | ค่าเริ่มต้น | ใช้ `x-goog-api-key` ส่วนหัว |
|
||||
| Google ราศีเมถุน CLI | OAuth | GeminiCLI | ใช้ปลายทาง `streamGenerateContent` |
|
||||
| ต้านแรงโน้มถ่วง | OAuth | ต้านแรงโน้มถ่วง | ทางเลือกหลาย URL, ลองแยกวิเคราะห์อีกครั้งแบบกำหนดเอง |
|
||||
| OpenAI | คีย์ API | ค่าเริ่มต้น | ผู้ถือมาตรฐานรับรองความถูกต้อง |
|
||||
| โคเด็กซ์ | OAuth | โคเด็กซ์ | อัดคำสั่งระบบ จัดการการคิด |
|
||||
| นักบิน GitHub | OAuth + โทเค็น Copilot | Github | โทเค็นคู่, ส่วนหัว VSCode เลียนแบบ |
|
||||
| คิโระ (AWS) | AWS SSO OIDC หรือโซเชียล | คิโระ | การแยกวิเคราะห์ EventStream ไบนารี |
|
||||
| เคอร์เซอร์ IDE | การตรวจสอบความถูกต้อง | เคอร์เซอร์ | การเข้ารหัส Protobuf, เช็คซัม SHA-256 |
|
||||
| ควีน | OAuth | ค่าเริ่มต้น | การรับรองมาตรฐาน |
|
||||
| ไอโฟลว์ | OAuth (พื้นฐาน + ผู้ถือ) | ค่าเริ่มต้น | ส่วนหัวการรับรองความถูกต้องแบบคู่ |
|
||||
| OpenRouter | คีย์ API | ค่าเริ่มต้น | ผู้ถือมาตรฐานรับรองความถูกต้อง |
|
||||
| GLM, Kimi, MiniMax | คีย์ API | ค่าเริ่มต้น | เข้ากันได้กับ Claude ใช้ `x-api-key` |
|
||||
| `openai-compatible-*` | คีย์ API | ค่าเริ่มต้น | ไดนามิก: จุดสิ้นสุดที่เข้ากันได้กับ OpenAI |
|
||||
| `anthropic-compatible-*` | คีย์ API | ค่าเริ่มต้น | ไดนามิก: จุดสิ้นสุดที่เข้ากันได้กับ Claude |
|
||||
|
||||
---
|
||||
|
||||
## 8. สรุปการไหลของข้อมูล
|
||||
|
||||
### คำขอสตรีมมิ่ง
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### คำขอที่ไม่ใช่สตรีมมิ่ง
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### บายพาสโฟลว์ (Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
77
docs/i18n/th/FEATURES.md
Normal file
77
docs/i18n/th/FEATURES.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OmniRoute - แกลเลอรีคุณลักษณะแดชบอร์ด
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../FEATURES.md) | 🇧🇷 [Português (Brasil)](../pt-BR/FEATURES.md) | 🇪🇸 [Español](../es/FEATURES.md) | 🇫🇷 [Français](../fr/FEATURES.md) | 🇮🇹 [Italiano](../it/FEATURES.md) | 🇷🇺 [Русский](../ru/FEATURES.md) | 🇨🇳 [中文 (简体)](../zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](../de/FEATURES.md) | 🇮🇳 [हिन्दी](../in/FEATURES.md) | 🇹🇭 [ไทย](../th/FEATURES.md) | 🇺🇦 [Українська](../uk-UA/FEATURES.md) | 🇸🇦 [العربية](../ar/FEATURES.md) | 🇯🇵 [日本語](../ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](../vi/FEATURES.md) | 🇧🇬 [Български](../bg/FEATURES.md) | 🇩🇰 [Dansk](../da/FEATURES.md) | 🇫🇮 [Suomi](../fi/FEATURES.md) | 🇮🇱 [עברית](../he/FEATURES.md) | 🇭🇺 [Magyar](../hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](../id/FEATURES.md) | 🇰🇷 [한국어](../ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](../ms/FEATURES.md) | 🇳🇱 [Nederlands](../nl/FEATURES.md) | 🇳🇴 [Norsk](../no/FEATURES.md) | 🇵🇹 [Português (Portugal)](../pt/FEATURES.md) | 🇷🇴 [Română](../ro/FEATURES.md) | 🇵🇱 [Polski](../pl/FEATURES.md) | 🇸🇰 [Slovenčina](../sk/FEATURES.md) | 🇸🇪 [Svenska](../sv/FEATURES.md) | 🇵🇭 [Filipino](../phi/FEATURES.md)
|
||||
|
||||
ภาพแนะนำทุกส่วนของแดชบอร์ด OmniRoute
|
||||
|
||||
---
|
||||
|
||||
## 🔌 ผู้ให้บริการ
|
||||
|
||||
จัดการการเชื่อมต่อผู้ให้บริการ AI: ผู้ให้บริการ OAuth (Claude Code, Codex, Gemini CLI), ผู้ให้บริการคีย์ API (Groq, DeepSeek, OpenRouter) และผู้ให้บริการฟรี (iFlow, Qwen, Kiro)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 คอมโบ
|
||||
|
||||
สร้างคอมโบการกำหนดเส้นทางแบบจำลองด้วย 6 กลยุทธ์: เติมก่อน ปัดเศษ ยกกำลังสองตัวเลือก สุ่ม ใช้น้อยที่สุด และปรับต้นทุนให้เหมาะสม แต่ละคอมโบเชื่อมโยงหลายรุ่นพร้อมทางเลือกอัตโนมัติ
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 การวิเคราะห์
|
||||
|
||||
การวิเคราะห์การใช้งานที่ครอบคลุมด้วยการใช้โทเค็น การประมาณการต้นทุน แผนที่ความร้อนของกิจกรรม แผนภูมิการกระจายรายสัปดาห์ และรายละเอียดต่อผู้ให้บริการ
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 สุขภาพของระบบ
|
||||
|
||||
การตรวจสอบแบบเรียลไทม์: เวลาทำงาน หน่วยความจำ เวอร์ชัน เปอร์เซ็นต์ไทล์แฝง (p50/p95/p99) สถิติแคช และสถานะเซอร์กิตเบรกเกอร์ของผู้ให้บริการ
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ???? สนามเด็กเล่นนักแปล
|
||||
|
||||
สี่โหมดสำหรับการดีบักการแปล API: **Playground** (ตัวแปลงรูปแบบ), **Chat Tester** (คำขอสด), **Test Bench** (การทดสอบเป็นกลุ่ม) และ **Live Monitor** (สตรีมแบบเรียลไทม์)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⚙️ การตั้งค่า
|
||||
|
||||
การตั้งค่าทั่วไป ที่เก็บข้อมูลระบบ การจัดการการสำรองข้อมูล (ฐานข้อมูลส่งออก/นำเข้า) ลักษณะที่ปรากฏ (โหมดมืด/สว่าง) ความปลอดภัย (รวมถึงการป้องกันจุดสิ้นสุด API และการบล็อกผู้ให้บริการแบบกำหนดเอง) การกำหนดเส้นทาง ความยืดหยุ่น และการกำหนดค่าขั้นสูง
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🛠 เครื่องมือ CLI
|
||||
|
||||
การกำหนดค่าเพียงคลิกเดียวสำหรับเครื่องมือเข้ารหัส AI: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code และ Antigravity
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⏩ ขอบันทึก
|
||||
|
||||
การบันทึกคำขอแบบเรียลไทม์พร้อมการกรองตามผู้ให้บริการ โมเดล บัญชี และคีย์ API แสดงรหัสสถานะ การใช้โทเค็น เวลาแฝง และรายละเอียดการตอบกลับ
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 จุดสิ้นสุด API
|
||||
|
||||
ตำแหน่งข้อมูล API แบบรวมของคุณพร้อมรายละเอียดความสามารถ: การแชทให้เสร็จสิ้น การฝัง การสร้างรูปภาพ การจัดอันดับใหม่ การถอดเสียง และคีย์ API ที่ลงทะเบียน
|
||||
|
||||

|
||||
219
docs/i18n/th/TROUBLESHOOTING.md
Normal file
219
docs/i18n/th/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# การแก้ไขปัญหา
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](../pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](../es/TROUBLESHOOTING.md) | 🇫🇷 [Français](../fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](../it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](../ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](../zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](../de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](../in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](../th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](../uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](../ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](../ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](../vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](../bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](../da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](../fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](../he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](../hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](../id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](../ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](../ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](../nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](../no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](../pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](../ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](../pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](../sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](../sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](../phi/TROUBLESHOOTING.md)
|
||||
|
||||
ปัญหาและวิธีแก้ปัญหาทั่วไปสำหรับ OmniRoute
|
||||
|
||||
---
|
||||
|
||||
## แก้ไขด่วน
|
||||
|
||||
| ปัญหา | โซลูชั่น |
|
||||
| ------------------------------- | ---------------------------------------------------------------------- |
|
||||
| การเข้าสู่ระบบครั้งแรกไม่ทำงาน | ทำเครื่องหมาย `INITIAL_PASSWORD` ใน `.env` (ค่าเริ่มต้น: `123456`) |
|
||||
| แดชบอร์ดเปิดบนพอร์ตผิด | ตั้งค่า `PORT=20128` และ `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| ไม่มีบันทึกคำขอภายใต้ `logs/` | ตั้งค่า `ENABLE_REQUEST_LOGS=true` |
|
||||
| EACCES: การอนุญาตถูกปฏิเสธ | ตั้งค่า `DATA_DIR=/path/to/writable/dir` เพื่อแทนที่ `~/.omniroute` |
|
||||
| กลยุทธ์การกำหนดเส้นทางไม่บันทึก | อัปเดตเป็น v1.4.11+ (แก้ไข Zod schema สำหรับการคงอยู่ของการตั้งค่า) |
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาของผู้ให้บริการ
|
||||
|
||||
### "โมเดลภาษาไม่ได้ให้ข้อความ"
|
||||
|
||||
**สาเหตุ:** โควต้าของผู้ให้บริการหมดลง
|
||||
|
||||
**แก้ไข:**
|
||||
|
||||
1. ตรวจสอบตัวติดตามโควต้าแดชบอร์ด
|
||||
2. ใช้คอมโบที่มีระดับทางเลือก
|
||||
3. เปลี่ยนไปใช้ระดับที่ถูกกว่า/ฟรี
|
||||
|
||||
### การจำกัดอัตรา
|
||||
|
||||
**สาเหตุ:** โควต้าการสมัครใช้งานหมดลง
|
||||
|
||||
**แก้ไข:**
|
||||
|
||||
- เพิ่มทางเลือก: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- ใช้ GLM/MiniMax เป็นข้อมูลสำรองราคาถูก
|
||||
|
||||
### โทเค็น OAuth หมดอายุแล้ว
|
||||
|
||||
OmniRoute รีเฟรชโทเค็นอัตโนมัติ หากปัญหายังคงอยู่:
|
||||
|
||||
1. แดชบอร์ด → ผู้ให้บริการ → เชื่อมต่อใหม่
|
||||
2. ลบและเพิ่มการเชื่อมต่อผู้ให้บริการอีกครั้ง
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาคลาวด์
|
||||
|
||||
### ข้อผิดพลาดการซิงค์คลาวด์
|
||||
|
||||
1. ตรวจสอบ `BASE_URL` ชี้ไปยังอินสแตนซ์ที่ทำงานอยู่ของคุณ (เช่น `http://localhost:20128`)
|
||||
2. ตรวจสอบ `CLOUD_URL` ชี้ไปยังจุดสิ้นสุดระบบคลาวด์ของคุณ (เช่น `https://omniroute.dev`)
|
||||
3. เก็บค่า `NEXT_PUBLIC_*` ให้สอดคล้องกับค่าฝั่งเซิร์ฟเวอร์
|
||||
|
||||
### คลาวด์ `stream=false` ส่งคืน 500
|
||||
|
||||
**อาการ:** `Unexpected token 'd'...` บนจุดปลายทางคลาวด์สำหรับการโทรที่ไม่ใช่การสตรีม
|
||||
|
||||
**สาเหตุ:** อัปสตรีมส่งคืนเพย์โหลด SSE ในขณะที่ไคลเอ็นต์คาดหวัง JSON
|
||||
|
||||
**วิธีแก้ปัญหา:** ใช้ `stream=true` สำหรับการโทรโดยตรงบนคลาวด์ รันไทม์ในเครื่องรวมถึงทางเลือก SSE → JSON
|
||||
|
||||
### Cloud บอกว่าเชื่อมต่อแล้ว แต่ "คีย์ API ไม่ถูกต้อง"
|
||||
|
||||
1. สร้างคีย์ใหม่จากแดชบอร์ดในเครื่อง (`/api/keys`)
|
||||
2. เรียกใช้คลาวด์ซิงค์: เปิดใช้งานคลาวด์ → ซิงค์ทันที
|
||||
3. คีย์เก่า/ที่ไม่ได้ซิงค์ยังสามารถส่งคืน `401` บนคลาวด์ได้
|
||||
|
||||
---
|
||||
|
||||
## ปัญหานักเทียบท่า
|
||||
|
||||
### เครื่องมือ CLI แสดงว่าไม่ได้ติดตั้ง
|
||||
|
||||
1. ตรวจสอบฟิลด์รันไทม์: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. สำหรับโหมดพกพา: ใช้เป้าหมายรูปภาพ `runner-cli` (CLI ที่รวมกลุ่ม)
|
||||
3. สำหรับโหมดเมาต์โฮสต์: ตั้งค่า `CLI_EXTRA_PATHS` และเมาต์ไดเร็กทอรี bin โฮสต์เป็นแบบอ่านอย่างเดียว
|
||||
4. หาก `installed=true` และ `runnable=false`: พบไบนารีแต่ตรวจสุขภาพไม่สำเร็จ
|
||||
|
||||
### การตรวจสอบรันไทม์ด่วน
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาต้นทุน
|
||||
|
||||
### ต้นทุนสูง
|
||||
|
||||
1. ตรวจสอบสถิติการใช้งานในแดชบอร์ด → การใช้งาน
|
||||
2. สลับโมเดลหลักเป็น GLM/MiniMax
|
||||
3. ใช้ Free Tier (Gemini CLI, iFlow) สำหรับงานที่ไม่สำคัญ
|
||||
4. กำหนดงบประมาณต้นทุนต่อคีย์ API: แดชบอร์ด → คีย์ API → งบประมาณ
|
||||
|
||||
---
|
||||
|
||||
## การดีบัก
|
||||
|
||||
### เปิดใช้งานบันทึกคำขอ
|
||||
|
||||
ตั้งค่า `ENABLE_REQUEST_LOGS=true` ในไฟล์ `.env` ของคุณ บันทึกจะปรากฏภายใต้ไดเรกทอรี `logs/`
|
||||
|
||||
### ตรวจสอบสุขภาพของผู้ให้บริการ
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### พื้นที่เก็บข้อมูลรันไทม์
|
||||
|
||||
- สถานะหลัก: `${DATA_DIR}/db.json` (ผู้ให้บริการ คอมโบ นามแฝง คีย์ การตั้งค่า)
|
||||
- การใช้งาน: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- บันทึกคำขอ: `<repo>/logs/...` (เมื่อ `ENABLE_REQUEST_LOGS=true`)
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาเซอร์กิตเบรกเกอร์
|
||||
|
||||
### ผู้ให้บริการติดอยู่ในสถานะเปิด
|
||||
|
||||
เมื่อเซอร์กิตเบรกเกอร์ของผู้ให้บริการเปิดอยู่ คำขอจะถูกบล็อกจนกว่าคูลดาวน์จะหมดลง
|
||||
|
||||
**แก้ไข:**
|
||||
|
||||
1. ไปที่ **แดชบอร์ด → การตั้งค่า → ความยืดหยุ่น**
|
||||
2. ตรวจสอบการ์ดเซอร์กิตเบรกเกอร์สำหรับผู้ให้บริการที่ได้รับผลกระทบ
|
||||
3. คลิก **รีเซ็ตทั้งหมด** เพื่อล้างเบรกเกอร์ทั้งหมด หรือรอให้คูลดาวน์หมดลง
|
||||
4. ตรวจสอบว่าผู้ให้บริการพร้อมใช้งานจริงก่อนที่จะรีเซ็ต
|
||||
|
||||
### ผู้ให้บริการสะดุดเบรกเกอร์อย่างต่อเนื่อง
|
||||
|
||||
หากผู้ให้บริการเข้าสู่สถานะเปิดซ้ำๆ:
|
||||
|
||||
1. ตรวจสอบ **แดชบอร์ด → สุขภาพ → สุขภาพของผู้ให้บริการ** เพื่อดูรูปแบบความล้มเหลว
|
||||
2. ไปที่ **การตั้งค่า → ความยืดหยุ่น → โปรไฟล์ผู้ให้บริการ** และเพิ่มเกณฑ์ความล้มเหลว
|
||||
3. ตรวจสอบว่าผู้ให้บริการได้เปลี่ยนแปลงขีดจำกัด API หรือต้องมีการตรวจสอบสิทธิ์อีกครั้งหรือไม่
|
||||
4. ตรวจสอบการวัดและส่งข้อมูลทางไกลเวลาแฝง — เวลาแฝงสูงอาจทำให้เกิดความล้มเหลวตามการหมดเวลา
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาการถอดเสียง
|
||||
|
||||
### ข้อผิดพลาด "รุ่นที่ไม่รองรับ"
|
||||
|
||||
- ตรวจสอบให้แน่ใจว่าคุณใช้คำนำหน้าที่ถูกต้อง: `deepgram/nova-3` หรือ `assemblyai/best`
|
||||
- ตรวจสอบว่าผู้ให้บริการเชื่อมต่ออยู่ใน **Dashboard → Providers**
|
||||
|
||||
### การถอดเสียงกลับว่างเปล่าหรือล้มเหลว
|
||||
|
||||
- ตรวจสอบรูปแบบเสียงที่รองรับ: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
- ตรวจสอบขนาดไฟล์อยู่ภายในขีดจำกัดของผู้ให้บริการ (โดยทั่วไปคือ <25MB)
|
||||
- ตรวจสอบความถูกต้องของคีย์ API ของผู้ให้บริการในการ์ดผู้ให้บริการ
|
||||
|
||||
---
|
||||
|
||||
## การแก้ไขจุดบกพร่องของนักแปล
|
||||
|
||||
ใช้ **แดชบอร์ด → ตัวแปล** เพื่อแก้ไขปัญหาการแปลรูปแบบ:
|
||||
|
||||
| โหมด | เมื่อใดควรใช้ |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ | ------- |
|
||||
| **สนามเด็กเล่น** | เปรียบเทียบรูปแบบอินพุต/เอาต์พุตแบบเคียงข้างกัน — วางคำขอที่ล้มเหลวเพื่อดูว่าคำขอแปล | อย่างไร |
|
||||
| **เครื่องมือทดสอบแชท** | ส่งข้อความสดและตรวจสอบเพย์โหลดคำขอ/การตอบกลับทั้งหมด รวมถึงส่วนหัว |
|
||||
| **ม้านั่งทดสอบ** | เรียกใช้การทดสอบเป็นชุดระหว่างรูปแบบต่างๆ เพื่อดูว่าคำแปลใดเสียหาย |
|
||||
| **ถ่ายทอดสด** | ดูขั้นตอนคำขอแบบเรียลไทม์เพื่อตรวจจับปัญหาการแปลเป็นระยะๆ |
|
||||
|
||||
### ปัญหารูปแบบทั่วไป
|
||||
|
||||
- **แท็กการคิดไม่ปรากฏ** — ตรวจสอบว่าผู้ให้บริการเป้าหมายสนับสนุนการคิดและการตั้งค่างบประมาณการคิดหรือไม่
|
||||
- **การเรียกเครื่องมือลดลง** — การแปลรูปแบบบางรูปแบบอาจตัดช่องที่ไม่รองรับออก ตรวจสอบในโหมดสนามเด็กเล่น
|
||||
- **การแจ้งเตือนของระบบหายไป** — ระบบแจ้งของ Claude และ Gemini แตกต่างกัน ตรวจสอบผลลัพธ์การแปล
|
||||
- **SDK ส่งคืนสตริงดิบแทนที่จะเป็นวัตถุ** — แก้ไขในเวอร์ชัน 1.1.0: ตอนนี้ตัวล้างการตอบสนองจะตัดฟิลด์ที่ไม่ได้มาตรฐาน (`x_groq`, `usage_breakdown` ฯลฯ) ที่ทำให้การตรวจสอบ OpenAI SDK Pydantic ล้มเหลว
|
||||
- **GLM/ERNIE ปฏิเสธบทบาท `system`** — แก้ไขในเวอร์ชัน 1.1.0: บทบาท Normalizer จะรวมข้อความระบบเข้ากับข้อความผู้ใช้โดยอัตโนมัติสำหรับรุ่นที่เข้ากันไม่ได้
|
||||
- **`developer` ไม่รู้จักบทบาท** — แก้ไขใน v1.1.0: แปลงเป็น `system` โดยอัตโนมัติสำหรับผู้ให้บริการที่ไม่ใช่ OpenAI
|
||||
- **`json_schema` ไม่ทำงานกับ Gemini** — แก้ไขใน v1.1.0: `response_format` ตอนนี้ถูกแปลงเป็น `responseMimeType` + `responseSchema` ของ Gemini แล้ว
|
||||
|
||||
---
|
||||
|
||||
## การตั้งค่าความยืดหยุ่น
|
||||
|
||||
### ขีดจำกัดอัตราอัตโนมัติไม่ทริกเกอร์
|
||||
|
||||
- การจำกัดอัตราอัตโนมัติใช้กับผู้ให้บริการคีย์ API เท่านั้น (ไม่ใช่ OAuth/การสมัครสมาชิก)
|
||||
- ตรวจสอบ **การตั้งค่า → ความยืดหยุ่น → โปรไฟล์ผู้ให้บริการ** ได้เปิดใช้งานการจำกัดอัตราอัตโนมัติแล้ว
|
||||
- ตรวจสอบว่าผู้ให้บริการส่งคืนรหัสสถานะ `429` หรือส่วนหัว `Retry-After` หรือไม่
|
||||
|
||||
### การปรับแต่งการถอยกลับแบบเอ็กซ์โปเนนเชียล
|
||||
|
||||
โปรไฟล์ผู้ให้บริการรองรับการตั้งค่าเหล่านี้:
|
||||
|
||||
- **ความล่าช้าพื้นฐาน** — เวลารอเริ่มต้นหลังจากความล้มเหลวครั้งแรก (ค่าเริ่มต้น: 1 วินาที)
|
||||
- **ความล่าช้าสูงสุด** — ขีดจำกัดเวลารอสูงสุด (ค่าเริ่มต้น: 30 วินาที)
|
||||
- **ตัวคูณ** — จะต้องเพิ่มความล่าช้าเท่าใดต่อความล้มเหลวติดต่อกัน (ค่าเริ่มต้น: 2x)
|
||||
|
||||
### ฝูงต่อต้านฟ้าร้อง
|
||||
|
||||
เมื่อคำขอหลายรายการส่งถึงผู้ให้บริการที่จำกัดอัตรา OmniRoute จะใช้ mutex + การจำกัดอัตราอัตโนมัติเพื่อซีเรียลไลซ์คำขอและป้องกันความล้มเหลวแบบเรียงซ้อน นี่เป็นแบบอัตโนมัติสำหรับผู้ให้บริการคีย์ API
|
||||
|
||||
---
|
||||
|
||||
## ยังติดอยู่เหรอ?
|
||||
|
||||
- **ปัญหา GitHub**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **สถาปัตยกรรม**: ดู [**OMNI_TOKEN_55**](ARCHITECTURE.md) สำหรับรายละเอียดภายใน
|
||||
- **การอ้างอิง API**: ดู [**OMNI_TOKEN_56**](API_REFERENCE.md) สำหรับจุดสิ้นสุดทั้งหมด
|
||||
- **แดชบอร์ดสุขภาพ**: ตรวจสอบ **แดชบอร์ด → สุขภาพ** เพื่อดูสถานะของระบบแบบเรียลไทม์
|
||||
- **นักแปล**: ใช้ **แดชบอร์ด → นักแปล** เพื่อแก้ไขปัญหาเกี่ยวกับรูปแบบ
|
||||
698
docs/i18n/th/USER_GUIDE.md
Normal file
698
docs/i18n/th/USER_GUIDE.md
Normal file
@@ -0,0 +1,698 @@
|
||||
# คู่มือการใช้งาน
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](../es/USER_GUIDE.md) | 🇫🇷 [Français](../fr/USER_GUIDE.md) | 🇮🇹 [Italiano](../it/USER_GUIDE.md) | 🇷🇺 [Русский](../ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](../de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](../in/USER_GUIDE.md) | 🇹🇭 [ไทย](../th/USER_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](../ar/USER_GUIDE.md) | 🇯🇵 [日本語](../ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/USER_GUIDE.md) | 🇧🇬 [Български](../bg/USER_GUIDE.md) | 🇩🇰 [Dansk](../da/USER_GUIDE.md) | 🇫🇮 [Suomi](../fi/USER_GUIDE.md) | 🇮🇱 [עברית](../he/USER_GUIDE.md) | 🇭🇺 [Magyar](../hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/USER_GUIDE.md) | 🇰🇷 [한국어](../ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](../nl/USER_GUIDE.md) | 🇳🇴 [Norsk](../no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/USER_GUIDE.md) | 🇷🇴 [Română](../ro/USER_GUIDE.md) | 🇵🇱 [Polski](../pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/USER_GUIDE.md) | 🇸🇪 [Svenska](../sv/USER_GUIDE.md) | 🇵🇭 [Filipino](../phi/USER_GUIDE.md)
|
||||
|
||||
คู่มือฉบับสมบูรณ์สำหรับการกำหนดค่าผู้ให้บริการ การสร้างคอมโบ การผสานรวมเครื่องมือ CLI และการปรับใช้ OmniRoute
|
||||
|
||||
---
|
||||
|
||||
## สารบัญ
|
||||
|
||||
- [Pricing at a Glance](#-pricing-at-a-glance)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Provider Setup](#-provider-setup)
|
||||
- [CLI Integration](#-cli-integration)
|
||||
- [Deployment](#-deployment)
|
||||
- [Available Models](#-available-models)
|
||||
- [Advanced Features](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 ราคาโดยสรุป
|
||||
|
||||
| ชั้น | ผู้ให้บริการ | ราคา | รีเซ็ตโควต้า | ดีที่สุดสำหรับ |
|
||||
| ------------------ | ---------------- | ---------------- | ------------------- | ---------------------------- |
|
||||
| **💳 สมัครสมาชิก** | รหัสคลอดด์ (Pro) | $20/เดือน | 5 ชม. + รายสัปดาห์ | สมัครสมาชิกแล้ว |
|
||||
| | Codex (พลัส/โปร) | $20-200/เดือน | 5 ชม. + รายสัปดาห์ | ผู้ใช้ OpenAI |
|
||||
| | ราศีเมถุน CLI | **ฟรี** | 180K/เดือน + 1K/วัน | ทุกคน! |
|
||||
| | นักบิน GitHub | $10-19/เดือน | รายเดือน | ผู้ใช้ GitHub |
|
||||
| **🔑 คีย์ API** | DeepSeek | จ่ายตามการใช้งาน | ไม่มี | การใช้เหตุผลราคาถูก |
|
||||
| | กรอค | จ่ายตามการใช้งาน | ไม่มี | การอนุมานที่รวดเร็วเป็นพิเศษ |
|
||||
| | xAI (โกรก) | จ่ายตามการใช้งาน | ไม่มี | Grok 4 การใช้เหตุผล |
|
||||
| | มิสทรัล | จ่ายตามการใช้งาน | ไม่มี | โมเดลที่โฮสต์โดยสหภาพยุโรป |
|
||||
| | ความฉงนสนเท่ห์ | จ่ายตามการใช้งาน | ไม่มี | การค้นหาเสริม |
|
||||
| | ร่วมกัน AI | จ่ายตามการใช้งาน | ไม่มี | โมเดลโอเพ่นซอร์ส |
|
||||
| | ดอกไม้ไฟ AI | จ่ายตามการใช้งาน | ไม่มี | ภาพ FLUX ที่รวดเร็ว |
|
||||
| | สมอง | จ่ายตามการใช้งาน | ไม่มี | ความเร็วระดับเวเฟอร์ |
|
||||
| | เชื่อมโยง | จ่ายตามการใช้งาน | ไม่มี | คำสั่ง R+ RAG |
|
||||
| | NVIDIA NIM | จ่ายตามการใช้งาน | ไม่มี | โมเดลองค์กร |
|
||||
| **💰 ราคาถูก** | GLM-4.7 | $0.6/1M | ทุกวัน 10.00 น. | สำรองงบประมาณ |
|
||||
| | MiniMax M2.1 | $0.2/1M | กลิ้ง 5 ชั่วโมง | ตัวเลือกที่ถูกที่สุด |
|
||||
| | คิมิ K2 | $9/เดือน คงที่ | 10M โทเค็น/เดือน | ต้นทุนที่คาดการณ์ได้ |
|
||||
| **🆓 ฟรี** | ไอโฟลว์ | $0 | ไม่จำกัด | ฟรี 8 รุ่น |
|
||||
| | ควีน | $0 | ไม่จำกัด | ฟรี 3 รุ่น |
|
||||
| | คิโระ | $0 | ไม่จำกัด | คลอดด์ฟรี |
|
||||
|
||||
**💡 เคล็ดลับสำหรับมืออาชีพ:** เริ่มต้นด้วย Gemini CLI (ฟรี 180,000 ต่อเดือน) + iFlow (ฟรีไม่จำกัด) คอมโบ = ค่าใช้จ่าย $0!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 กรณีการใช้งาน
|
||||
|
||||
### กรณีที่ 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-coder-plus (unlimited free)
|
||||
|
||||
Monthly cost: $0
|
||||
Quality: Production-ready models
|
||||
```
|
||||
|
||||
### กรณีที่ 3: "ฉันต้องการการเข้ารหัสตลอด 24 ชั่วโมงทุกวัน ไม่มีการหยุดชะงัก"
|
||||
|
||||
**ปัญหา:** กำหนดเวลา ไม่สามารถหยุดการทำงานได้
|
||||
|
||||
```
|
||||
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. minimax/MiniMax-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: "ฉันต้องการ AI ฟรีใน OpenClaw"
|
||||
|
||||
**ปัญหา:** ต้องการผู้ช่วย 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...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 การตั้งค่าผู้ให้บริการ
|
||||
|
||||
### 🔐 ผู้ให้บริการสมัครสมาชิก
|
||||
|
||||
#### รหัสคลอด (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 เพื่อความรวดเร็ว โควต้าการติดตาม OmniRoute ต่อรุ่น!
|
||||
|
||||
#### OpenAI Codex (พลัส/โปร)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/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
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ 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. รับคีย์ API จาก Coding Plan
|
||||
3. แดชบอร์ด → เพิ่มคีย์ API: ผู้ให้บริการ: `glm`, คีย์ API: `your-key`
|
||||
|
||||
**ใช้:** `glm/glm-4.7` — **เคล็ดลับสำหรับมืออาชีพ:** แผนการเขียนโค้ดเสนอโควต้า 3× ในราคา 1/7! รีเซ็ตทุกวัน 10.00 น.
|
||||
|
||||
#### MiniMax M2.1 (รีเซ็ต 5 ชม., $0.20/1M)
|
||||
|
||||
1. ลงทะเบียน: [MiniMax](https://www.minimax.io/)
|
||||
2. รับคีย์ API → แดชบอร์ด → เพิ่มคีย์ API
|
||||
|
||||
**ใช้:** `minimax/MiniMax-M2.1` — **เคล็ดลับสำหรับมือโปร:** ตัวเลือกที่ถูกที่สุดสำหรับบริบทแบบยาว (โทเค็น 1M)!
|
||||
|
||||
#### Kimi K2 ($9/เดือน)
|
||||
|
||||
1. สมัครสมาชิก: [Moonshot AI](https://platform.moonshot.ai/)
|
||||
2. รับคีย์ API → แดชบอร์ด → เพิ่มคีย์ API
|
||||
|
||||
**ใช้:** `kimi/kimi-latest` — **เคล็ดลับสำหรับมืออาชีพ:** แก้ไข $9/เดือนสำหรับโทเค็น 10M = $0.90/ต้นทุนจริง 1M!
|
||||
|
||||
### 🆓 ผู้ให้บริการฟรี
|
||||
|
||||
#### iFlow (ฟรี 8 รุ่น)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect 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 auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### คิโระ (โคลด ฟรี)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
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/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### ตัวอย่างที่ 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
|
||||
|
||||
### เคอร์เซอร์ IDE
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### รหัสคลอด
|
||||
|
||||
แก้ไข `~/.claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### โอเพ่นคลอว์
|
||||
|
||||
แก้ไข `~/.openclaw/openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**หรือใช้แดชบอร์ด:** เครื่องมือ CLI → OpenClaw → กำหนดค่าอัตโนมัติ
|
||||
|
||||
### ไคลน์ / ดำเนินการต่อ / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 การปรับใช้
|
||||
|
||||
### การปรับใช้ VPS
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### นักเทียบท่า
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
สำหรับโหมดรวมโฮสต์ที่มีไบนารี CLI โปรดดูส่วนนักเทียบท่าในเอกสารหลัก
|
||||
|
||||
### ตัวแปรสภาพแวดล้อม
|
||||
|
||||
| ตัวแปร | ค่าเริ่มต้น | คำอธิบาย |
|
||||
| --------------------- | ------------------------------------ | ------------------------------------------------------------------- |
|
||||
| `JWT_SECRET` | `omniroute-default-secret-change-me` | เคล็ดลับการลงนาม JWT (**การเปลี่ยนแปลงในการผลิต**) |
|
||||
| `INITIAL_PASSWORD` | `123456` | รหัสผ่านเข้าสู่ระบบครั้งแรก |
|
||||
| `DATA_DIR` | `~/.omniroute` | ไดเร็กทอรีข้อมูล (db, การใช้งาน, บันทึก) |
|
||||
| `PORT` | ค่าเริ่มต้นของเฟรมเวิร์ก | พอร์ตบริการ (`20128` ในตัวอย่าง) |
|
||||
| `HOSTNAME` | ค่าเริ่มต้นของเฟรมเวิร์ก | ผูกโฮสต์ (ค่าเริ่มต้นของ Docker คือ `0.0.0.0`) |
|
||||
| `NODE_ENV` | รันไทม์เริ่มต้น | ตั้งค่า `production` สำหรับการปรับใช้ |
|
||||
| `BASE_URL` | `http://localhost:20128` | URL ฐานภายในฝั่งเซิร์ฟเวอร์ |
|
||||
| `CLOUD_URL` | `https://omniroute.dev` | URL ฐานปลายทางการซิงค์บนคลาวด์ |
|
||||
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | ข้อมูลลับ HMAC สำหรับคีย์ API ที่สร้างขึ้น |
|
||||
| `REQUIRE_API_KEY` | `false` | บังคับใช้คีย์ Bearer API บน `/v1/*` |
|
||||
| `ENABLE_REQUEST_LOGS` | `false` | เปิดใช้งานบันทึกคำขอ/การตอบกลับ |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | บังคับ `Secure` คุกกี้รับรองความถูกต้อง (หลังพร็อกซีย้อนกลับ HTTPS) |
|
||||
|
||||
สำหรับการอ้างอิงตัวแปรสภาพแวดล้อมแบบเต็ม โปรดดูที่ [README](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## 📊 รุ่นที่มีจำหน่าย
|
||||
|
||||
<details>
|
||||
<summary><b>ดูรุ่นที่มีทั้งหมด</b>OMNI_TOKEN_161__
|
||||
|
||||
**รหัสโคลด (`cc/`)** — โปร/สูงสุด: `cc/claude-opus-4-6`, `cc/claude-sonnet-4-5-20250929`, `cc/claude-haiku-4-5-20251001`
|
||||
|
||||
**โคเด็กซ์ (`cx/`)** — บวก/โปร: `cx/gpt-5.2-codex`, `cx/gpt-5.1-codex-max`
|
||||
|
||||
**ราศีเมถุน CLI (`gc/`)** — ฟรี: `gc/gemini-3-flash-preview`, `gc/gemini-2.5-pro`
|
||||
|
||||
**โปรแกรมควบคุม GitHub (`gh/`)**: `gh/gpt-5`, `gh/claude-4.5-sonnet`
|
||||
|
||||
**GLM (`glm/`)** — $0.6/1M: `glm/glm-4.7`
|
||||
|
||||
**MiniMax (`minimax/`)** — $0.2/1M: `minimax/MiniMax-M2.1`
|
||||
|
||||
**iFlow (`if/`)** — ฟรี: `if/kimi-k2-thinking`, `if/qwen3-coder-plus`, `if/deepseek-r1`
|
||||
|
||||
**คิวเวน (`qw/`)** — ฟรี: `qw/qwen3-coder-plus`, `qw/qwen3-coder-flash`
|
||||
|
||||
**คิโระ (`kr/`)** — ฟรี: `kr/claude-sonnet-4.5`, `kr/claude-haiku-4.5`
|
||||
|
||||
**ดีพซีค (`ds/`)**: `ds/deepseek-chat`, `ds/deepseek-reasoner`
|
||||
|
||||
**โกรก (`groq/`)**: `groq/llama-3.3-70b-versatile`, `groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI (`xai/`)**: `xai/grok-4`, `xai/grok-4-0709-fast-reasoning`, `xai/grok-code-mini`
|
||||
|
||||
**มิสทรัล (`mistral/`)**: `mistral/mistral-large-2501`, `mistral/codestral-2501`
|
||||
|
||||
**ความสับสน (`pplx/`)**: `pplx/sonar-pro`, `pplx/sonar`
|
||||
|
||||
** AI ร่วมกัน (`together/`)**: `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**ดอกไม้ไฟ AI (`fireworks/`)**: `fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**เซรีบร้า (`cerebras/`)**: `cerebras/llama-3.3-70b`
|
||||
|
||||
**เชื่อมโยงกัน (`cohere/`)**: `cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM (`nvidia/`)**: `nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 คุณสมบัติขั้นสูง
|
||||
|
||||
### โมเดลที่กำหนดเอง
|
||||
|
||||
เพิ่ม ID รุ่นใดๆ ให้กับผู้ให้บริการโดยไม่ต้องรอการอัปเดตแอป:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
หรือใช้แดชบอร์ด: **ผู้ให้บริการ → [ผู้ให้บริการ] → โมเดลที่กำหนดเอง**
|
||||
|
||||
### เส้นทางของผู้ให้บริการเฉพาะ
|
||||
|
||||
กำหนดเส้นทางคำขอโดยตรงไปยังผู้ให้บริการเฉพาะด้วยการตรวจสอบโมเดล:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
คำนำหน้าผู้ให้บริการจะถูกเพิ่มอัตโนมัติหากไม่มี โมเดลที่ไม่ตรงกันส่งคืน `400`
|
||||
|
||||
### การกำหนดค่าพร็อกซีเครือข่าย
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**ลำดับความสำคัญ:** เฉพาะคีย์ → เฉพาะคอมโบ → เฉพาะผู้ให้บริการ → ทั่วโลก → สภาพแวดล้อม
|
||||
|
||||
### โมเดลแคตตาล็อก API
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
ส่งคืนโมเดลที่จัดกลุ่มตามผู้ให้บริการที่มีประเภท (`chat`, `embedding`, `image`)
|
||||
|
||||
### คลาวด์ซิงค์
|
||||
|
||||
- ซิงค์ผู้ให้บริการ คอมโบ และการตั้งค่าระหว่างอุปกรณ์ต่างๆ
|
||||
- การซิงค์พื้นหลังอัตโนมัติพร้อมการหมดเวลา + ล้มเหลวอย่างรวดเร็ว
|
||||
- ต้องการ `BASE_URL`/`CLOUD_URL` ฝั่งเซิร์ฟเวอร์ในการใช้งานจริง
|
||||
|
||||
### LLM Gateway Intelligence (ระยะที่ 9)
|
||||
|
||||
- **Semantic Cache** — แคชอัตโนมัติไม่สตรีม อุณหภูมิ=0 การตอบสนอง (บายพาสด้วย `X-OmniRoute-No-Cache: true`)
|
||||
- **คำขอ Idempotency** — กรองคำขอที่ซ้ำกันภายใน 5 วินาทีผ่านส่วนหัว `Idempotency-Key` หรือ `X-Request-Id`
|
||||
- **การติดตามความคืบหน้า** — เลือกใช้กิจกรรม SSE `event: progress` ผ่านส่วนหัว `X-OmniRoute-Progress: true`
|
||||
|
||||
---
|
||||
|
||||
### สนามเด็กเล่นนักแปล
|
||||
|
||||
เข้าถึงได้ผ่าน **Dashboard → Translator** แก้ไขข้อบกพร่องและเห็นภาพว่า OmniRoute แปลคำขอ API ระหว่างผู้ให้บริการอย่างไร
|
||||
|
||||
| โหมด | วัตถุประสงค์ |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------- |
|
||||
| **สนามเด็กเล่น** | เลือกรูปแบบต้นทาง/เป้าหมาย วางคำขอ และดูผลลัพธ์ที่แปลได้ทันที |
|
||||
| **เครื่องมือทดสอบแชท** | ส่งข้อความแชทสดผ่านพร็อกซีและตรวจสอบรอบคำขอ/การตอบกลับทั้งหมด |
|
||||
| **ม้านั่งทดสอบ** | เรียกใช้การทดสอบเป็นกลุ่มโดยใช้รูปแบบต่างๆ ร่วมกันเพื่อตรวจสอบความถูกต้องของการแปล |
|
||||
| **ถ่ายทอดสด** | ดูการแปลแบบเรียลไทม์ตามคำขอที่ไหลผ่านพร็อกซี |
|
||||
|
||||
**กรณีการใช้งาน:**
|
||||
|
||||
- ตรวจแก้จุดบกพร่องว่าทำไมการรวมไคลเอนต์/ผู้ให้บริการเฉพาะจึงล้มเหลว
|
||||
- ตรวจสอบว่าแท็กการคิด การเรียกใช้เครื่องมือ และการแจ้งเตือนของระบบแปลอย่างถูกต้อง
|
||||
- เปรียบเทียบความแตกต่างของรูปแบบระหว่างรูปแบบ OpenAI, Claude, Gemini และ Responses API
|
||||
|
||||
---
|
||||
|
||||
### กลยุทธ์การกำหนดเส้นทาง
|
||||
|
||||
กำหนดค่าผ่าน **แดชบอร์ด → การตั้งค่า → การกำหนดเส้นทาง**
|
||||
|
||||
| กลยุทธ์ | คำอธิบาย |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **กรอกก่อน** | ใช้บัญชีตามลำดับความสำคัญ — บัญชีหลักจะจัดการคำขอทั้งหมดจนกว่าจะไม่พร้อมใช้งาน |
|
||||
| **โรบินตัวกลม** | วนรอบบัญชีทั้งหมดด้วยขีดจำกัดที่กำหนดได้ (ค่าเริ่มต้น: 3 สายต่อบัญชี) |
|
||||
| **P2C (พลังสองตัวเลือก)** | เลือกบัญชีและเส้นทางแบบสุ่ม 2 บัญชีไปยังบัญชีที่ดีต่อสุขภาพมากขึ้น — สร้างสมดุลระหว่างภาระกับการรับรู้เรื่องสุขภาพ |
|
||||
| **สุ่ม** | สุ่มเลือกบัญชีสำหรับแต่ละคำขอโดยใช้ Fisher-Yates shuffle |
|
||||
| **ใช้น้อยที่สุด** | กำหนดเส้นทางไปยังบัญชีที่มีการประทับเวลา `lastUsedAt` เก่าที่สุด กระจายการรับส่งข้อมูลเท่าๆ กัน |
|
||||
| **ปรับต้นทุนให้เหมาะสม** | กำหนดเส้นทางไปยังบัญชีที่มีค่าลำดับความสำคัญต่ำสุด ปรับให้เหมาะสมสำหรับผู้ให้บริการที่มีต้นทุนต่ำที่สุด |
|
||||
|
||||
#### นามแฝงโมเดลไวด์การ์ด
|
||||
|
||||
สร้างรูปแบบไวด์การ์ดเพื่อทำการแมปชื่อโมเดลใหม่:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
Wildcard รองรับ `*` (อักขระใดก็ได้) และ `?` (อักขระเดี่ยว)
|
||||
|
||||
#### โซ่สำรอง
|
||||
|
||||
กำหนดห่วงโซ่ทางเลือกส่วนกลางที่ใช้กับคำขอทั้งหมด:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ความยืดหยุ่นและเซอร์กิตเบรกเกอร์
|
||||
|
||||
กำหนดค่าผ่าน **แดชบอร์ด → การตั้งค่า → ความยืดหยุ่น**
|
||||
|
||||
OmniRoute ใช้ความยืดหยุ่นระดับผู้ให้บริการด้วยองค์ประกอบสี่ประการ:
|
||||
|
||||
1. **โปรไฟล์ผู้ให้บริการ** — การกำหนดค่าต่อผู้ให้บริการสำหรับ:
|
||||
- เกณฑ์ความล้มเหลว (จำนวนความล้มเหลวก่อนเปิด)
|
||||
- ระยะเวลาคูลดาวน์
|
||||
- ความไวในการตรวจจับขีด จำกัด อัตรา
|
||||
- พารามิเตอร์แบ็คออฟเอ็กซ์โปเนนเชียล
|
||||
|
||||
2. **ขีดจำกัดอัตราที่แก้ไขได้** — ค่าเริ่มต้นระดับระบบที่กำหนดค่าได้ในแดชบอร์ด:
|
||||
- **คำขอต่อนาที (RPM)** — คำขอสูงสุดต่อนาทีต่อบัญชี
|
||||
- **เวลาขั้นต่ำระหว่างคำขอ** — ช่องว่างขั้นต่ำเป็นมิลลิวินาทีระหว่างคำขอ
|
||||
- **คำขอพร้อมกันสูงสุด** — คำขอพร้อมกันสูงสุดต่อบัญชี
|
||||
- คลิก **แก้ไข** เพื่อแก้ไข จากนั้น **บันทึก** หรือ **ยกเลิก** ค่ายังคงมีอยู่ผ่าน API ความยืดหยุ่น
|
||||
|
||||
3. **เซอร์กิตเบรกเกอร์** — ติดตามความล้มเหลวของผู้ให้บริการแต่ละราย และเปิดวงจรโดยอัตโนมัติเมื่อถึงเกณฑ์:
|
||||
- **ปิด** (สมบูรณ์) — คำขอดำเนินไปตามปกติ
|
||||
- **เปิด** — ผู้ให้บริการถูกบล็อกชั่วคราวหลังจากเกิดข้อผิดพลาดซ้ำแล้วซ้ำอีก
|
||||
- **HALF_OPEN** — ทดสอบว่าผู้ให้บริการฟื้นตัวหรือไม่
|
||||
|
||||
4. **นโยบายและตัวระบุที่ถูกล็อค** — แสดงสถานะเซอร์กิตเบรกเกอร์และตัวระบุที่ถูกล็อคพร้อมความสามารถในการบังคับปลดล็อค
|
||||
|
||||
5. **การตรวจจับขีดจำกัดอัตราอัตโนมัติ** — ตรวจสอบส่วนหัว `429` และ `Retry-After` เพื่อหลีกเลี่ยงไม่ให้ถึงขีดจำกัดอัตราของผู้ให้บริการในเชิงรุก
|
||||
|
||||
**เคล็ดลับสำหรับมือโปร:** ใช้ปุ่ม **รีเซ็ตทั้งหมด** เพื่อล้างเซอร์กิตเบรกเกอร์และคูลดาวน์ทั้งหมดเมื่อผู้ให้บริการฟื้นตัวจากการหยุดทำงาน
|
||||
|
||||
---
|
||||
|
||||
### ส่งออก / นำเข้าฐานข้อมูล
|
||||
|
||||
จัดการการสำรองฐานข้อมูลใน **แดชบอร์ด → การตั้งค่า → ระบบและที่เก็บข้อมูล**
|
||||
|
||||
| การกระทำ | คำอธิบาย |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **ฐานข้อมูลการส่งออก** | ดาวน์โหลดฐานข้อมูล SQLite ปัจจุบันเป็นไฟล์ `.sqlite` |
|
||||
| **ส่งออกทั้งหมด (.tar.gz)** | ดาวน์โหลดไฟล์เก็บถาวรการสำรองข้อมูลแบบเต็ม รวมถึง: ฐานข้อมูล การตั้งค่า คอมโบ การเชื่อมต่อของผู้ให้บริการ (ไม่มีข้อมูลประจำตัว) ข้อมูลเมตาของคีย์ API |
|
||||
| **นำเข้าฐานข้อมูล** | อัปโหลดไฟล์ `.sqlite` เพื่อแทนที่ฐานข้อมูลปัจจุบัน การสำรองข้อมูลก่อนนำเข้าจะถูกสร้างขึ้นโดยอัตโนมัติ |
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**การตรวจสอบการนำเข้า:** ไฟล์ที่นำเข้าได้รับการตรวจสอบความถูกต้อง (การตรวจสอบ SQLite Pragma), ตารางที่จำเป็น (`provider_connections`, `provider_nodes`, `combos`, `api_keys`) และขนาด (สูงสุด 100MB)
|
||||
|
||||
**กรณีการใช้งาน:**
|
||||
|
||||
- โยกย้าย OmniRoute ระหว่างเครื่อง
|
||||
- สร้างการสำรองข้อมูลภายนอกสำหรับการกู้คืนระบบ
|
||||
- แบ่งปันการกำหนดค่าระหว่างสมาชิกในทีม (ส่งออกทั้งหมด → แชร์ไฟล์เก็บถาวร)
|
||||
|
||||
---
|
||||
|
||||
### แดชบอร์ดการตั้งค่า
|
||||
|
||||
หน้าการตั้งค่าแบ่งออกเป็น 5 แท็บเพื่อให้ง่ายต่อการนำทาง:
|
||||
|
||||
| แท็บ | สารบัญ |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **ความปลอดภัย** | การตั้งค่าการเข้าสู่ระบบ/รหัสผ่าน, การควบคุมการเข้าถึง IP, การตรวจสอบสิทธิ์ API สำหรับ `/models` และการบล็อกผู้ให้บริการ |
|
||||
| **การกำหนดเส้นทาง** | กลยุทธ์การกำหนดเส้นทางทั่วโลก (6 ตัวเลือก), นามแฝงโมเดลไวด์การ์ด, เชนทางเลือก, ค่าเริ่มต้นคอมโบ |
|
||||
| **ความยืดหยุ่น** | โปรไฟล์ผู้ให้บริการ ขีดจำกัดอัตราที่แก้ไขได้ สถานะเซอร์กิตเบรกเกอร์ นโยบาย และตัวระบุที่ถูกล็อค |
|
||||
| **เอไอ** | คิดการกำหนดค่างบประมาณ, การแทรกพร้อมท์ของระบบทั่วโลก, สถิติแคชพร้อมต์ |
|
||||
| **ขั้นสูง** | การกำหนดค่าพร็อกซีส่วนกลาง (HTTP/SOCKS5) |
|
||||
|
||||
---
|
||||
|
||||
### ต้นทุนและการจัดการงบประมาณ
|
||||
|
||||
เข้าถึงได้ผ่าน **แดชบอร์ด → ค่าใช้จ่าย**
|
||||
|
||||
| แท็บ | วัตถุประสงค์ |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------- |
|
||||
| **งบประมาณ** | กำหนดขีดจำกัดการใช้จ่ายต่อคีย์ API ด้วยงบประมาณรายวัน/รายสัปดาห์/รายเดือนและการติดตามแบบเรียลไทม์ |
|
||||
| **ราคา** | ดูและแก้ไขรายการการกำหนดราคาโมเดล — ต้นทุนต่อโทเค็นอินพุต/เอาท์พุต 1K ต่อผู้ให้บริการ |
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**การติดตามต้นทุน:** ทุกคำขอจะบันทึกการใช้โทเค็นและคำนวณต้นทุนโดยใช้ตารางราคา ดูรายละเอียดใน **แดชบอร์ด → การใช้งาน** ตามผู้ให้บริการ รุ่น และคีย์ API
|
||||
|
||||
---
|
||||
|
||||
### การถอดเสียง
|
||||
|
||||
OmniRoute รองรับการถอดเสียงผ่านปลายทางที่เข้ากันได้กับ OpenAI:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
ผู้ให้บริการที่มีอยู่: **Deepgram** (`deepgram/`), **AssemblyAI** (`assemblyai/`)
|
||||
|
||||
รูปแบบเสียงที่รองรับ: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
|
||||
---
|
||||
|
||||
### กลยุทธ์การปรับสมดุลคอมโบ
|
||||
|
||||
กำหนดค่าการปรับสมดุลต่อคอมโบใน **แดชบอร์ด → คอมโบ → สร้าง/แก้ไข → กลยุทธ์**
|
||||
|
||||
| กลยุทธ์ | คำอธิบาย |
|
||||
| ----------------------------- | -------------------------------------------------------------- |
|
||||
| **โรบินตัวกลม** | หมุนเวียนไปตามโมเดลต่างๆ ตามลำดับ |
|
||||
| **ลำดับความสำคัญ** | ลองใช้โมเดลแรกเสมอ ถอยกลับเฉพาะข้อผิดพลาด |
|
||||
| **สุ่ม** | เลือกโมเดลแบบสุ่มจากคอมโบสำหรับแต่ละคำขอ |
|
||||
| **ถ่วงน้ำหนัก** | เส้นทางตามสัดส่วนตามน้ำหนักที่กำหนดต่อรุ่น |
|
||||
| **ใช้งานน้อยที่สุด** | กำหนดเส้นทางไปยังโมเดลที่มีคำขอล่าสุดน้อยที่สุด (ใช้เมตริกผสม) |
|
||||
| **การเพิ่มประสิทธิภาพต้นทุน** | เส้นทางไปยังรุ่นที่ถูกที่สุด (ใช้ตารางราคา) |
|
||||
|
||||
ค่าเริ่มต้นคอมโบสากลสามารถตั้งค่าได้ใน **แดชบอร์ด → การตั้งค่า → การกำหนดเส้นทาง → ค่าเริ่มต้นคอมโบ**
|
||||
|
||||
---
|
||||
|
||||
### แดชบอร์ดสุขภาพ
|
||||
|
||||
เข้าถึงได้ทาง **Dashboard → Health** ภาพรวมความสมบูรณ์ของระบบเรียลไทม์พร้อมการ์ด 6 ใบ:
|
||||
|
||||
| บัตร | มันแสดงอะไร |
|
||||
| ----------------------------- | ---------------------------------------------------------------- |
|
||||
| **สถานะระบบ** | สถานะการออนไลน์ เวอร์ชัน การใช้หน่วยความจำ ไดเร็กทอรีข้อมูล |
|
||||
| **สุขภาพของผู้ให้บริการ** | สถานะเซอร์กิตเบรกเกอร์ต่อผู้ให้บริการ (ปิด/เปิด/เปิดครึ่ง) |
|
||||
| **จำกัดอัตรา** | คูลดาวน์จำกัดอัตราที่ใช้งานอยู่ต่อบัญชีพร้อมเวลาที่เหลืออยู่ |
|
||||
| **การล็อกที่ใช้งานอยู่** | ผู้ให้บริการถูกบล็อกชั่วคราวโดยนโยบายการล็อค |
|
||||
| **แคชลายเซ็น** | สถิติแคชการขจัดข้อมูลซ้ำซ้อน (คีย์ที่ใช้งานอยู่ อัตราการเข้าถึง) |
|
||||
| **การวัดระยะไกลแบบหน่วงเวลา** | การรวมเวลาแฝง p50/p95/p99 ต่อผู้ให้บริการ |
|
||||
|
||||
**เคล็ดลับสำหรับมือโปร:** หน้าสุขภาพจะรีเฟรชอัตโนมัติทุกๆ 10 วินาที ใช้การ์ดเซอร์กิตเบรกเกอร์เพื่อระบุว่าผู้ให้บริการรายใดกำลังประสบปัญหา
|
||||
441
docs/i18n/uk-UA/API_REFERENCE.md
Normal file
441
docs/i18n/uk-UA/API_REFERENCE.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# API Reference
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
|
||||
|
||||
Повний довідник для всіх кінцевих точок OmniRoute API.
|
||||
|
||||
---
|
||||
|
||||
## Зміст
|
||||
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Semantic Cache](#semantic-cache)
|
||||
- [Dashboard & Management](#dashboard--management)
|
||||
- [Request Processing](#request-processing)
|
||||
- [Authentication](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## Завершення чату
|
||||
|
||||
```bash
|
||||
POST /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
|
||||
}
|
||||
```
|
||||
|
||||
### Спеціальні заголовки
|
||||
|
||||
| Заголовок | Напрям | Опис |
|
||||
| ------------------------ | --------- | --------------------------------------- |
|
||||
| `X-OmniRoute-No-Cache` | Запит | Установіть `true`, щоб обійти кеш |
|
||||
| `X-OmniRoute-Progress` | Запит | Встановіть `true` для подій прогресу |
|
||||
| `Idempotency-Key` | Запит | Ключ дедуплювання (5-секундне вікно) |
|
||||
| `X-Request-Id` | Запит | Альтернативний ключ дедуплювання |
|
||||
| `X-OmniRoute-Cache` | Відповідь | `HIT` або `MISS` (не потоковий) |
|
||||
| `X-OmniRoute-Idempotent` | Відповідь | `true` якщо дедупліковано |
|
||||
| `X-OmniRoute-Progress` | Відповідь | `enabled`, якщо відстеження прогресу на |
|
||||
|
||||
---
|
||||
|
||||
## Вбудовування
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
Доступні постачальники: Nebius, OpenAI, Mistral, Together AI, Fireworks, NVIDIA.
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Генерація зображень
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
Доступні постачальники: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI.
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Список моделей
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Кінцеві точки сумісності
|
||||
|
||||
| Метод | Шлях | Формат |
|
||||
| ------------ | --------------------------- | ---------------------- |
|
||||
| Опублікувати | `/v1/chat/completions` | OpenAI |
|
||||
| Опублікувати | `/v1/messages` | Антропний |
|
||||
| Опублікувати | `/v1/responses` | Відповіді OpenAI |
|
||||
| Опублікувати | `/v1/embeddings` | OpenAI |
|
||||
| Опублікувати | `/v1/images/generations` | OpenAI |
|
||||
| ОТРИМАТИ | `/v1/models` | OpenAI |
|
||||
| Опублікувати | `/v1/messages/count_tokens` | Антропний |
|
||||
| ОТРИМАТИ | `/v1beta/models` | Близнюки |
|
||||
| Опублікувати | `/v1beta/models/{...path}` | Gemini generateContent |
|
||||
| Опублікувати | `/v1/api/chat` | Оллама |
|
||||
|
||||
### Виділені маршрути постачальників
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
Префікс провайдера додається автоматично, якщо його немає. Невідповідні моделі повертають `400`.
|
||||
|
||||
---
|
||||
|
||||
## Семантичний кеш
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
Приклад відповіді:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Інформаційна панель і керування
|
||||
|
||||
### Автентифікація
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ----------------------------- | ------------ | -------------------------- |
|
||||
| `/api/auth/login` | Опублікувати | Вхід |
|
||||
| `/api/auth/logout` | Опублікувати | Вийти |
|
||||
| `/api/settings/require-login` | GET/PUT | Перемкнути необхідний вхід |
|
||||
|
||||
### Керування провайдером
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ---------------------------- | --------------- | ------------------------------------ |
|
||||
| `/api/providers` | GET/POST | Список / створення постачальників |
|
||||
| `/api/providers/[id]` | GET/PUT/DELETE | Керувати постачальником |
|
||||
| `/api/providers/[id]/test` | Опублікувати | Перевірте підключення провайдера |
|
||||
| `/api/providers/[id]/models` | ОТРИМАТИ | Список моделей провайдерів |
|
||||
| `/api/providers/validate` | Опублікувати | Перевірте конфігурацію постачальника |
|
||||
| `/api/provider-nodes*` | Різні | Керування вузлом провайдера |
|
||||
| `/api/provider-models` | GET/POST/DELETE | Індивідуальні моделі |
|
||||
|
||||
### Потоки OAuth
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| -------------------------------- | ----- | ----------------------- |
|
||||
| `/api/oauth/[provider]/[action]` | Різні | OAuth для постачальника |
|
||||
|
||||
### Маршрутизація та конфігурація
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------------- | -------- | ------------------------------- |
|
||||
| `/api/models/alias` | GET/POST | Псевдоніми моделей |
|
||||
| `/api/models/catalog` | ОТРИМАТИ | Всі моделі за провайдером + тип |
|
||||
| `/api/combos*` | Різні | Комбо управління |
|
||||
| `/api/keys*` | Різні | Керування ключами API |
|
||||
| `/api/pricing` | ОТРИМАТИ | Модель ціноутворення |
|
||||
|
||||
### Використання та аналітика
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------------------- | -------- | -------------------------------- |
|
||||
| `/api/usage/history` | ОТРИМАТИ | Історія використання |
|
||||
| `/api/usage/logs` | ОТРИМАТИ | Журнали використання |
|
||||
| `/api/usage/request-logs` | ОТРИМАТИ | Журнали рівня запиту |
|
||||
| `/api/usage/[connectionId]` | ОТРИМАТИ | Використання кожного підключення |
|
||||
|
||||
### Налаштування
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ------------------------------- | ------------ | ------------------------------- |
|
||||
| `/api/settings` | GET/PUT | Загальні налаштування |
|
||||
| `/api/settings/proxy` | GET/PUT | Конфігурація мережевого проксі |
|
||||
| `/api/settings/proxy/test` | Опублікувати | Тест проксі-з'єднання |
|
||||
| `/api/settings/ip-filter` | GET/PUT | Список дозволених/чорних IP |
|
||||
| `/api/settings/thinking-budget` | GET/PUT | Обґрунтування жетонного бюджету |
|
||||
| `/api/settings/system-prompt` | GET/PUT | Глобальна системна підказка |
|
||||
|
||||
### Моніторинг
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ------------------------ | ----------------- | -------------------------------- |
|
||||
| `/api/sessions` | ОТРИМАТИ | Відстеження активної сесії |
|
||||
| `/api/rate-limits` | ОТРИМАТИ | Ліміти ставок за обліковий запис |
|
||||
| `/api/monitoring/health` | ОТРИМАТИ | Перевірка стану здоров'я |
|
||||
| `/api/cache` | ОТРИМАТИ/ВИДАЛИТИ | Статистика кешу / очищення |
|
||||
|
||||
### Резервне копіювання та експорт/імпорт
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------------------- | ------------ | ------------------------------------------------ |
|
||||
| `/api/db-backups` | ОТРИМАТИ | Список доступних резервних копій |
|
||||
| `/api/db-backups` | ПОСТАВИТИ | Створіть резервну копію вручну |
|
||||
| `/api/db-backups` | Опублікувати | Відновити з певної резервної копії |
|
||||
| `/api/db-backups/export` | ОТРИМАТИ | Завантажити базу даних як файл .sqlite |
|
||||
| `/api/db-backups/import` | Опублікувати | Завантажте файл .sqlite для заміни бази даних |
|
||||
| `/api/db-backups/exportAll` | ОТРИМАТИ | Завантажте повну резервну копію як архів .tar.gz |
|
||||
|
||||
### Хмарна синхронізація
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ---------------------- | ------------ | ------------------------------ |
|
||||
| `/api/sync/cloud` | Різні | Операції хмарної синхронізації |
|
||||
| `/api/sync/initialize` | Опублікувати | Ініціалізація синхронізації |
|
||||
| `/api/cloud/*` | Різні | Управління хмарою |
|
||||
|
||||
### Інструменти CLI
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ---------------------------------- | -------- | --------------------------------- |
|
||||
| `/api/cli-tools/claude-settings` | ОТРИМАТИ | Клод CLI статус |
|
||||
| `/api/cli-tools/codex-settings` | ОТРИМАТИ | Codex CLI status |
|
||||
| `/api/cli-tools/droid-settings` | ОТРИМАТИ | Droid CLI status |
|
||||
| `/api/cli-tools/openclaw-settings` | ОТРИМАТИ | Статус OpenClaw CLI |
|
||||
| `/api/cli-tools/runtime/[toolId]` | ОТРИМАТИ | Загальне середовище виконання CLI |
|
||||
|
||||
Відповіді CLI включають: `installed`, `runnable`, `command`, `commandPath`, `runtimeMode`, `reason`.
|
||||
|
||||
### Стійкість і обмеження швидкості
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ----------------------- | ------------ | -------------------------------------------- |
|
||||
| `/api/resilience` | GET/PUT | Отримати/оновити профілі стійкості |
|
||||
| `/api/resilience/reset` | Опублікувати | Скидання автоматичних вимикачів |
|
||||
| `/api/rate-limits` | ОТРИМАТИ | Статус обмеження ставки на обліковий запис |
|
||||
| `/api/rate-limit` | ОТРИМАТИ | Конфігурація глобального обмеження швидкості |
|
||||
|
||||
### Оцінки
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| ------------- | -------- | ---------------------------------------------- |
|
||||
| `/api/evals` | GET/POST | Створити список eval suites / запустити оцінку |
|
||||
|
||||
### Політика
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------- | --------------- | --------------------------------- |
|
||||
| `/api/policies` | GET/POST/DELETE | Керування політикою маршрутизації |
|
||||
|
||||
### Відповідність
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------------------- | -------- | ---------------------------------------- |
|
||||
| `/api/compliance/audit-log` | ОТРИМАТИ | Журнал аудиту відповідності (останній N) |
|
||||
|
||||
### v1beta (сумісний із Gemini)
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| -------------------------- | ------------ | -------------------------------------- |
|
||||
| `/v1beta/models` | ОТРИМАТИ | Список моделей у форматі Gemini |
|
||||
| `/v1beta/models/{...path}` | Опублікувати | Кінцева точка Gemini `generateContent` |
|
||||
|
||||
Ці кінцеві точки відображають формат API Gemini для клієнтів, які очікують нативної сумісності з Gemini SDK.
|
||||
|
||||
### Внутрішні/системні API
|
||||
|
||||
| Кінцева точка | Метод | Опис |
|
||||
| --------------- | ------------ | --------------------------------------------------------------------------- |
|
||||
| `/api/init` | ОТРИМАТИ | Перевірка ініціалізації програми (використовується під час першого запуску) |
|
||||
| `/api/tags` | ОТРИМАТИ | Сумісні з Ollama теги моделей (для клієнтів Ollama) |
|
||||
| `/api/restart` | Опублікувати | Ініціювати плавний перезапуск сервера |
|
||||
| `/api/shutdown` | Опублікувати | Ініціювати плавне завершення роботи сервера |
|
||||
|
||||
> **Примітка.** Ці кінцеві точки використовуються внутрішньо системою або для сумісності клієнта Ollama. Зазвичай вони не викликаються кінцевими користувачами.
|
||||
|
||||
---
|
||||
|
||||
## Транскрипція аудіо
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
Транскрибуйте аудіофайли за допомогою Deepgram або AssemblyAI.
|
||||
|
||||
**Запит:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**Відповідь:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
**Підтримувані постачальники:** `deepgram/nova-3`, `assemblyai/best`.
|
||||
|
||||
**Підтримувані формати:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
## Сумісність Ollama
|
||||
|
||||
Для клієнтів, які використовують формат API Ollama:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
Запити автоматично перекладаються між Ollama та внутрішніми форматами.
|
||||
|
||||
---
|
||||
|
||||
## Телеметрія
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**Відповідь:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Бюджет
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Наявність моделі
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Обробка запиту
|
||||
|
||||
1. Клієнт надсилає запит на `/v1/*`
|
||||
2. Обробник маршруту викликає `handleChat`, `handleEmbedding`, `handleAudioTranscription` або `handleImageGeneration`
|
||||
3. Модель вирішено (прямий постачальник/модель або псевдонім/комбо)
|
||||
4. Облікові дані, вибрані з локальної БД з фільтрацією доступності облікових записів
|
||||
5. Для чату: `handleChatCore` — визначення формату, переклад, перевірка кешу, перевірка ідемпотентності
|
||||
6. Виконавець провайдера надсилає висхідний запит
|
||||
7. Відповідь перекладається назад у формат клієнта (чат) або повертається як є (вбудовування/зображення/аудіо)
|
||||
8. Запис використання/реєстрації
|
||||
9. Резервний варіант застосовується до помилок відповідно до правил комбінування
|
||||
|
||||
Повне посилання на архітектуру: [**OMNI_TOKEN_119**](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## Автентифікація
|
||||
|
||||
- Маршрути інформаційної панелі (`/dashboard/*`) використовують `auth_token` cookie
|
||||
- Вхід використовує збережений хеш пароля; повернутися до `INITIAL_PASSWORD`
|
||||
- `requireLogin` можна перемикати через `/api/settings/require-login`
|
||||
- Маршрути `/v1/*` додатково вимагають ключ API носія, коли `REQUIRE_API_KEY=true`
|
||||
782
docs/i18n/uk-UA/ARCHITECTURE.md
Normal file
782
docs/i18n/uk-UA/ARCHITECTURE.md
Normal file
@@ -0,0 +1,782 @@
|
||||
# Архітектура OmniRoute
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
|
||||
|
||||
_Останнє оновлення: 2026-02-18_
|
||||
|
||||
## Резюме
|
||||
|
||||
OmniRoute — це локальний шлюз штучного інтелекту та інформаційна панель, побудована на Next.js.
|
||||
Він надає єдину кінцеву точку, сумісну з OpenAI (`/v1/*`), і направляє трафік між декількома вихідними постачальниками з перекладом, резервним варіантом, оновленням маркерів і відстеженням використання.
|
||||
|
||||
Основні можливості:
|
||||
|
||||
- OpenAI-сумісна поверхня API для CLI/інструментів (28 постачальників)
|
||||
- Переклад запитів/відповідей між форматами постачальників
|
||||
- Запасна комбінована модель (багатомодельна послідовність)
|
||||
- Запасний варіант на рівні облікового запису (декілька облікових записів на постачальника)
|
||||
- OAuth + API-ключ управління підключенням провайдера
|
||||
- Генерація вбудовування через `/v1/embeddings` (6 провайдерів, 9 моделей)
|
||||
- Генерація зображення через `/v1/images/generations` (4 постачальники, 9 моделей)
|
||||
- Аналіз тегів мислення (`<think>...</think>`) для моделей міркування
|
||||
— Дезінфекція відповіді для суворої сумісності з OpenAI SDK
|
||||
— Нормалізація ролі (розробник→система, система→користувач) для сумісності між постачальниками
|
||||
- Перетворення структурованого виводу (json_schema → Gemini responseSchema)
|
||||
- Локальна постійність для провайдерів, ключів, псевдонімів, комбо, налаштувань, ціноутворення
|
||||
- Відстеження використання/вартості та реєстрація запитів
|
||||
- Додаткова хмарна синхронізація для синхронізації кількох пристроїв/станів
|
||||
— Список дозволених/чорних IP-адрес для контролю доступу до API
|
||||
- Продумане управління бюджетом (прохідний/автоматичний/спеціальний/адаптивний)
|
||||
- Оперативна ін'єкція глобальної системи
|
||||
- Відстеження сесії та відбитки пальців
|
||||
- Розширене обмеження швидкості для кожного облікового запису за допомогою профілів постачальника
|
||||
- Схема автоматичного вимикача для стійкості провайдера
|
||||
- Захист стада від грому з блокуванням м'ютексу
|
||||
— Кеш дедуплікації запитів на основі підпису
|
||||
- Рівень домену: доступність моделі, правила вартості, резервна політика, політика блокування
|
||||
- Постійність стану домену (скрізний кеш SQLite для резервних копій, бюджетів, блокувань, автоматичних вимикачів)
|
||||
- Механізм політики для централізованої оцінки запитів (блокування → бюджет → резервний варіант)
|
||||
- Запит телеметрії з агрегацією затримок p50/p95/p99
|
||||
- Ідентифікатор кореляції (X-Request-Id) для наскрізного відстеження
|
||||
- Журнал аудиту відповідності з відмовою для кожного ключа API
|
||||
- Eval framework для забезпечення якості LLM
|
||||
— Панель інструментів інтерфейсу Resilience зі статусом автоматичного вимикача в режимі реального часу
|
||||
- Модульні постачальники OAuth (12 окремих модулів під `src/lib/oauth/providers/`)
|
||||
|
||||
Основна модель середовища виконання:
|
||||
|
||||
- Маршрути програми Next.js під `src/app/api/*` реалізують як API панелі керування, так і API сумісності
|
||||
- Спільне ядро SSE/маршрутизації в `src/sse/*` + `open-sse/*` обробляє виконання провайдера, переклад, потокове передавання, відкат і використання
|
||||
|
||||
## Обсяг і межі
|
||||
|
||||
### У межах
|
||||
|
||||
- Час виконання локального шлюзу
|
||||
- API керування інформаційною панеллю
|
||||
- Автентифікація постачальника та оновлення маркера
|
||||
- Запит на переклад і потокове передавання SSE
|
||||
— Локальний стан + постійність використання
|
||||
— Додаткова синхронізація з хмарою
|
||||
|
||||
### Поза межами
|
||||
|
||||
- Реалізація хмарної служби за `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Площина SLA/контроль постачальника поза локальним процесом
|
||||
- Самі зовнішні двійкові файли CLI (Claude CLI, Codex CLI тощо)
|
||||
|
||||
## Системний контекст високого рівня
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(db.json)]
|
||||
UDB[(usage.json + log.txt)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## Основні компоненти середовища виконання
|
||||
|
||||
## 1) API та рівень маршрутизації (маршрути програми Next.js)
|
||||
|
||||
Основні каталоги:
|
||||
|
||||
- `src/app/api/v1/*` та `src/app/api/v1beta/*` для API сумісності
|
||||
- `src/app/api/*` для API керування/конфігурації
|
||||
- Далі перезаписує в `next.config.mjs` карту `/v1/*` на `/api/v1/*`
|
||||
|
||||
Важливі маршрути сумісності:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — включає власні моделі з `custom: true`
|
||||
- `src/app/api/v1/embeddings/route.ts` — генерація вбудовування (6 провайдерів)
|
||||
- `src/app/api/v1/images/generations/route.ts` — генерація зображень (4+ провайдери вкл. Antigravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — спеціальний чат для кожного провайдера
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — виділені вбудовування для кожного постачальника
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — виділені зображення для кожного постачальника
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
Домени керування:
|
||||
|
||||
- Аутентифікація/налаштування: `src/app/api/auth/*`, `src/app/api/settings/*`
|
||||
- Постачальники/підключення: `src/app/api/providers*`
|
||||
- Вузли постачальника: `src/app/api/provider-nodes*`
|
||||
- Спеціальні моделі: `src/app/api/provider-models` (GET/POST/DELETE)
|
||||
- Каталог моделей: `src/app/api/models/catalog` (GET)
|
||||
- Конфігурація проксі: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth: `src/app/api/oauth/*`
|
||||
- Ключі/псевдоніми/комбінації/ціни: `src/app/api/keys*`, `src/app/api/models/alias`, `src/app/api/combos*`, `src/app/api/pricing`
|
||||
- Використання: `src/app/api/usage/*`
|
||||
- Синхронізація/хмара: `src/app/api/sync/*`, `src/app/api/cloud/*`
|
||||
- Допоміжні інструменти CLI: `src/app/api/cli-tools/*`
|
||||
- IP-фільтр: `src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- Бюджет мислення: `src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- Системне повідомлення: `src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- Сеанси: `src/app/api/sessions` (ОТРИМАТИ)
|
||||
- Обмеження швидкості: `src/app/api/rate-limits` (GET)
|
||||
- Стійкість: `src/app/api/resilience` (GET/PATCH) — профілі постачальників, автоматичний вимикач, граничний стан швидкості
|
||||
- Скидання стійкості: `src/app/api/resilience/reset` (POST) — скидання вимикачів + відновлення
|
||||
- Статистика кешу: `src/app/api/cache/stats` (GET/DELETE)
|
||||
- Доступність моделі: `src/app/api/models/availability` (GET/POST)
|
||||
- Телеметрія: `src/app/api/telemetry/summary` (GET)
|
||||
- Бюджет: `src/app/api/usage/budget` (GET/POST)
|
||||
- Резервні ланцюжки: `src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- Аудит відповідності: `src/app/api/compliance/audit-log` (GET)
|
||||
- Оцінки: `src/app/api/evals` (GET/POST), `src/app/api/evals/[suiteId]` (GET)
|
||||
- Правила: `src/app/api/policies` (GET/POST)
|
||||
|
||||
## 2) SSE + Translation Core
|
||||
|
||||
Основні модулі потоку:
|
||||
|
||||
- Запис: `src/sse/handlers/chat.ts`
|
||||
- Оркестровка ядра: `open-sse/handlers/chatCore.ts`
|
||||
- Адаптери виконання постачальника: `open-sse/executors/*`
|
||||
- Виявлення формату/конфігурація постачальника: `open-sse/services/provider.ts`
|
||||
- Розбір/вирішення моделі: `src/sse/services/model.ts`, `open-sse/services/model.ts`
|
||||
- Резервна логіка облікового запису: `open-sse/services/accountFallback.ts`
|
||||
- Реєстр перекладів: `open-sse/translator/index.ts`
|
||||
- Трансформації потоку: `open-sse/utils/stream.ts`, `open-sse/utils/streamHandler.ts`
|
||||
- Вилучення/нормалізація використання: `open-sse/utils/usageTracking.ts`
|
||||
- Розбір тегів Think: `open-sse/utils/thinkTagParser.ts`
|
||||
- Обробник вбудовування: `open-sse/handlers/embeddings.ts`
|
||||
- Реєстр постачальника вбудовування: `open-sse/config/embeddingRegistry.ts`
|
||||
- Обробник створення зображення: `open-sse/handlers/imageGeneration.ts`
|
||||
- Реєстр постачальників зображень: `open-sse/config/imageRegistry.ts`
|
||||
- Дезінфекція відповіді: `open-sse/handlers/responseSanitizer.ts`
|
||||
- Нормалізація ролі: `open-sse/services/roleNormalizer.ts`
|
||||
|
||||
Послуги (бізнес-логіка):
|
||||
|
||||
- Вибір облікового запису/оцінка: `open-sse/services/accountSelector.ts`
|
||||
- Керування життєвим циклом контексту: `open-sse/services/contextManager.ts`
|
||||
- Примусовий IP-фільтр: `open-sse/services/ipFilter.ts`
|
||||
- Відстеження сесії: `open-sse/services/sessionManager.ts`
|
||||
- Запит на дедуплікацію: `open-sse/services/signatureCache.ts`
|
||||
- Системна підказка: `open-sse/services/systemPrompt.ts`
|
||||
- Продумане управління бюджетом: `open-sse/services/thinkingBudget.ts`
|
||||
- Маршрутизація моделі підстановок: `open-sse/services/wildcardRouter.ts`
|
||||
- Керування обмеженнями швидкості: `open-sse/services/rateLimitManager.ts`
|
||||
- Автоматичний вимикач: `open-sse/services/circuitBreaker.ts`
|
||||
|
||||
Модулі рівня домену:
|
||||
|
||||
- Доступність моделі: `src/lib/domain/modelAvailability.ts`
|
||||
- Правила витрат/бюджети: `src/lib/domain/costRules.ts`
|
||||
- Резервна політика: `src/lib/domain/fallbackPolicy.ts`
|
||||
- Комбінований розпізнавач: `src/lib/domain/comboResolver.ts`
|
||||
- Політика блокування: `src/lib/domain/lockoutPolicy.ts`
|
||||
- Механізм політики: `src/domain/policyEngine.ts` — централізоване блокування → бюджет → резервна оцінка
|
||||
- Каталог кодів помилок: `src/lib/domain/errorCodes.ts`
|
||||
- Ідентифікатор запиту: `src/lib/domain/requestId.ts`
|
||||
- Час очікування отримання: `src/lib/domain/fetchTimeout.ts`
|
||||
- Запит телеметрії: `src/lib/domain/requestTelemetry.ts`
|
||||
- Відповідність/аудит: `src/lib/domain/compliance/index.ts`
|
||||
- Eval runner: `src/lib/domain/evalRunner.ts`
|
||||
- Постійність стану домену: `src/lib/db/domainState.ts` — SQLite CRUD для резервних ланцюжків, бюджетів, історії витрат, стану блокування, автоматичних вимикачів
|
||||
|
||||
Модулі постачальника OAuth (12 окремих файлів під `src/lib/oauth/providers/`):
|
||||
|
||||
- Індекс реєстру: `src/lib/oauth/providers/index.ts`
|
||||
- Окремі постачальники: `claude.ts`, `codex.ts`, `gemini.ts`, `antigravity.ts`, `iflow.ts`, `qwen.ts`, `kimi-coding.ts`, `github.ts`, `kiro.ts`, `cursor.ts`, `kilocode.ts`, `cline.ts`
|
||||
- Тонка оболонка: `src/lib/oauth/providers.ts` — реекспорт з окремих модулів
|
||||
|
||||
## 3) Рівень стійкості
|
||||
|
||||
База даних первинного стану:
|
||||
|
||||
- `src/lib/localDb.ts`
|
||||
- файл: `${DATA_DIR}/db.json` (або `$XDG_CONFIG_HOME/omniroute/db.json`, якщо встановлено, інакше `~/.omniroute/db.json`)
|
||||
- сутності: providerConnections, providerNodes, modelAliases, combos, apiKeys, settings, pricing, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
|
||||
|
||||
БД використання:
|
||||
|
||||
- `src/lib/usageDb.ts`
|
||||
- файли: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- дотримується тієї ж базової політики каталогу, що й `localDb` (`DATA_DIR`, потім `XDG_CONFIG_HOME/omniroute`, якщо встановлено)
|
||||
- розкладено на підмодулі: `migrations.ts`, `usageHistory.ts`, `costCalculator.ts`, `usageStats.ts`, `callLogs.ts`
|
||||
|
||||
БД стану домену (SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — операції CRUD для стану домену
|
||||
- Таблиці (створені в `src/lib/db/core.ts`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
|
||||
- Шаблон кешу наскрізного запису: Карти в пам'яті є авторитетними під час виконання; мутації записуються синхронно в SQLite; стан відновлюється з БД при холодному запуску
|
||||
|
||||
## 4) Auth + Security Surfaces
|
||||
|
||||
- Автентифікація файлів cookie інформаційної панелі: `src/proxy.ts`, `src/app/api/auth/login/route.ts`
|
||||
- Генерація/перевірка ключа API: `src/shared/utils/apiKey.ts`
|
||||
- Секрети постачальника зберігаються в `providerConnections` записах
|
||||
- Підтримка вихідного проксі-сервера через `open-sse/utils/proxyFetch.ts` (env vars) і `open-sse/utils/networkProxy.ts` (налаштовується для кожного постачальника або глобально)
|
||||
|
||||
## 5) Хмарна синхронізація
|
||||
|
||||
- Ініціалізація планувальника: `src/lib/initCloudSync.ts`, `src/shared/services/initializeCloudSync.ts`
|
||||
- Періодичне завдання: `src/shared/services/cloudSyncScheduler.ts`
|
||||
- Контрольний маршрут: `src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## Життєвий цикл запиту (`/v1/chat/completions`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## Combo + Потік резервного облікового запису
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
Резервні рішення керуються `open-sse/services/accountFallback.ts` за допомогою кодів стану та евристики повідомлень про помилки.
|
||||
|
||||
## Введення OAuth і життєвий цикл оновлення маркерів
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
Оновлення під час живого трафіку виконується всередині `open-sse/handlers/chatCore.ts` через виконавець `refreshCredentials()`.
|
||||
|
||||
## Життєвий цикл Cloud Sync (Увімкнути / Синхронізувати / Вимкнути)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
Періодичну синхронізацію запускає `CloudSyncScheduler`, коли хмару ввімкнено.
|
||||
|
||||
## Модель даних і карта зберігання
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
Файли фізичного зберігання:
|
||||
|
||||
- основний стан: `${DATA_DIR}/db.json` (або `$XDG_CONFIG_HOME/omniroute/db.json`, якщо встановлено, інакше `~/.omniroute/db.json`)
|
||||
- статистика використання: `${DATA_DIR}/usage.json`
|
||||
- рядки журналу запитів: `${DATA_DIR}/log.txt`
|
||||
- додатковий перекладач/сеанси налагодження запитів: `<repo>/logs/...`
|
||||
|
||||
## Топологія розгортання
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(db.json)]
|
||||
UsageDB[(usage.json/log.txt)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## Відображення модулів (важливо для прийняття рішень)
|
||||
|
||||
### Модулі маршруту та API
|
||||
|
||||
- `src/app/api/v1/*`, `src/app/api/v1beta/*`: API сумісності
|
||||
- `src/app/api/v1/providers/[provider]/*`: виділені маршрути для кожного постачальника (чат, вбудовування, зображення)
|
||||
- `src/app/api/providers*`: CRUD провайдера, перевірка, тестування
|
||||
- `src/app/api/provider-nodes*`: настроюване сумісне керування вузлом
|
||||
- `src/app/api/provider-models`: користувацьке керування моделлю (CRUD)
|
||||
- `src/app/api/models/catalog`: API каталогу повної моделі (усі типи згруповані за постачальником)
|
||||
- `src/app/api/oauth/*`: потоки OAuth/код пристрою
|
||||
- `src/app/api/keys*`: життєвий цикл локального ключа API
|
||||
- `src/app/api/models/alias`: керування псевдонімами
|
||||
- `src/app/api/combos*`: резервне керування комбо
|
||||
- `src/app/api/pricing`: заміна ціноутворення для розрахунку вартості
|
||||
- `src/app/api/settings/proxy`: конфігурація проксі (GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test`: тест підключення вихідного проксі (POST)
|
||||
- `src/app/api/usage/*`: API використання та журналів
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*`: хмарна синхронізація та помічники в хмарі
|
||||
- `src/app/api/cli-tools/*`: локальні автори/перевірки конфігурації CLI
|
||||
- `src/app/api/settings/ip-filter`: список дозволених/чорних IP-адрес (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget`: конфігурація бюджету маркера мислення (GET/PUT)
|
||||
- `src/app/api/settings/system-prompt`: глобальна системна підказка (GET/PUT)
|
||||
- `src/app/api/sessions`: список активних сеансів (GET)
|
||||
- `src/app/api/rate-limits`: статус ліміту ставки на обліковий запис (GET)
|
||||
|
||||
### Ядро маршрутизації та виконання
|
||||
|
||||
- `src/sse/handlers/chat.ts`: синтаксичний аналіз запиту, комбінована обробка, цикл вибору облікового запису
|
||||
- `open-sse/handlers/chatCore.ts`: переклад, розсилка виконавця, обробка повторів/оновлень, налаштування потоку
|
||||
- `open-sse/executors/*`: поведінка мережі та формату залежно від постачальника
|
||||
|
||||
### Реєстр перекладів і конвертери форматів
|
||||
|
||||
- `open-sse/translator/index.ts`: реєстр перекладачів і оркестровка
|
||||
- Запит перекладачів: `open-sse/translator/request/*`
|
||||
- Перекладачі відповідей: `open-sse/translator/response/*`
|
||||
- Константи формату: `open-sse/translator/formats.ts`
|
||||
|
||||
### Наполегливість
|
||||
|
||||
- `src/lib/localDb.ts`: постійна конфігурація/стан
|
||||
- `src/lib/usageDb.ts`: історія використання та журнали поточних запитів
|
||||
|
||||
## Покриття виконавця постачальника (шаблон стратегії)
|
||||
|
||||
Кожен постачальник має спеціалізований виконавець, що розширює `BaseExecutor` (у `open-sse/executors/base.ts`), який забезпечує побудову URL-адреси, побудову заголовка, повторну спробу з експоненційною відстрочкою, перехоплювачі оновлення облікових даних і метод оркестровки `execute()`.
|
||||
|
||||
| Виконавець | Постачальник(и) | Спеціальна обробка |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| `DefaultExecutor` | OpenAI, Claude, Gemini, Qwen, iFlow, OpenRouter, GLM, Kimi, MiniMax, DeepSeek, Groq, xAI, Mistral, Perplexity, Together, Fireworks, Cerebras, Cohere, NVIDIA | Конфігурація динамічної URL-адреси/заголовка для кожного постачальника |
|
||||
| `AntigravityExecutor` | Антигравітація Google | Ідентифікатори користувацьких проектів/сеансів, повторна спроба після аналізу |
|
||||
| `CodexExecutor` | OpenAI Codex | Впроваджує системні інструкції, змушує міркувати |
|
||||
| `CursorExecutor` | Курсор IDE | Протокол ConnectRPC, кодування Protobuf, підпис запиту через контрольну суму |
|
||||
| `GithubExecutor` | Копілот GitHub | Оновлення маркера Copilot, заголовки, що імітують VSCode |
|
||||
| `KiroExecutor` | AWS CodeWhisperer/Kiro | Двійковий формат AWS EventStream → Перетворення SSE |
|
||||
| `GeminiCLIExecutor` | Gemini CLI | Цикл оновлення маркера Google OAuth |
|
||||
|
||||
Усі інші постачальники (включно з настроюваними сумісними вузлами) використовують `DefaultExecutor`.
|
||||
|
||||
## Матриця сумісності постачальників
|
||||
|
||||
| Постачальник | Формат | Авторизація | Потік | Непотоковий | Токен Оновити | Використання API |
|
||||
| ---------------- | ---------------- | ---------------------- | ---------------- | ----------- | ------------- | ------------------------- |
|
||||
| Клод | Клод | Ключ API / OAuth | ✅ | ✅ | ✅ | ⚠️ Лише адміністратор |
|
||||
| Близнюки | близнюки | Ключ API / OAuth | ✅ | ✅ | ✅ | ⚠️ Хмарна консоль |
|
||||
| Gemini CLI | gemini-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ Хмарна консоль |
|
||||
| Антигравітація | антигравітація | OAuth | ✅ | ✅ | ✅ | ✅ Повна квота API |
|
||||
| OpenAI | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Кодекс | openai-відповіді | OAuth | ✅ примусовий | ❌ | ✅ | ✅ Обмеження тарифів |
|
||||
| Копілот GitHub | openai | OAuth + Copilot Token | ✅ | ✅ | ✅ | ✅ Знімки квот |
|
||||
| Курсор | курсор | Власна контрольна сума | ✅ | ✅ | ❌ | ❌ |
|
||||
| Кіро | kiro | AWS SSO OIDC | ✅ (EventStream) | ❌ | ✅ | ✅ Обмеження використання |
|
||||
| Квен | openai | OAuth | ✅ | ✅ | ✅ | ⚠️ За запитом |
|
||||
| iFlow | openai | OAuth (базовий) | ✅ | ✅ | ✅ | ⚠️ За запитом |
|
||||
| OpenRouter | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| GLM/Kimi/MiniMax | Клод | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| DeepSeek | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Groq | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| xAI (Грок) | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Містраль | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Розгубленість | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Разом AI | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Феєрверк AI | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Головний мозок | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Cohere | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
| NVIDIA NIM | openai | Ключ API | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
## Покриття перекладу формату
|
||||
|
||||
Виявлені вихідні формати включають:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
Цільові формати включають:
|
||||
|
||||
- Чат/Відповіді OpenAI
|
||||
- Клод
|
||||
- Gemini/Gemini-CLI/Антигравітаційна оболонка
|
||||
- Кіро
|
||||
- Курсор
|
||||
|
||||
Для перекладу використовується **OpenAI як центральний формат** — усі перетворення проходять через OpenAI як проміжний:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
Переклади вибираються динамічно на основі форми вихідного корисного навантаження та цільового формату постачальника.
|
||||
|
||||
Додаткові рівні обробки в конвеєрі перекладу:
|
||||
|
||||
- **Дезінфікація відповіді** — видаляє нестандартні поля з відповідей у форматі OpenAI (як потокових, так і не потокових), щоб забезпечити сувору відповідність SDK
|
||||
- **Нормалізація ролі** — перетворює `developer` → `system` для цілей, що не є OpenAI; об’єднує `system` → `user` для моделей, які відхиляють системну роль (GLM, ERNIE)
|
||||
- **Вилучення тегів мислення** — аналізує блоки `<think>...</think>` з вмісту в поле `reasoning_content`
|
||||
- **Структурований вихід** — перетворює OpenAI `response_format.json_schema` на `responseMimeType` + `responseSchema` Gemini
|
||||
|
||||
## Підтримувані кінцеві точки API
|
||||
|
||||
| Кінцева точка | Формат | Обробник |
|
||||
| -------------------------------------------------- | --------------------- | ----------------------------------------------------------------- |
|
||||
| `POST /v1/chat/completions` | Чат OpenAI | `src/sse/handlers/chat.ts` |
|
||||
| `POST /v1/messages` | Повідомлення Клода | Той самий обробник (визначено автоматично) |
|
||||
| `POST /v1/responses` | Відповіді OpenAI | `open-sse/handlers/responsesHandler.ts` |
|
||||
| `POST /v1/embeddings` | Вбудовування OpenAI | `open-sse/handlers/embeddings.ts` |
|
||||
| `GET /v1/embeddings` | Список моделей | Маршрут API |
|
||||
| `POST /v1/images/generations` | Зображення OpenAI | `open-sse/handlers/imageGeneration.ts` |
|
||||
| `GET /v1/images/generations` | Список моделей | Маршрут API |
|
||||
| `POST /v1/providers/{provider}/chat/completions` | Чат OpenAI | Виділено для кожного постачальника з перевіркою моделі |
|
||||
| `POST /v1/providers/{provider}/embeddings` | Вбудовування OpenAI | Виділено для кожного постачальника з перевіркою моделі |
|
||||
| `POST /v1/providers/{provider}/images/generations` | Зображення OpenAI | Виділено для кожного постачальника з перевіркою моделі |
|
||||
| `POST /v1/messages/count_tokens` | Клод Токен Підрахунок | Маршрут API |
|
||||
| `GET /v1/models` | Список моделей OpenAI | Маршрут API (чат + вбудовування + зображення + спеціальні моделі) |
|
||||
| `GET /api/models/catalog` | Каталог | Усі моделі згруповані за постачальником + тип |
|
||||
| `POST /v1beta/models/*:streamGenerateContent` | Близнюки рідні | Маршрут API |
|
||||
| `GET/PUT/DELETE /api/settings/proxy` | Конфігурація проксі | Налаштування мережевого проксі |
|
||||
| `POST /api/settings/proxy/test` | З'єднання проксі | Кінцева точка перевірки справності/з’єднання проксі |
|
||||
| `GET/POST/DELETE /api/provider-models` | Спеціальні моделі | Керування індивідуальною моделлю для кожного постачальника |
|
||||
|
||||
## Обхідний обробник
|
||||
|
||||
Обхідний обробник (`open-sse/utils/bypassHandler.ts`) перехоплює відомі запити на «викидання» від Claude CLI — пінг розігріву, вилучення заголовків і підрахунок токенів — і повертає **підроблену відповідь**, не споживаючи токени постачальника вищестоящих даних. Це спрацьовує лише тоді, коли `User-Agent` містить `claude-cli`.
|
||||
|
||||
## Конвеєр реєстратора запитів
|
||||
|
||||
Реєстратор запитів (`open-sse/utils/requestLogger.ts`) забезпечує 7-етапний конвеєр журналювання налагодження, вимкнений за замовчуванням, увімкнений через `ENABLE_REQUEST_LOGS=true`:
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
Файли записуються в `<repo>/logs/<session>/` для кожного сеансу запиту.
|
||||
|
||||
## Режими відмов і стійкість
|
||||
|
||||
## 1) Доступність облікового запису/постачальника
|
||||
|
||||
- час відновлення облікового запису постачальника через тимчасові помилки/помилки швидкості/автентифікації
|
||||
- резервний обліковий запис перед невдалим запитом
|
||||
- резервна комбінована модель, коли поточний шлях моделі/постачальника вичерпано
|
||||
|
||||
## 2) Термін дії маркера
|
||||
|
||||
- попередня перевірка та оновлення з повторною спробою для оновлюваних постачальників
|
||||
- Повторна спроба 401/403 після спроби оновлення в основному шляху
|
||||
|
||||
## 3) Безпека потоку
|
||||
|
||||
- контролер потоку з відключенням
|
||||
- потік перекладу зі змивом у кінці потоку та обробкою `[DONE]`
|
||||
- резервна оцінка використання, якщо метадані використання постачальника відсутні
|
||||
|
||||
## 4) Деградація хмарної синхронізації
|
||||
|
||||
- виникають помилки синхронізації, але локальне виконання продовжується
|
||||
- планувальник має логіку повторної спроби, але періодичне виконання наразі викликає синхронізацію з одноразовою спробою за замовчуванням
|
||||
|
||||
## 5) Цілісність даних
|
||||
|
||||
— Міграція/відновлення форми БД для відсутніх ключів
|
||||
|
||||
- пошкоджені гарантії скидання JSON для localDb і usageDb
|
||||
|
||||
## Спостережливість і робочі сигнали
|
||||
|
||||
Джерела видимості під час виконання:
|
||||
|
||||
- журнали консолі від `src/sse/utils/logger.ts`
|
||||
- сукупні дані про використання за запитом у `usage.json`
|
||||
- текстовий журнал статусу запиту `log.txt`
|
||||
- додаткові глибокі журнали запитів/перекладів під `logs/`, коли `ENABLE_REQUEST_LOGS=true`
|
||||
- кінцеві точки використання інформаційної панелі (`/api/usage/*`) для використання інтерфейсу користувача
|
||||
|
||||
## Чутливі до безпеки межі
|
||||
|
||||
- Секрет JWT (`JWT_SECRET`) захищає перевірку/підпис файлів cookie сеансу інструментальної панелі
|
||||
- Початковий резервний пароль (`INITIAL_PASSWORD`, за замовчуванням `123456`) має бути перевизначений у реальних розгортаннях
|
||||
- Ключ API HMAC Secret (`API_KEY_SECRET`) захищає згенерований локальний формат ключа API
|
||||
- Секрети постачальника (ключі/токени API) зберігаються в локальній БД і повинні бути захищені на рівні файлової системи
|
||||
- Кінцеві точки хмарної синхронізації покладаються на автентику ключа API + семантику ідентифікатора машини
|
||||
|
||||
## Середовище та матриця виконання
|
||||
|
||||
Змінні середовища, які активно використовуються кодом:
|
||||
|
||||
- Додаток/автентифікація: `JWT_SECRET`, `INITIAL_PASSWORD`
|
||||
- Зберігання: `DATA_DIR`
|
||||
- Сумісна поведінка вузла: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- Перевизначення додаткової бази пам’яті (Linux/macOS, коли `DATA_DIR` не встановлено): `XDG_CONFIG_HOME`
|
||||
- Хешування безпеки: `API_KEY_SECRET`, `MACHINE_ID_SALT`
|
||||
- Журнал: `ENABLE_REQUEST_LOGS`
|
||||
- URL-адреси синхронізації/хмари: `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Вихідний проксі: `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` та варіанти в нижньому регістрі
|
||||
- Прапори функції SOCKS5: `ENABLE_SOCKS5_PROXY`, `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- Помічники платформи/виконання (не для конкретної програми): `APPDATA`, `NODE_ENV`, `PORT`, `HOSTNAME`
|
||||
|
||||
## Відомі архітектурні примітки
|
||||
|
||||
1. `usageDb` та `localDb` тепер спільно використовують ту саму базову політику каталогу (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) із переміщенням файлів у старі версії.
|
||||
2. `/api/v1/route.ts` повертає список статичних моделей і не є основним джерелом моделей, яке використовує `/v1/models`.
|
||||
3. Реєстратор запитів записує повні заголовки/тіло, якщо ввімкнено; вважати каталог журналу конфіденційним.
|
||||
4. Поведінка хмари залежить від правильності `NEXT_PUBLIC_BASE_URL` та доступності кінцевої точки хмари.
|
||||
5. Каталог `open-sse/` опубліковано як `@omniroute/open-sse` **пакет робочої області npm**. Вихідний код імпортує його через `@omniroute/open-sse/...` (вирішено Next.js `transpilePackages`). Шляхи до файлів у цьому документі все ще використовують назву каталогу `open-sse/` для узгодженості.
|
||||
6. Діаграми на інформаційній панелі використовують **Recharts** (на основі SVG) для доступної інтерактивної візуалізації аналітики (гістограми використання моделі, таблиці розбивки постачальників із показниками успіху).
|
||||
7. Тести E2E використовують **Playwright** (`tests/e2e/`), запускають через `npm run test:e2e`. У модульних тестах використовується **Node.js Test Runner** (`tests/unit/`), запускається через `npm run test:plan3`. Вихідним кодом під `src/` є **TypeScript** (`.ts`/`.tsx`); робоча область `open-sse/` залишається JavaScript (`.js`).
|
||||
8. Сторінка налаштувань організована на 5 вкладках: Безпека, Маршрутизація (6 глобальних стратегій: спочатку заповнює, циклічна, p2c, випадкова, найменш використовувана, оптимізована за витратами), Стійкість (редаговані обмеження швидкості, автоматичний вимикач, політики), ШІ (бюджет мислення, системна підказка, кеш підказок), Додатково (проксі).
|
||||
|
||||
## Контрольний список операційної перевірки
|
||||
|
||||
- Збірка з джерела: `npm run build`
|
||||
- Створити образ Docker: `docker build -t omniroute .`
|
||||
- Запустіть службу та перевірте:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- Цільова базова URL-адреса CLI має бути `http://<host>:20128/v1`, коли `PORT=20128`
|
||||
589
docs/i18n/uk-UA/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/uk-UA/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# omniroute — Документація кодової бази
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](../pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](../es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](../fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](../it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](../ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](../zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](../de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](../in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](../th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](../uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](../ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](../ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](../vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](../bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](../da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](../fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](../he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](../hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](../id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](../ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](../ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](../nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](../no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](../pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](../ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](../pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](../sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](../sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](../phi/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> Вичерпний, зручний для початківців посібник із **omniroute** багатопровайдерного проксі-маршрутизатора AI.
|
||||
|
||||
---
|
||||
|
||||
## 1. Що таке omniroute?
|
||||
|
||||
omniroute — це **проксі-маршрутизатор**, який знаходиться між клієнтами AI (Claude CLI, Codex, Cursor IDE тощо) та постачальниками AI (Anthropic, Google, OpenAI, AWS, GitHub тощо). Це вирішує одну велику проблему:
|
||||
|
||||
> **Різні клієнти ШІ розмовляють різними «мовами» (форматами API), і різні постачальники ШІ також очікують різних «мов».** omniroute автоматично перекладає між ними.
|
||||
|
||||
Думайте про це як про універсального перекладача в Організації Об’єднаних Націй — будь-який делегат може говорити будь-якою мовою, і перекладач перетворює її для будь-якого іншого делегата.
|
||||
|
||||
---
|
||||
|
||||
## 2. Огляд архітектури
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### Основний принцип: комплексний переклад
|
||||
|
||||
Усі трансляції форматів проходять через **формат OpenAI як центр**:
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
Це означає, що вам потрібно лише **N перекладачів** (по одному на формат) замість **N²** (кожна пара).
|
||||
|
||||
---
|
||||
|
||||
## 3. Структура проекту
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Розбивка по модулях
|
||||
|
||||
### 4.1 Конфігурація (`open-sse/config/`)
|
||||
|
||||
**Єдине джерело правди** для всіх конфігурацій постачальників.
|
||||
|
||||
| Файл | Призначення |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `constants.ts` | Об’єкт `PROVIDERS` з базовими URL-адресами, обліковими даними OAuth (за замовчуванням), заголовками та системними підказками за замовчуванням для кожного постачальника. Також визначає `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG` та `SKIP_PATTERNS`. |
|
||||
| `credentialLoader.ts` | Завантажує зовнішні облікові дані з `data/provider-credentials.json` та об’єднує їх із жорстко запрограмованими параметрами за замовчуванням у `PROVIDERS`. Зберігає секрети поза контролем джерела, зберігаючи зворотну сумісність. |
|
||||
| `providerModels.ts` | Центральний реєстр моделей: псевдоніми постачальників карт → ідентифікатори моделей. Такі функції, як `getModels()`, `getProviderByAlias()`. |
|
||||
| `codexInstructions.ts` | Системні інструкції, введені в запити Codex (обмеження редагування, правила пісочниці, політики затвердження). |
|
||||
| `defaultThinkingSignature.ts` | Стандартні «мислячі» підписи для моделей Claude і Gemini. |
|
||||
| `ollamaModels.ts` | Визначення схеми для локальних моделей Ollama (назва, розмір, сімейство, квантування). |
|
||||
|
||||
#### Потік завантаження облікових даних
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Виконавці (`open-sse/executors/`)
|
||||
|
||||
Виконавці інкапсулюють **специфічну логіку постачальника** за допомогою **шаблону стратегії**. Кожен виконавець замінює базові методи за потреби.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
| Виконавець | Постачальник | Ключові спеціалізації |
|
||||
| ---------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `base.ts` | — | Абстрактна база: створення URL-адреси, заголовки, логіка повтору, оновлення облікових даних |
|
||||
| `default.ts` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Оновлення універсального маркера OAuth для стандартних постачальників |
|
||||
| `antigravity.ts` | Google Cloud Code | Генерація ідентифікатора проекту/сеансу, резервна копія кількох URL-адрес, користувацький аналіз повторної спроби з повідомлень про помилку ("скинути через 2 год. 7 хв. 23 с.") |
|
||||
| `cursor.ts` | Курсор IDE | **Найскладніше**: автентифікація контрольної суми SHA-256, кодування запиту Protobuf, двійковий EventStream → аналіз відповіді SSE |
|
||||
| `codex.ts` | OpenAI Codex | Впроваджує системні інструкції, керує рівнями мислення, видаляє непідтримувані параметри |
|
||||
| `gemini-cli.ts` | Google Gemini CLI | Створення спеціальної URL-адреси (`streamGenerateContent`), оновлення маркера Google OAuth |
|
||||
| `github.ts` | Копілот GitHub | Подвійна система маркерів (GitHub OAuth + маркер Copilot), імітація заголовка VSCode |
|
||||
| `kiro.ts` | AWS CodeWhisperer | Двійковий аналіз AWS EventStream, кадри подій AMZN, оцінка маркерів |
|
||||
| `index.ts` | — | Фабрика: відображає ім’я постачальника → клас виконавця, із резервним варіантом за замовчуванням |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Обробники (`open-sse/handlers/`)
|
||||
|
||||
**Рівень оркестровки** — координує переклад, виконання, потокове передавання та обробку помилок.
|
||||
|
||||
| Файл | Призначення |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `chatCore.ts` | **Центральний оркестр** (~600 рядків). Обробляє повний життєвий цикл запиту: виявлення формату → переклад → відправка виконавця → потокова/непотокова відповідь → оновлення маркера → обробка помилок → журнал використання. |
|
||||
| `responsesHandler.ts` | Адаптер для API відповідей OpenAI: перетворює формат відповідей → Завершення чату → надсилає до `chatCore` → перетворює SSE назад у формат відповідей. |
|
||||
| `embeddings.ts` | Обробник генерації вбудовування: розпізнає модель вбудовування → постачальник, надсилає до API постачальника, повертає відповідь на вбудовування, сумісну з OpenAI. Підтримує 6+ провайдерів. |
|
||||
| `imageGeneration.ts` | Обробник генерації зображень: розпізнає модель зображення → постачальник, підтримує режими, сумісні з OpenAI, Gemini-image (Antigravity) і резервний (Nebius). Повертає base64 або URL-зображення. |
|
||||
|
||||
#### Життєвий цикл запиту (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Послуги (`open-sse/services/`)
|
||||
|
||||
Бізнес-логіка, яка підтримує обробники та виконавці.
|
||||
|
||||
| Файл | Призначення |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `provider.ts` | **Виявлення формату** (`detectFormat`): аналізує структуру тіла запиту, щоб визначити формати Claude/OpenAI/Gemini/Antigravity/Responses (включає `max_tokens` евристику для Claude). Також: створення URL-адрес, створення заголовків, нормалізація конфігурації мислення. Підтримує динамічних постачальників `openai-compatible-*` та `anthropic-compatible-*`. |
|
||||
| `model.ts` | Синтаксичний аналіз рядка моделі (`claude/model-name` → `{provider: "claude", model: "model-name"}`), вирішення псевдонімів із виявленням зіткнень, очищення вхідних даних (відхиляє обхід шляхів/контрольні символи) та вирішення інформації про модель із підтримкою асинхронного засобу отримання псевдонімів. |
|
||||
| `accountFallback.ts` | Обробка ліміту швидкості: експоненціальна віддача (1 с → 2 с → 4 с → макс. 2 хв), керування відновленням облікового запису, класифікація помилок (які помилки викликають відкат, а які ні). |
|
||||
| `tokenRefresh.ts` | Оновлення маркерів OAuth для **кожного постачальника**: Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth). Включає в себе кеш дедуплікації обіцянок у польоті та повторну спробу з експоненціальним відстрочкою. |
|
||||
| `combo.ts` | **Комбіновані моделі**: ланцюжки резервних моделей. Якщо модель A виходить з ладу через помилку, придатну для повернення, спробуйте модель B, потім C тощо. Повертає фактичні коди стану висхідного каналу. |
|
||||
| `usage.ts` | Отримує дані про квоту/використання з API постачальника (квоти GitHub Copilot, квоти моделі Antigravity, обмеження швидкості Codex, аналіз використання Kiro, налаштування Claude). |
|
||||
| `accountSelector.ts` | Інтелектуальний вибір облікового запису з алгоритмом підрахунку балів: враховує пріоритет, стан здоров’я, позицію циклічного циклу та стан відновлення, щоб вибрати оптимальний обліковий запис для кожного запиту. |
|
||||
| `contextManager.ts` | Керування життєвим циклом контексту запиту: створює та відстежує об’єкти контексту кожного запиту з метаданими (ідентифікатор запиту, часові позначки, інформація про постачальника) для налагодження та журналювання. |
|
||||
| `ipFilter.ts` | Контроль доступу на основі IP: підтримує режими білого та чорного списків. Перевіряє IP клієнта на відповідність налаштованим правилам перед обробкою запитів API. |
|
||||
| `sessionManager.ts` | Відстеження сеансу за допомогою відбитків пальців клієнта: відстежує активні сеанси за допомогою хешованих ідентифікаторів клієнта, відстежує кількість запитів і надає показники сеансу. |
|
||||
| `signatureCache.ts` | Кеш дедуплікації на основі підписів запитів: запобігає повторюваним запитам, кешуючи останні підписи запитів і повертаючи кешовані відповіді для ідентичних запитів протягом певного періоду часу. |
|
||||
| `systemPrompt.ts` | Впровадження глобальної системної підказки: додає або додає настроювану системну підказку до всіх запитів із обробкою сумісності для кожного постачальника. |
|
||||
| `thinkingBudget.ts` | Управління бюджетом резонансних токенів: підтримує прохідний, автоматичний (конфігурація розгалуженого мислення), спеціальний (фіксований бюджет) і адаптивний (з урахуванням складності) режими для керування жетонами мислення/міркування. |
|
||||
| `wildcardRouter.ts` | Маршрутизація шаблонів шаблонів підстановки: розв’язує шаблони підстановки (наприклад, `*/claude-*`) до конкретних пар постачальник/модель на основі доступності та пріоритету. |
|
||||
|
||||
#### Дедуплікація оновлення маркера
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### Запасний автомат стану облікового запису
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### Комбінована модель ланцюжка
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Перекладач (`open-sse/translator/`)
|
||||
|
||||
**Система перекладу форматів**, яка використовує систему плагінів із самореєстрацією.
|
||||
|
||||
#### Архітектура
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
| Довідник | Файли | Опис |
|
||||
| ------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `request/` | 8 перекладачів | Перетворюйте тіла запиту між форматами. Кожен файл самостійно реєструється через `register(from, to, fn)` під час імпорту. |
|
||||
| `response/` | 7 перекладачів | Перетворюйте фрагменти потокової відповіді між форматами. Обробляє типи подій SSE, блоки мислення, виклики інструментів. |
|
||||
| `helpers/` | 6 помічників | Спільні утиліти: `claudeHelper` (вилучення системних підказок, конфігурація мислення), `geminiHelper` (відображення частин/вмісту), `openaiHelper` (фільтрування формату), `toolCallHelper` (генерація ідентифікатора, впровадження відсутніх відповідей), `maxTokensHelper`, `responsesApiHelper`. |
|
||||
| `index.ts` | — | Система перекладу: `translateRequest()`, `translateResponse()`, державне управління, реєстр. |
|
||||
| `formats.ts` | — | Константи формату: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
|
||||
|
||||
#### Дизайн ключа: плагіни, що самостійно реєструються
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 Утиліти (`open-sse/utils/`)
|
||||
|
||||
| Файл | Призначення |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `error.ts` | Формування відповіді на помилку (формат, сумісний з OpenAI), синтаксичний аналіз помилок вгорі, вилучення часу повторної спроби Antigravity з повідомлень про помилки, потокова передача помилок SSE. |
|
||||
| `stream.ts` | **SSE Transform Stream** — основний потоковий конвеєр. Два режими: `TRANSLATE` (повноформатний переклад) і `PASSTHROUGH` (нормалізувати + витягнути використання). Керується буферизацією фрагментів, оцінкою використання, відстеженням довжини вмісту. Екземпляри потокового кодера/декодера уникають спільного стану. |
|
||||
| `streamHelpers.ts` | Утиліти SSE низького рівня: `parseSSELine` (толерантний до пробілів), `hasValuableContent` (фільтрує порожні фрагменти для OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (серіалізація SSE з урахуванням формату за допомогою `perf_metrics` очищення). |
|
||||
| `usageTracking.ts` | Видалення використання маркерів із будь-якого формату (Claude/OpenAI/Gemini/Responses), оцінка з окремими співвідношеннями символів на маркер для інструментів/повідомлень, додавання буфера (2000 запасів маркерів), фільтрація полів для певного формату, консольне журналювання з кольорами ANSI. |
|
||||
| `requestLogger.ts` | Реєстрація запитів на основі файлів (увімкніться через `ENABLE_REQUEST_LOGS=true`). Створює папки сеансу з пронумерованими файлами: `1_req_client.json` → `7_res_client.txt`. Весь ввід-вивід є асинхронним (запустив і забув). Маскує чутливі заголовки. |
|
||||
| `bypassHandler.ts` | Перехоплює певні шаблони від Claude CLI (вилучення заголовків, розминка, підрахунок) і повертає фальшиві відповіді без виклику жодного постачальника. Підтримує як потокове, так і не потокове. Навмисно обмежено областю CLI Claude. |
|
||||
| `networkProxy.ts` | Вирішує URL-адресу вихідного проксі-сервера для даного постачальника з пріоритетом: конфігурація для конкретного постачальника → глобальна конфігурація → змінні середовища (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Підтримує виключення `NO_PROXY`. Кеш конфігурації на 30 с. |
|
||||
|
||||
#### SSE Streaming Pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### Структура сеансу реєстратора запитів
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 Рівень програми (`src/`)
|
||||
|
||||
| Довідник | Призначення |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `src/app/` | Веб-інтерфейс користувача, маршрути API, проміжне програмне забезпечення Express, обробники зворотного виклику OAuth |
|
||||
| `src/lib/` | Доступ до бази даних (`localDb.ts`, `usageDb.ts`), автентифікація, спільний |
|
||||
| `src/mitm/` | Проксі-утиліти Man-in-the-middle для перехоплення трафіку провайдера |
|
||||
| `src/models/` | Визначення моделі бази даних |
|
||||
| `src/shared/` | Обгортки навколо функцій open-sse (провайдер, потік, помилка тощо) |
|
||||
| `src/sse/` | Обробники кінцевих точок SSE, які підключають бібліотеку open-sse до експрес-маршрутів |
|
||||
| `src/store/` | Застосування управління станом |
|
||||
|
||||
#### Відомі маршрути API
|
||||
|
||||
| Маршрут | Методи | Призначення |
|
||||
| --------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `/api/provider-models` | GET/POST/DELETE | CRUD для спеціальних моделей на постачальника |
|
||||
| `/api/models/catalog` | ОТРИМАТИ | Зведений каталог усіх моделей (чат, вбудовування, зображення, настроювання), згрупований за постачальником |
|
||||
| `/api/settings/proxy` | GET/PUT/DELETE | Конфігурація ієрархічного вихідного проксі (`global/providers/combos/keys`) |
|
||||
| `/api/settings/proxy/test` | Опублікувати | Перевіряє підключення проксі та повертає загальнодоступну IP-адресу/затримку |
|
||||
| `/v1/providers/[provider]/chat/completions` | Опублікувати | Спеціальне завершення чату для кожного постачальника з перевіркою моделі |
|
||||
| `/v1/providers/[provider]/embeddings` | Опублікувати | Спеціальне вбудовування для кожного постачальника з перевіркою моделі |
|
||||
| `/v1/providers/[provider]/images/generations` | Опублікувати | Спеціальне створення зображень для кожного постачальника з перевіркою моделі |
|
||||
| `/api/settings/ip-filter` | GET/PUT | Керування списком дозволених/чорних IP-адрес |
|
||||
| `/api/settings/thinking-budget` | GET/PUT | Конфігурація бюджету токена міркування (прохідний/автоматичний/спеціальний/адаптивний) |
|
||||
| `/api/settings/system-prompt` | GET/PUT | Глобальна системна підказка для всіх запитів |
|
||||
| `/api/sessions` | ОТРИМАТИ | Відстеження активної сесії та метрики |
|
||||
| `/api/rate-limits` | ОТРИМАТИ | Статус обмеження ставки на обліковий запис |
|
||||
|
||||
---
|
||||
|
||||
## 5. Ключові шаблони проектування
|
||||
|
||||
### 5.1 Переклад Hub-and-Spoke
|
||||
|
||||
Усі формати перекладаються через **формат OpenAI як центр**. Додавання нового постачальника вимагає лише написання **однієї пари** перекладачів (до/з OpenAI), а не N пар.
|
||||
|
||||
### 5.2 Шаблон стратегії виконавця
|
||||
|
||||
Кожен провайдер має спеціальний клас виконавця, успадкований від `BaseExecutor`. Фабрика в `executors/index.ts` вибирає правильний під час виконання.
|
||||
|
||||
### 5.3 Система плагінів із самореєстрацією
|
||||
|
||||
Модулі перекладача реєструються під час імпорту через `register()`. Додавання нового перекладача означає лише створення файлу та його імпорт.
|
||||
|
||||
### 5.4 Резервний обліковий запис із експоненціальним відстрочкою
|
||||
|
||||
Коли постачальник повертає 429/401/500, система може перейти до наступного облікового запису, застосовуючи експоненціальне відновлення (1 с → 2 с → 4 с → макс. 2 хв).
|
||||
|
||||
### Ланцюги комбінованих моделей 5.5
|
||||
|
||||
"Combo" групує кілька рядків `provider/model`. Якщо перший не вдається, автоматично поверніться до наступного.
|
||||
|
||||
### 5.6 Потоковий переклад із збереженням стану
|
||||
|
||||
Трансляція відповіді підтримує стан у блоках SSE (відстеження блоків мислення, накопичення викликів інструментів, індексація блоків вмісту) через механізм `initState()`.
|
||||
|
||||
### 5.7 Буфер безпеки використання
|
||||
|
||||
Буфер на 2000 маркерів додається до звітів про використання, щоб запобігти перевищенню клієнтами обмежень вікон контексту через накладні витрати на системні підказки та переклад формату.
|
||||
|
||||
---
|
||||
|
||||
## 6. Підтримувані формати
|
||||
|
||||
| Формат | Напрям | Ідентифікатор |
|
||||
| ---------------------- | -------------- | ------------------ |
|
||||
| Завершення чату OpenAI | джерело + ціль | `openai` |
|
||||
| OpenAI Responses API | джерело + ціль | `openai-responses` |
|
||||
| Антропний Клод | джерело + ціль | `claude` |
|
||||
| Google Gemini | джерело + ціль | `gemini` |
|
||||
| Google Gemini CLI | тільки мета | `gemini-cli` |
|
||||
| Антигравітація | джерело + ціль | `antigravity` |
|
||||
| AWS Kiro | тільки мета | `kiro` |
|
||||
| Курсор | тільки мета | `cursor` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Підтримувані постачальники
|
||||
|
||||
| Постачальник | Метод авторизації | Виконавець | Ключові примітки |
|
||||
| ------------------------ | ------------------------------- | ---------------- | ------------------------------------------------------------------------- |
|
||||
| Антропний Клод | Ключ API або OAuth | За замовчуванням | Використовує заголовок `x-api-key` |
|
||||
| Google Gemini | Ключ API або OAuth | За замовчуванням | Використовує заголовок `x-goog-api-key` |
|
||||
| Google Gemini CLI | OAuth | GeminiCLI | Використовує кінцеву точку `streamGenerateContent` |
|
||||
| Антигравітація | OAuth | Антигравітація | Резервний варіант із кількома URL-адресами, настроюваний повторний аналіз |
|
||||
| OpenAI | Ключ API | За замовчуванням | Автентифікація стандартного носія |
|
||||
| Кодекс | OAuth | Кодекс | Впроваджує системні інструкції, керує мисленням |
|
||||
| Копілот GitHub | OAuth + маркер Copilot | Github | Подвійний маркер, імітація заголовка VSCode |
|
||||
| Кіро (AWS) | AWS SSO OIDC або Social | Кіро | Розбір двійкового потоку подій |
|
||||
| Курсор IDE | Аутентифікація контрольної суми | Курсор | Кодування Protobuf, контрольні суми SHA-256 |
|
||||
| Квен | OAuth | За замовчуванням | Стандартна авторизація |
|
||||
| iFlow | OAuth (базовий + носій) | За замовчуванням | Заголовок подвійної авторизації |
|
||||
| OpenRouter | Ключ API | За замовчуванням | Автентифікація стандартного носія |
|
||||
| GLM, Kimi, MiniMax | Ключ API | За замовчуванням | Claude-сумісний, використовуйте `x-api-key` |
|
||||
| `openai-compatible-*` | Ключ API | За замовчуванням | Динамічний: будь-яка кінцева точка, сумісна з OpenAI |
|
||||
| `anthropic-compatible-*` | Ключ API | За замовчуванням | Динамічний: будь-яка Claude-сумісна кінцева точка |
|
||||
|
||||
---
|
||||
|
||||
## 8. Підсумок потоку даних
|
||||
|
||||
### Запит на потокове передавання
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### Непотоковий запит
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### Обхідний потік (Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
77
docs/i18n/uk-UA/FEATURES.md
Normal file
77
docs/i18n/uk-UA/FEATURES.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OmniRoute — Галерея функцій приладової панелі
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../FEATURES.md) | 🇧🇷 [Português (Brasil)](../pt-BR/FEATURES.md) | 🇪🇸 [Español](../es/FEATURES.md) | 🇫🇷 [Français](../fr/FEATURES.md) | 🇮🇹 [Italiano](../it/FEATURES.md) | 🇷🇺 [Русский](../ru/FEATURES.md) | 🇨🇳 [中文 (简体)](../zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](../de/FEATURES.md) | 🇮🇳 [हिन्दी](../in/FEATURES.md) | 🇹🇭 [ไทย](../th/FEATURES.md) | 🇺🇦 [Українська](../uk-UA/FEATURES.md) | 🇸🇦 [العربية](../ar/FEATURES.md) | 🇯🇵 [日本語](../ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](../vi/FEATURES.md) | 🇧🇬 [Български](../bg/FEATURES.md) | 🇩🇰 [Dansk](../da/FEATURES.md) | 🇫🇮 [Suomi](../fi/FEATURES.md) | 🇮🇱 [עברית](../he/FEATURES.md) | 🇭🇺 [Magyar](../hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](../id/FEATURES.md) | 🇰🇷 [한국어](../ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](../ms/FEATURES.md) | 🇳🇱 [Nederlands](../nl/FEATURES.md) | 🇳🇴 [Norsk](../no/FEATURES.md) | 🇵🇹 [Português (Portugal)](../pt/FEATURES.md) | 🇷🇴 [Română](../ro/FEATURES.md) | 🇵🇱 [Polski](../pl/FEATURES.md) | 🇸🇰 [Slovenčina](../sk/FEATURES.md) | 🇸🇪 [Svenska](../sv/FEATURES.md) | 🇵🇭 [Filipino](../phi/FEATURES.md)
|
||||
|
||||
Візуальний путівник по кожному розділу інформаційної панелі OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Постачальники
|
||||
|
||||
Керуйте підключеннями постачальників AI: постачальників OAuth (Claude Code, Codex, Gemini CLI), постачальників ключів API (Groq, DeepSeek, OpenRouter) і безкоштовних постачальників (iFlow, Qwen, Kiro).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 Комбо
|
||||
|
||||
Створюйте комбіновані моделі маршрутизації за допомогою 6 стратегій: спочатку заповнюйте, циклічний, вибір двох варіантів, випадковий, найменш використовуваний і оптимізований за витратами. Кожен комбо об’єднує кілька моделей із автоматичним резервним копіюванням.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 Аналітика
|
||||
|
||||
Комплексна аналітика використання із споживанням токенів, оцінками витрат, тепловими картами активності, тижневими діаграмами розподілу та розподілом за постачальниками.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 Здоров'я системи
|
||||
|
||||
Моніторинг у режимі реального часу: безвідмовна робота, пам’ять, версія, процентилі затримки (p50/p95/p99), статистика кешу та стани автоматичного вимикача постачальника.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Ігровий майданчик для перекладачів
|
||||
|
||||
Чотири режими для налагодження перекладів API: **Playground** (конвертер форматів), **Chat Tester** (живі запити), **Test Bench** (пакетні тести) і **Live Monitor** (потік у реальному часі).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Налаштування
|
||||
|
||||
Загальні параметри, системне сховище, керування резервним копіюванням (база даних експорту/імпорту), зовнішній вигляд (темний/світлий режим), безпека (включає захист кінцевих точок API і блокування спеціального постачальника), маршрутизація, стійкість і розширена конфігурація.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Інструменти CLI
|
||||
|
||||
Конфігурація в один клік для інструментів кодування AI: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code та Antigravity.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📝 Журнали запитів
|
||||
|
||||
Реєстрація запитів у режимі реального часу з фільтрацією за постачальником, моделлю, обліковим записом і ключем API. Показує коди стану, використання маркера, затримку та деталі відповіді.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 Кінцева точка API
|
||||
|
||||
Ваша уніфікована кінцева точка API з розподілом можливостей: завершення чату, вбудовування, генерація зображень, зміна рейтингу, транскрипція аудіо та зареєстровані ключі API.
|
||||
|
||||

|
||||
219
docs/i18n/uk-UA/TROUBLESHOOTING.md
Normal file
219
docs/i18n/uk-UA/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Усунення несправностей
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](../pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](../es/TROUBLESHOOTING.md) | 🇫🇷 [Français](../fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](../it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](../ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](../zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](../de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](../in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](../th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](../uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](../ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](../ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](../vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](../bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](../da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](../fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](../he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](../hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](../id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](../ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](../ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](../nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](../no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](../pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](../ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](../pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](../sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](../sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](../phi/TROUBLESHOOTING.md)
|
||||
|
||||
Поширені проблеми та рішення для OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Швидкі виправлення
|
||||
|
||||
| Проблема | Рішення |
|
||||
| -------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| Перший вхід не працює | Перевірте `INITIAL_PASSWORD` в `.env` (за замовчуванням: `123456`) |
|
||||
| Інформаційна панель відкривається на неправильному порту | Установити `PORT=20128` та `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| Немає журналів запитів під `logs/` | Установити `ENABLE_REQUEST_LOGS=true` |
|
||||
| EACCES: у дозволі відмовлено | Установіть `DATA_DIR=/path/to/writable/dir` на заміну `~/.omniroute` |
|
||||
| Стратегія маршрутизації не зберігається | Оновлення до версії 1.4.11+ (виправлення схеми Zod для збереження налаштувань) |
|
||||
|
||||
---
|
||||
|
||||
## Проблеми постачальника
|
||||
|
||||
### "Мовна модель не надавала повідомлень"
|
||||
|
||||
**Причина:** Квота постачальника вичерпана.
|
||||
|
||||
**Виправлення:**
|
||||
|
||||
1. Перевірте трекер квот на інформаційній панелі
|
||||
2. Використовуйте комбінацію з запасними рівнями
|
||||
3. Перейдіть на дешевший/безкоштовний рівень
|
||||
|
||||
### Обмеження швидкості
|
||||
|
||||
**Причина:** Квота підписки вичерпана.
|
||||
|
||||
**Виправлення:**
|
||||
|
||||
- Додати запасний варіант: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- Використовуйте GLM/MiniMax як дешеву резервну копію
|
||||
|
||||
### Маркер OAuth минув
|
||||
|
||||
OmniRoute автоматично оновлює маркери. Якщо проблеми не зникають:
|
||||
|
||||
1. Інформаційна панель → Постачальник → Повторне підключення
|
||||
2. Видаліть і знову додайте підключення провайдера
|
||||
|
||||
---
|
||||
|
||||
## Проблеми з хмарою
|
||||
|
||||
### Помилки хмарної синхронізації
|
||||
|
||||
1. Перевірте, чи `BASE_URL` вказує на ваш запущений екземпляр (наприклад, `http://localhost:20128`)
|
||||
2. Перевірте, чи `CLOUD_URL` вказує на кінцеву точку вашої хмари (наприклад, `https://omniroute.dev`)
|
||||
3. Зберігайте значення `NEXT_PUBLIC_*` у відповідності зі значеннями на стороні сервера
|
||||
|
||||
### Cloud `stream=false` Повертає 500
|
||||
|
||||
**Симптом:** `Unexpected token 'd'...` на хмарній кінцевій точці для непотокових викликів.
|
||||
|
||||
**Причина:** Upstream повертає корисне навантаження SSE, тоді як клієнт очікує JSON.
|
||||
|
||||
**Рішення:** використовуйте `stream=true` для прямих дзвінків із хмари. Місцеве середовище виконання включає SSE→JSON.
|
||||
|
||||
### Хмара повідомляє, що підключено, але "недійсний ключ API"
|
||||
|
||||
1. Створіть новий ключ із локальної інформаційної панелі (`/api/keys`)
|
||||
2. Запустіть хмарну синхронізацію: увімкніть Cloud → Синхронізувати зараз
|
||||
3. Старі/несинхронізовані ключі все ще можуть повертати `401` у хмарі
|
||||
|
||||
---
|
||||
|
||||
## Проблеми Docker
|
||||
|
||||
### Інструмент CLI показує, що не встановлено
|
||||
|
||||
1. Перевірте поля часу виконання: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. Для портативного режиму: використовуйте цільове зображення `runner-cli` (пакет CLI)
|
||||
3. Для режиму монтування хосту: встановіть `CLI_EXTRA_PATHS` і змонтуйте каталог bin хоста як доступний лише для читання
|
||||
4. Якщо `installed=true` та `runnable=false`: двійковий файл знайдено, але перевірка працездатності не пройшла
|
||||
|
||||
### Швидка перевірка часу виконання
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Проблеми з вартістю
|
||||
|
||||
### Високі витрати
|
||||
|
||||
1. Перевірте статистику використання в Інформаційна панель → Використання
|
||||
2. Переключіть основну модель на GLM/MiniMax
|
||||
3. Використовуйте безкоштовний рівень (Gemini CLI, iFlow) для некритичних завдань
|
||||
4. Встановіть бюджет витрат на ключ API: Інформаційна панель → Ключі API → Бюджет
|
||||
|
||||
---
|
||||
|
||||
## Налагодження
|
||||
|
||||
### Увімкнути журнали запитів
|
||||
|
||||
Установіть `ENABLE_REQUEST_LOGS=true` у вашому файлі `.env`. Журнали відображаються в каталозі `logs/`.
|
||||
|
||||
### Перевірити працездатність постачальника
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### Сховище часу виконання
|
||||
|
||||
- Основний стан: `${DATA_DIR}/db.json` (постачальники, комбо, псевдоніми, ключі, налаштування)
|
||||
- Використання: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- Журнали запитів: `<repo>/logs/...` (коли `ENABLE_REQUEST_LOGS=true`)
|
||||
|
||||
---
|
||||
|
||||
## Проблеми з автоматичним вимикачем
|
||||
|
||||
### Постачальник застряг у стані ВІДКРИТО
|
||||
|
||||
Коли автоматичний вимикач постачальника ВІДКРИТО, запити блокуються до закінчення часу відновлення.
|
||||
|
||||
**Виправлення:**
|
||||
|
||||
1. Перейдіть до **Інформаційна панель → Налаштування → Стійкість**
|
||||
2. Перевірте плату автоматичного вимикача для постраждалого постачальника
|
||||
3. Натисніть **Скинути все**, щоб очистити всі вимикачі, або зачекайте, доки закінчиться час відновлення
|
||||
4. Перед скиданням переконайтеся, що постачальник дійсно доступний
|
||||
|
||||
### Постачальник продовжує вмикати автоматичний вимикач
|
||||
|
||||
Якщо постачальник постійно переходить у стан ВІДКРИТО:
|
||||
|
||||
1. Перевірте **Інформаційна панель → Справність → Справність постачальника**, щоб дізнатися про збій
|
||||
2. Перейдіть до **Налаштування → Стійкість → Профілі постачальників** і збільште поріг відмов
|
||||
3. Перевірте, чи постачальник змінив обмеження API або вимагає повторної автентифікації
|
||||
4. Перегляньте телеметрію затримки — висока затримка може спричинити збої, пов’язані з тайм-аутом
|
||||
|
||||
---
|
||||
|
||||
## Проблеми з транскрипцією аудіо
|
||||
|
||||
### Помилка "Непідтримувана модель".
|
||||
|
||||
- Переконайтеся, що ви використовуєте правильний префікс: `deepgram/nova-3` або `assemblyai/best`
|
||||
- Переконайтеся, що постачальник підключено в **Інформаційна панель → Постачальники**
|
||||
|
||||
### Транскрипція повертається порожньою або не вдається
|
||||
|
||||
- Перевірте підтримувані аудіоформати: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
- Переконайтеся, що розмір файлу відповідає обмеженням постачальника (зазвичай < 25 МБ)
|
||||
- Перевірте дійсність ключа API провайдера в картці провайдера
|
||||
|
||||
---
|
||||
|
||||
## Налагодження перекладача
|
||||
|
||||
Використовуйте **Інформаційну панель → Перекладач**, щоб усунути проблеми з перекладом формату:
|
||||
|
||||
| Режим | Коли використовувати |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| **Дитячий майданчик** | Порівняйте формати введення/виведення поруч — вставте невдалий запит, щоб побачити, як він перекладається |
|
||||
| **Тестувальник чату** | Надсилайте живі повідомлення та перевіряйте повне корисне навантаження запитів/відповідей, включаючи заголовки |
|
||||
| **Випробувальний стенд** | Запустіть пакетне тестування комбінацій форматів, щоб знайти, які переклади порушені |
|
||||
| **Живий монітор** | Слідкуйте за потоком запитів у реальному часі, щоб виявити періодичні проблеми з перекладом |
|
||||
|
||||
### Поширені проблеми формату
|
||||
|
||||
- **Теги мислення не відображаються** — перевірте, чи підтримує цільовий постачальник мислення та налаштування бюджету мислення
|
||||
- **Відмова від викликів інструментів** — деякі переклади форматів можуть видаляти непідтримувані поля; перевірити в режимі Playground
|
||||
- **Відсутня системна підказка** — Клод і Близнюки по-різному обробляють системні підказки; перевірити результат перекладу
|
||||
- **SDK повертає необроблений рядок замість об’єкта** — Виправлено у версії 1.1.0: дезінфікуючий засіб відповіді тепер видаляє нестандартні поля (`x_groq`, `usage_breakdown` тощо), які викликають помилки підтвердження OpenAI SDK Pydantic
|
||||
- **GLM/ERNIE відхиляє роль `system`** — Виправлено у версії 1.1.0: нормалізатор ролі автоматично об’єднує системні повідомлення в повідомлення користувача для несумісних моделей
|
||||
- **`developer` роль не розпізнається** — Виправлено у версії 1.1.0: автоматично конвертовано в `system` для постачальників, які не є OpenAI
|
||||
- **`json_schema` не працює з Gemini** — Виправлено у версії 1.1.0: `response_format` тепер перетворено на `responseMimeType` + `responseSchema` Gemini
|
||||
|
||||
---
|
||||
|
||||
## Налаштування стійкості
|
||||
|
||||
### Автоматичне обмеження швидкості не спрацьовує
|
||||
|
||||
- Автоматичне обмеження швидкості стосується лише постачальників ключів API (не OAuth/підписки)
|
||||
- Переконайтеся, що **Налаштування → Стійкість → Профілі постачальників** увімкнено автоматичне обмеження швидкості
|
||||
- Перевірте, чи повертає постачальник коди статусу `429` або заголовки `Retry-After`
|
||||
|
||||
### Налаштування експоненціального відступу
|
||||
|
||||
Профілі постачальників підтримують такі налаштування:
|
||||
|
||||
- **Базова затримка** — початковий час очікування після першої помилки (за замовчуванням: 1 с)
|
||||
- **Макс. затримка** — обмеження максимального часу очікування (за замовчуванням: 30 с)
|
||||
- **Множник** — скільки збільшити затримку на послідовну помилку (за замовчуванням: 2x)
|
||||
|
||||
### Протигромове стадо
|
||||
|
||||
Коли багато одночасних запитів надходять до постачальника з обмеженою швидкістю, OmniRoute використовує м’ютекс + автоматичне обмеження швидкості для серіалізації запитів і запобігання каскадним помилкам. Це відбувається автоматично для постачальників ключів API.
|
||||
|
||||
---
|
||||
|
||||
## Все ще застрягли?
|
||||
|
||||
- **Проблеми GitHub**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **Архітектура**: див. [**OMNI_TOKEN_55**](ARCHITECTURE.md) для внутрішніх деталей
|
||||
- **API Reference**: див. [**OMNI_TOKEN_56**](API_REFERENCE.md) для всіх кінцевих точок
|
||||
- **Інформаційна панель справності**: перевірте **Інформаційна панель → Здоров’я**, щоб дізнатися про стан системи в реальному часі
|
||||
- **Перекладач**: використовуйте **Інформаційна панель → Перекладач** для усунення проблем із форматом
|
||||
698
docs/i18n/uk-UA/USER_GUIDE.md
Normal file
698
docs/i18n/uk-UA/USER_GUIDE.md
Normal file
@@ -0,0 +1,698 @@
|
||||
# Керівництво користувача
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](../es/USER_GUIDE.md) | 🇫🇷 [Français](../fr/USER_GUIDE.md) | 🇮🇹 [Italiano](../it/USER_GUIDE.md) | 🇷🇺 [Русский](../ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](../de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](../in/USER_GUIDE.md) | 🇹🇭 [ไทย](../th/USER_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](../ar/USER_GUIDE.md) | 🇯🇵 [日本語](../ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/USER_GUIDE.md) | 🇧🇬 [Български](../bg/USER_GUIDE.md) | 🇩🇰 [Dansk](../da/USER_GUIDE.md) | 🇫🇮 [Suomi](../fi/USER_GUIDE.md) | 🇮🇱 [עברית](../he/USER_GUIDE.md) | 🇭🇺 [Magyar](../hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/USER_GUIDE.md) | 🇰🇷 [한국어](../ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](../nl/USER_GUIDE.md) | 🇳🇴 [Norsk](../no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/USER_GUIDE.md) | 🇷🇴 [Română](../ro/USER_GUIDE.md) | 🇵🇱 [Polski](../pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/USER_GUIDE.md) | 🇸🇪 [Svenska](../sv/USER_GUIDE.md) | 🇵🇭 [Filipino](../phi/USER_GUIDE.md)
|
||||
|
||||
Повний посібник із налаштування постачальників, створення комбінацій, інтеграції інструментів CLI та розгортання OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Зміст
|
||||
|
||||
- [Pricing at a Glance](#-pricing-at-a-glance)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Provider Setup](#-provider-setup)
|
||||
- [CLI Integration](#-cli-integration)
|
||||
- [Deployment](#-deployment)
|
||||
- [Available Models](#-available-models)
|
||||
- [Advanced Features](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 Короткий огляд цін
|
||||
|
||||
| Рівень | Постачальник | Вартість | Скидання квоти | Найкраще для |
|
||||
| ------------------ | ---------------- | ------------------------ | ----------------------------- | --------------------------- |
|
||||
| **💳 ПІДПИСКА** | Клод Код (Pro) | 20 доларів США на місяць | 5 годин + щотижня | Вже підписані |
|
||||
| | Codex (Plus/Pro) | $20-200/міс | 5 годин + щотижня | Користувачі OpenAI |
|
||||
| | Gemini CLI | **БЕЗКОШТОВНО** | 180 тис./місяць + 1 тис./день | всі! |
|
||||
| | Копілот GitHub | $10-19/міс | Щомісяця | Користувачі GitHub |
|
||||
| **🔑 КЛЮЧ API** | DeepSeek | Оплата за використання | Жодного | Дешеві міркування |
|
||||
| | Groq | Оплата за використання | Жодного | Надшвидкий висновок |
|
||||
| | xAI (Грок) | Оплата за використання | Жодного | Грок 4 міркування |
|
||||
| | Містраль | Оплата за використання | Жодного | Моделі, розміщені в ЄС |
|
||||
| | Розгубленість | Оплата за використання | Жодного | Search-augmented |
|
||||
| | Разом AI | Оплата за використання | Жодного | Моделі з відкритим кодом |
|
||||
| | Феєрверк AI | Оплата за використання | Жодного | Швидкі зображення FLUX |
|
||||
| | Головний мозок | Оплата за використання | Жодного | Швидкість вафельної шкали |
|
||||
| | Cohere | Оплата за використання | Жодного | Команда R+ RAG |
|
||||
| | NVIDIA NIM | Оплата за використання | Жодного | Моделі підприємства |
|
||||
| **💰 ДЕШЕВО** | GLM-4.7 | $0,6/1 млн | Щодня о 10 ранку | Резервне копіювання бюджету |
|
||||
| | MiniMax M2.1 | $0,2/1 млн | 5-годинний роликовий | Найдешевший варіант |
|
||||
| | Кімі К2 | 9 $/міс квартира | 10 млн токенів/міс | Передбачувана вартість |
|
||||
| **🆓 БЕЗКОШТОВНО** | iFlow | $0 | Необмежений | 8 моделей безкоштовно |
|
||||
| | Квен | $0 | Необмежений | 3 моделі безкоштовно |
|
||||
| | Кіро | $0 | Необмежений | Клод безкоштовно |
|
||||
|
||||
**💡 Порада професіонала:** Почніть із Gemini CLI (180 тис. безкоштовно/місяць) + iFlow (необмежено безкоштовно) = 0 доларів США!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Випадки використання
|
||||
|
||||
### Випадок 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: "Я хочу нульову вартість"
|
||||
|
||||
**Проблема:** не можу дозволити собі підписку, потрібне надійне кодування ШІ
|
||||
|
||||
```
|
||||
Combo: "free-forever"
|
||||
1. gc/gemini-3-flash (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited free)
|
||||
3. qw/qwen3-coder-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. minimax/MiniMax-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»
|
||||
|
||||
**Проблема:** потрібен помічник штучного інтелекту в програмах для обміну повідомленнями, повністю безкоштовний
|
||||
|
||||
```
|
||||
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...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Налаштування постачальника
|
||||
|
||||
### 🔐 Постачальники підписки
|
||||
|
||||
#### 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 для швидкості. OmniRoute відстежує квоту на модель!
|
||||
|
||||
#### OpenAI Codex (Plus/Pro)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/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
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ Monthly reset (1st of month)
|
||||
|
||||
Models:
|
||||
gh/gpt-5
|
||||
gh/claude-4.5-sonnet
|
||||
gh/gemini-3-pro
|
||||
```
|
||||
|
||||
### 💰 Дешеві постачальники
|
||||
|
||||
#### GLM-4.7 (щоденне скидання, $0,6/1 млн)
|
||||
|
||||
1. Зареєструйтеся: [Zhipu AI](https://open.bigmodel.cn/)
|
||||
2. Отримайте ключ API від Coding Plan
|
||||
3. Інформаційна панель → Додати ключ API: Постачальник: `glm`, ключ API: `your-key`
|
||||
|
||||
**Використання:** `glm/glm-4.7` — **Порада професіонала:** План кодування пропонує 3× квоту за 1/7 вартості! Скидання щодня о 10:00.
|
||||
|
||||
#### MiniMax M2.1 (5 годин скидання, $0,20/1 млн)
|
||||
|
||||
1. Зареєструйтеся: [MiniMax](https://www.minimax.io/)
|
||||
2. Отримати ключ API → Інформаційна панель → Додати ключ API
|
||||
|
||||
**Використовуйте:** `minimax/MiniMax-M2.1` — **Порада:** Найдешевший варіант для довгого контексту (1 млн токенів)!
|
||||
|
||||
#### Kimi K2 ($9/місяць)
|
||||
|
||||
1. Підпишіться: [Moonshot AI](https://platform.moonshot.ai/)
|
||||
2. Отримати ключ API → Інформаційна панель → Додати ключ API
|
||||
|
||||
**Використання:** `kimi/kimi-latest` — **Порада професіонала:** Фіксовані 9 доларів США на місяць за 10 мільйонів токенів = 0,90 доларів США за 1 млн. ефективних витрат!
|
||||
|
||||
### 🆓 БЕЗКОШТОВНІ постачальники
|
||||
|
||||
#### iFlow (8 БЕЗКОШТОВНИХ моделей)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect 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 auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### Кіро (Клод БЕЗКОШТОВНО)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
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/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### Приклад 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
|
||||
|
||||
### Курсор IDE
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### Клод Код
|
||||
|
||||
Редагувати `~/.claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### OpenClaw
|
||||
|
||||
Редагувати `~/.openclaw/openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Або скористайтеся інформаційною панеллю:** Інструменти CLI → OpenClaw → Auto-config
|
||||
|
||||
### Cline / Продовжити / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Розгортання
|
||||
|
||||
### Розгортання VPS
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### Докер
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
Для інтегрованого режиму з двійковими файлами CLI дивіться розділ Docker в основних документах.
|
||||
|
||||
### Змінні середовища
|
||||
|
||||
| Змінна | За замовчуванням | Опис |
|
||||
| --------------------- | ------------------------------------ | -------------------------------------------------------------------- |
|
||||
| `JWT_SECRET` | `omniroute-default-secret-change-me` | Секрет підпису JWT (**зміни у виробництві**) |
|
||||
| `INITIAL_PASSWORD` | `123456` | Перший пароль для входу |
|
||||
| `DATA_DIR` | `~/.omniroute` | Каталог даних (база даних, використання, журнали) |
|
||||
| `PORT` | рамка за замовчуванням | Сервісний порт (`20128` у прикладах) |
|
||||
| `HOSTNAME` | рамка за замовчуванням | Прив’язати хост (Docker за замовчуванням `0.0.0.0`) |
|
||||
| `NODE_ENV` | виконання за замовчуванням | Установіть `production` для розгортання |
|
||||
| `BASE_URL` | `http://localhost:20128` | Внутрішня базова URL-адреса на стороні сервера |
|
||||
| `CLOUD_URL` | `https://omniroute.dev` | Базова URL-адреса кінцевої точки хмарної синхронізації |
|
||||
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | Секрет HMAC для згенерованих ключів API |
|
||||
| `REQUIRE_API_KEY` | `false` | Примусово застосувати ключ API носія на `/v1/*` |
|
||||
| `ENABLE_REQUEST_LOGS` | `false` | Вмикає журнали запитів/відповідей |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | Примусово `Secure` cookie автентифікації (за зворотним проксі HTTPS) |
|
||||
|
||||
Повну довідку про змінні середовища див. у [README](../README.md).
|
||||
|
||||
---
|
||||
|
||||
## 📊 Доступні моделі
|
||||
|
||||
<details>
|
||||
<summary><b>Переглянути всі доступні моделі</b></summary>
|
||||
|
||||
**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 (`gh/`)**: `gh/gpt-5`, `gh/claude-4.5-sonnet`
|
||||
|
||||
**GLM (`glm/`)** — $0,6/1 млн.: `glm/glm-4.7`
|
||||
|
||||
**MiniMax (`minimax/`)** — $0,2/1 млн.: `minimax/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`
|
||||
|
||||
**DeepSeek (`ds/`)**: `ds/deepseek-chat`, `ds/deepseek-reasoner`
|
||||
|
||||
**Groq (`groq/`)**: `groq/llama-3.3-70b-versatile`, `groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI (`xai/`)**: `xai/grok-4`, `xai/grok-4-0709-fast-reasoning`, `xai/grok-code-mini`
|
||||
|
||||
**Містраль (`mistral/`)**: `mistral/mistral-large-2501`, `mistral/codestral-2501`
|
||||
|
||||
**Нерозуміння (`pplx/`)**: `pplx/sonar-pro`, `pplx/sonar`
|
||||
|
||||
**Разом AI (`together/`)**: `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**Fireworks AI (`fireworks/`)**: `fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**Cerebras (`cerebras/`)**: `cerebras/llama-3.3-70b`
|
||||
|
||||
**Cohere (`cohere/`)**: `cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM (`nvidia/`)**: `nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Розширені функції
|
||||
|
||||
### Спеціальні моделі
|
||||
|
||||
Додайте будь-який ідентифікатор моделі до будь-якого постачальника, не чекаючи оновлення програми:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
Або скористайтеся інформаційною панеллю: **Постачальники → [Постачальник] → Спеціальні моделі**.
|
||||
|
||||
### Виділені маршрути постачальників
|
||||
|
||||
Направляйте запити безпосередньо до конкретного постачальника з перевіркою моделі:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
Префікс провайдера додається автоматично, якщо його немає. Невідповідні моделі повертають `400`.
|
||||
|
||||
### Конфігурація мережевого проксі
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**Пріоритет:** Специфічний ключ → Специфічний комбінований → Специфічний постачальник → Глобальний → Середовище.
|
||||
|
||||
### API каталогу моделей
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
Повертає моделі, згруповані за постачальниками з типами (`chat`, `embedding`, `image`).
|
||||
|
||||
### Хмарна синхронізація
|
||||
|
||||
- Синхронізація постачальників, комбінацій і налаштувань на всіх пристроях
|
||||
- Автоматична фонова синхронізація з тайм-аутом + швидка відмова
|
||||
- Віддавайте перевагу серверним `BASE_URL`/`CLOUD_URL` у виробництві
|
||||
|
||||
### LLM Gateway Intelligence (Phase 9)
|
||||
|
||||
- **Семантичний кеш** — автоматично кешує непотокові відповіді, температура=0 (обхід за допомогою `X-OmniRoute-No-Cache: true`)
|
||||
- **Request Idempotency** — Дедуплікує запити протягом 5 секунд через заголовок `Idempotency-Key` або `X-Request-Id`
|
||||
- **Відстеження прогресу** — підключення до SSE `event: progress` через заголовок `X-OmniRoute-Progress: true`
|
||||
|
||||
---
|
||||
|
||||
### Ігровий майданчик для перекладачів
|
||||
|
||||
Доступ через **Інформаційна панель → Перекладач**. Налагодьте та візуалізуйте, як OmniRoute перекладає запити API між постачальниками.
|
||||
|
||||
| Режим | Призначення |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| **Дитячий майданчик** | Виберіть вихідний/цільовий формати, вставте запит і миттєво перегляньте перекладений результат |
|
||||
| **Тестувальник чату** | Надсилайте повідомлення чату через проксі та перевіряйте повний цикл запитів/відповідей |
|
||||
| **Випробувальний стенд** | Виконайте пакетні тести для кількох комбінацій форматів, щоб перевірити правильність перекладу |
|
||||
| **Живий монітор** | Переглядайте переклади в реальному часі, коли запити проходять через проксі |
|
||||
|
||||
**Приклади використання:**
|
||||
|
||||
- Налагодження причин невдачі певної комбінації клієнт/постачальник
|
||||
- Переконайтеся, що теги мислення, виклики інструментів і системні підказки перекладаються правильно
|
||||
- Порівняйте відмінності форматів між форматами OpenAI, Claude, Gemini та Responses API
|
||||
|
||||
---
|
||||
|
||||
### Стратегії маршрутизації
|
||||
|
||||
Налаштувати через **Інформаційна панель → Налаштування → Маршрутизація**.
|
||||
|
||||
| Стратегія | Опис |
|
||||
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Спочатку заповніть** | Використовує облікові записи в пріоритетному порядку — основний обліковий запис обробляє всі запити, поки не стане доступним |
|
||||
| **Кругова система** | Переглядає всі облікові записи з настроюваним лімітом (за замовчуванням: 3 виклики на обліковий запис) |
|
||||
| **P2C (Power of Two Choices)** | Вибирає 2 випадкові облікові записи та направляє до більш здорового — балансує навантаження з усвідомленням здоров’я |
|
||||
| **Випадкове** | Випадково вибирає обліковий запис для кожного запиту за допомогою перемішування Фішера-Єйтса |
|
||||
| **Найменш використовуваний** | Маршрути до облікового запису з найстарішою міткою часу `lastUsedAt`, рівномірно розподіляючи трафік |
|
||||
| **Оптимізація вартості** | Маршрути до облікового запису з найнижчим значенням пріоритету, оптимізуючи для найнижчих постачальників |
|
||||
|
||||
#### Псевдоніми моделі підстановки
|
||||
|
||||
Створіть шаблони символів підстановки, щоб змінити назви моделей:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
Символи підстановки підтримують `*` (будь-які символи) і `?` (один символ).
|
||||
|
||||
#### Резервні ланцюги
|
||||
|
||||
Визначте глобальні резервні ланцюжки, які застосовуються до всіх запитів:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Стійкість і автоматичні вимикачі
|
||||
|
||||
Налаштуйте за допомогою **Інформаційна панель → Налаштування → Стійкість**.
|
||||
|
||||
OmniRoute реалізує стійкість на рівні постачальника за допомогою чотирьох компонентів:
|
||||
|
||||
1. **Профілі постачальників** — конфігурація кожного постачальника для:
|
||||
- Поріг відмови (кількість відмов до відкриття)
|
||||
- Тривалість відновлення
|
||||
- Чутливість визначення межі швидкості
|
||||
- Експоненціальні параметри відставання
|
||||
|
||||
2. **Обмеження швидкості, які можна редагувати** — параметри системного рівня, які можна налаштувати на інформаційній панелі:
|
||||
- **Запитів за хвилину (RPM)** — максимальна кількість запитів за хвилину на обліковий запис
|
||||
- **Мінімальний час між запитами** — мінімальний проміжок у мілісекундах між запитами
|
||||
- **Max Concurrent Requests** — максимальна кількість одночасних запитів на обліковий запис
|
||||
- Натисніть **Редагувати**, щоб змінити, потім **Зберегти** або **Скасувати**. Значення зберігаються через API стійкості.
|
||||
|
||||
3. **Circuit Breaker** — відстежує збої кожного постачальника та автоматично розмикає ланцюг, коли досягається порогове значення:
|
||||
- **ЗАКРИТО** (справний) — запити надходять нормально
|
||||
- **OPEN** — Провайдер тимчасово заблоковано після повторних збоїв
|
||||
- **HALF_OPEN** — Перевірка, якщо провайдер відновився
|
||||
|
||||
4. **Політики та заблоковані ідентифікатори** — показує статус автоматичного вимикача та заблоковані ідентифікатори з можливістю примусового розблокування.
|
||||
|
||||
5. **Автовизначення ліміту швидкості** — відстежує заголовки `429` та `Retry-After`, щоб завчасно уникнути перевищення лімітів швидкості постачальника.
|
||||
|
||||
**Порада:** Використовуйте кнопку **Скинути все**, щоб очистити всі автоматичні вимикачі та часи відновлення, коли постачальник відновиться після збою.
|
||||
|
||||
---
|
||||
|
||||
### Експорт/імпорт бази даних
|
||||
|
||||
Керуйте резервними копіями бази даних у **Інформаційна панель → Налаштування → Система та сховище**.
|
||||
|
||||
| Дія | Опис |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Експорт бази даних** | Завантажує поточну базу даних SQLite як файл `.sqlite` |
|
||||
| **Експортувати все (.tar.gz)** | Завантажує повний резервний архів, включаючи: базу даних, налаштування, комбінації, з’єднання провайдера (без облікових даних), метадані ключа API |
|
||||
| **Імпорт бази даних** | Завантажте файл `.sqlite`, щоб замінити поточну базу даних. Автоматично створюється резервна копія перед імпортом |
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**Перевірка імпорту:** Імпортований файл перевіряється на цілісність (перевірка прагми SQLite), необхідні таблиці (`provider_connections`, `provider_nodes`, `combos`, `api_keys`) і розмір (макс. 100 МБ).
|
||||
|
||||
**Випадки використання:**
|
||||
|
||||
- Перенесення OmniRoute між машинами
|
||||
- Створення зовнішніх резервних копій для аварійного відновлення
|
||||
- Спільний доступ до конфігурацій між членами команди (експортувати все → надати доступ до архіву)
|
||||
|
||||
---
|
||||
|
||||
### Інформаційна панель налаштувань
|
||||
|
||||
Для зручності навігації сторінка налаштувань складається з 5 вкладок:
|
||||
|
||||
| Вкладка | Зміст |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Безпека** | Налаштування логіна/пароля, контроль IP-доступу, авторизація API для `/models` та блокування постачальника |
|
||||
| **Маршрутизація** | Глобальна стратегія маршрутизації (6 варіантів), псевдоніми моделей із підстановкою, резервні ланцюжки, комбіновані параметри за замовчуванням |
|
||||
| **Стійкість** | Профілі постачальників, обмеження швидкості, які можна редагувати, статус автоматичного вимикача, політики та заблоковані ідентифікатори |
|
||||
| **AI** | Продумана конфігурація бюджету, впровадження глобальної системної підказки, швидка статистика кешу |
|
||||
| **Розширений** | Глобальна конфігурація проксі (HTTP/SOCKS5) |
|
||||
|
||||
---
|
||||
|
||||
### Управління витратами та бюджетом
|
||||
|
||||
Доступ через **Інформаційна панель → Витрати**.
|
||||
|
||||
| Вкладка | Призначення |
|
||||
| ---------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Бюджет** | Встановіть ліміти витрат на ключ API за допомогою щоденних/тижневих/місячних бюджетів і відстеження в реальному часі |
|
||||
| **Ціни** | Перегляд і редагування записів моделі ціноутворення — вартість 1 тис. токенів вводу/виводу на постачальника |
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**Відстеження вартості:** кожен запит реєструє використання токенів і розраховує вартість за допомогою таблиці цін. Перегляньте розбивку в **Інформаційна панель → Використання** за постачальником, моделлю та ключем API.
|
||||
|
||||
---
|
||||
|
||||
### Транскрипція аудіо
|
||||
|
||||
OmniRoute підтримує транскрипцію аудіо через кінцеву точку, сумісну з OpenAI:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
Доступні постачальники: **Deepgram** (`deepgram/`), **AssemblyAI** (`assemblyai/`).
|
||||
|
||||
Підтримувані аудіоформати: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
### Комбіновані стратегії балансування
|
||||
|
||||
Налаштуйте балансування за комбо в **Інформаційна панель → Комбо → Створити/Редагувати → Стратегія**.
|
||||
|
||||
| Стратегія | Опис |
|
||||
| ----------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| **Кругова система** | Обертає моделі послідовно |
|
||||
| **Пріоритет** | Завжди пробує першу модель; повертається лише в разі помилки |
|
||||
| **Випадкове** | Вибирає випадкову модель із комбо для кожного запиту |
|
||||
| **Зважений** | Маршрути пропорційно на основі призначеної ваги для моделі |
|
||||
| **Найменш використовуваний** | Маршрути до моделі з найменшою кількістю останніх запитів (використовує комбіновані показники) |
|
||||
| **Оптимізовано за витратами** | Маршрути до найдешевшої доступної моделі (використовується таблиця цін) |
|
||||
|
||||
Глобальні стандартні параметри комбінованих маршрутів можна встановити в **Інформаційна панель → Налаштування → Маршрутизація → Стандартні параметри комбінованих маршрутів**.
|
||||
|
||||
---
|
||||
|
||||
### Інформаційна панель здоров'я
|
||||
|
||||
Доступ через **Інформаційна панель → Здоров’я**. Огляд стану системи в реальному часі з 6 картками:
|
||||
|
||||
| Картка | Що це показує |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| **Стан системи** | Час роботи, версія, використання пам’яті, каталог даних |
|
||||
| **Здоров’я постачальника** | Стан автоматичного вимикача для кожного постачальника (замкнуто/розімкнуто/напіврозімкнуто) |
|
||||
| **Обмеження швидкості** | Обмеження активної швидкості перезарядки на обліковий запис із часом, що залишився |
|
||||
| **Активні блокування** | Провайдери, тимчасово заблоковані політикою блокування |
|
||||
| **Кеш підпису** | Статистика кешу дедуплікації (активні ключі, частота звернень) |
|
||||
| **Телеметрія затримки** | Агрегація затримок p50/p95/p99 для кожного провайдера |
|
||||
|
||||
**Професійна порада.** Сторінка «Здоров’я» автоматично оновлюється кожні 10 секунд. Використовуйте картку автоматичного вимикача, щоб визначити, які постачальники мають проблеми.
|
||||
441
docs/i18n/vi/API_REFERENCE.md
Normal file
441
docs/i18n/vi/API_REFERENCE.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# Tham chiếu API
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
|
||||
|
||||
Tham chiếu đầy đủ cho tất cả các điểm cuối API OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Mục lục
|
||||
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Semantic Cache](#semantic-cache)
|
||||
- [Dashboard & Management](#dashboard--management)
|
||||
- [Request Processing](#request-processing)
|
||||
- [Authentication](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## Hoàn thành cuộc trò chuyện
|
||||
|
||||
```bash
|
||||
POST /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
|
||||
}
|
||||
```
|
||||
|
||||
### Tiêu đề tùy chỉnh
|
||||
|
||||
| Tiêu đề | Hướng | Mô tả |
|
||||
| ------------------------ | -------- | ----------------------------------------------- |
|
||||
| `X-OmniRoute-No-Cache` | Yêu cầu | Đặt thành `true` để bỏ qua bộ đệm |
|
||||
| `X-OmniRoute-Progress` | Yêu cầu | Đặt thành `true` cho các sự kiện tiến trình |
|
||||
| `Idempotency-Key` | Yêu cầu | Khóa khấu trừ (cửa sổ 5s) |
|
||||
| `X-Request-Id` | Yêu cầu | Khóa khấu trừ thay thế |
|
||||
| `X-OmniRoute-Cache` | Phản hồi | `HIT` hoặc `MISS` (không phát trực tuyến) |
|
||||
| `X-OmniRoute-Idempotent` | Phản hồi | `true` nếu được loại bỏ trùng lặp |
|
||||
| `X-OmniRoute-Progress` | Phản hồi | `enabled` nếu bật tính năng theo dõi tiến trình |
|
||||
|
||||
---
|
||||
|
||||
## Nhúng
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
Các nhà cung cấp hiện có: Nebius, OpenAI, Mistral, Together AI, Fireworks, NVIDIA.
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tạo hình ảnh
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
Các nhà cung cấp hiện có: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI.
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Danh sách mô hình
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Điểm cuối tương thích
|
||||
|
||||
| Phương pháp | Đường dẫn | Định dạng |
|
||||
| ----------- | --------------------------- | ---------------------- |
|
||||
| ĐĂNG | `/v1/chat/completions` | OpenAI |
|
||||
| ĐĂNG | `/v1/messages` | Nhân chủng học |
|
||||
| ĐĂNG | `/v1/responses` | Phản hồi OpenAI |
|
||||
| ĐĂNG | `/v1/embeddings` | OpenAI |
|
||||
| ĐĂNG | `/v1/images/generations` | OpenAI |
|
||||
| NHẬN | `/v1/models` | OpenAI |
|
||||
| ĐĂNG | `/v1/messages/count_tokens` | Nhân chủng học |
|
||||
| NHẬN | `/v1beta/models` | Song Tử |
|
||||
| ĐĂNG | `/v1beta/models/{...path}` | Gemini generateContent |
|
||||
| ĐĂNG | `/v1/api/chat` | Olama |
|
||||
|
||||
### Tuyến đường dành riêng cho nhà cung cấp
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
Tiền tố nhà cung cấp được tự động thêm vào nếu thiếu. Các mô hình không khớp trả về `400`.
|
||||
|
||||
---
|
||||
|
||||
## Bộ đệm ngữ nghĩa
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
Ví dụ phản hồi:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bảng điều khiển & Quản lý
|
||||
|
||||
### Xác thực
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ----------------------------- | ----------- | ---------------------------- |
|
||||
| `/api/auth/login` | ĐĂNG | Đăng nhập |
|
||||
| `/api/auth/logout` | ĐĂNG | Đăng xuất |
|
||||
| `/api/settings/require-login` | NHẬN/ĐẶT | Chuyển đổi yêu cầu đăng nhập |
|
||||
|
||||
### Quản lý nhà cung cấp
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ---------------------------- | ------------- | ------------------------------- |
|
||||
| `/api/providers` | NHẬN/ĐĂNG | Liệt kê/tạo nhà cung cấp |
|
||||
| `/api/providers/[id]` | NHẬN/ĐẶT/XÓA | Quản lý nhà cung cấp |
|
||||
| `/api/providers/[id]/test` | ĐĂNG | Kết nối nhà cung cấp thử nghiệm |
|
||||
| `/api/providers/[id]/models` | NHẬN | Liệt kê mô hình nhà cung cấp |
|
||||
| `/api/providers/validate` | ĐĂNG | Xác thực cấu hình nhà cung cấp |
|
||||
| `/api/provider-nodes*` | Khác nhau | Quản lý nút nhà cung cấp |
|
||||
| `/api/provider-models` | NHẬN/ĐĂNG/XÓA | Mô hình tùy chỉnh |
|
||||
|
||||
### Luồng OAuth
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| -------------------------------- | ----------- | --------------------------------- |
|
||||
| `/api/oauth/[provider]/[action]` | Khác nhau | OAuth dành riêng cho nhà cung cấp |
|
||||
|
||||
### Định tuyến & Cấu hình
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------------- | ----------- | ------------------------------------------- |
|
||||
| `/api/models/alias` | NHẬN/ĐĂNG | Bí danh mẫu |
|
||||
| `/api/models/catalog` | NHẬN | Tất cả các mô hình theo nhà cung cấp + loại |
|
||||
| `/api/combos*` | Khác nhau | Quản lý kết hợp |
|
||||
| `/api/keys*` | Khác nhau | Quản lý khóa API |
|
||||
| `/api/pricing` | NHẬN | Giá mẫu |
|
||||
|
||||
### Cách sử dụng & Phân tích
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------------------- | ----------- | ---------------------------- |
|
||||
| `/api/usage/history` | NHẬN | Lịch sử sử dụng |
|
||||
| `/api/usage/logs` | NHẬN | Nhật ký sử dụng |
|
||||
| `/api/usage/request-logs` | NHẬN | Nhật ký cấp yêu cầu |
|
||||
| `/api/usage/[connectionId]` | NHẬN | Mức sử dụng trên mỗi kết nối |
|
||||
|
||||
### Cài đặt
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ------------------------------- | ----------- | ------------------------------------ |
|
||||
| `/api/settings` | NHẬN/ĐẶT | Cài đặt chung |
|
||||
| `/api/settings/proxy` | NHẬN/ĐẶT | Cấu hình proxy mạng |
|
||||
| `/api/settings/proxy/test` | ĐĂNG | Kiểm tra kết nối proxy |
|
||||
| `/api/settings/ip-filter` | NHẬN/ĐẶT | Danh sách cho phép/danh sách chặn IP |
|
||||
| `/api/settings/thinking-budget` | NHẬN/ĐẶT | Lập luận về ngân sách mã thông báo |
|
||||
| `/api/settings/system-prompt` | NHẬN/ĐẶT | Lời nhắc hệ thống toàn cầu |
|
||||
|
||||
### Giám sát
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ------------------------ | ----------- | -------------------------------- |
|
||||
| `/api/sessions` | NHẬN | Theo dõi phiên hoạt động |
|
||||
| `/api/rate-limits` | NHẬN | Giới hạn tỷ lệ cho mỗi tài khoản |
|
||||
| `/api/monitoring/health` | NHẬN | Kiểm tra sức khỏe |
|
||||
| `/api/cache` | NHẬN/XÓA | Thống kê bộ nhớ đệm / xóa |
|
||||
|
||||
### Sao lưu & Xuất/Nhập
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------------------- | ----------- | ---------------------------------------------------------- |
|
||||
| `/api/db-backups` | NHẬN | Liệt kê các bản sao lưu có sẵn |
|
||||
| `/api/db-backups` | ĐƯA | Tạo bản sao lưu thủ công |
|
||||
| `/api/db-backups` | ĐĂNG | Khôi phục từ bản sao lưu cụ thể |
|
||||
| `/api/db-backups/export` | NHẬN | Tải xuống cơ sở dữ liệu dưới dạng tệp .sqlite |
|
||||
| `/api/db-backups/import` | ĐĂNG | Tải lên tệp .sqlite để thay thế cơ sở dữ liệu |
|
||||
| `/api/db-backups/exportAll` | NHẬN | Tải xuống bản sao lưu đầy đủ dưới dạng kho lưu trữ .tar.gz |
|
||||
|
||||
### Đồng bộ đám mây
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ---------------------- | ----------- | ----------------------------- |
|
||||
| `/api/sync/cloud` | Khác nhau | Hoạt động đồng bộ hóa đám mây |
|
||||
| `/api/sync/initialize` | ĐĂNG | Khởi tạo đồng bộ hóa |
|
||||
| `/api/cloud/*` | Khác nhau | Quản lý đám mây |
|
||||
|
||||
### Công cụ CLI
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ---------------------------------- | ----------- | ------------------------ |
|
||||
| `/api/cli-tools/claude-settings` | NHẬN | Trạng thái Claude CLI |
|
||||
| `/api/cli-tools/codex-settings` | NHẬN | Trạng thái CLI của Codex |
|
||||
| `/api/cli-tools/droid-settings` | NHẬN | Trạng thái CLI của Droid |
|
||||
| `/api/cli-tools/openclaw-settings` | NHẬN | Trạng thái CLI OpenClaw |
|
||||
| `/api/cli-tools/runtime/[toolId]` | NHẬN | Thời gian chạy CLI chung |
|
||||
|
||||
Phản hồi CLI bao gồm: `installed`, `runnable`, `command`, `commandPath`, `runtimeMode`, `reason`.
|
||||
|
||||
### Khả năng phục hồi và giới hạn tỷ lệ
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ----------------------- | ----------- | ------------------------------------------- |
|
||||
| `/api/resilience` | NHẬN/ĐẶT | Nhận/cập nhật hồ sơ khả năng phục hồi |
|
||||
| `/api/resilience/reset` | ĐĂNG | Đặt lại bộ ngắt mạch |
|
||||
| `/api/rate-limits` | NHẬN | Trạng thái giới hạn tỷ lệ cho mỗi tài khoản |
|
||||
| `/api/rate-limit` | NHẬN | Cấu hình giới hạn tốc độ toàn cầu |
|
||||
|
||||
### Đánh giá
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| ------------ | ----------- | --------------------------------------- |
|
||||
| `/api/evals` | NHẬN/ĐĂNG | Liệt kê các bộ đánh giá / đánh giá chạy |
|
||||
|
||||
### Chính sách
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------- | ------------- | ----------------------------- |
|
||||
| `/api/policies` | NHẬN/ĐĂNG/XÓA | Quản lý chính sách định tuyến |
|
||||
|
||||
### Tuân thủ
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------------------- | ----------- | --------------------------------------- |
|
||||
| `/api/compliance/audit-log` | NHẬN | Nhật ký kiểm tra tuân thủ (N cuối cùng) |
|
||||
|
||||
### v1beta (Tương thích với Gemini)
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| -------------------------- | ----------- | -------------------------------------- |
|
||||
| `/v1beta/models` | NHẬN | Liệt kê các mô hình ở định dạng Gemini |
|
||||
| `/v1beta/models/{...path}` | ĐĂNG | Điểm cuối Gemini `generateContent` |
|
||||
|
||||
Các điểm cuối này phản ánh định dạng API của Gemini dành cho những khách hàng mong đợi khả năng tương thích SDK Gemini gốc.
|
||||
|
||||
### API nội bộ/hệ thống
|
||||
|
||||
| Điểm cuối | Phương pháp | Mô tả |
|
||||
| --------------- | ----------- | ----------------------------------------------------------------- |
|
||||
| `/api/init` | NHẬN | Kiểm tra khởi tạo ứng dụng (được sử dụng trong lần chạy đầu tiên) |
|
||||
| `/api/tags` | NHẬN | Thẻ mô hình tương thích với Ollama (dành cho khách hàng Ollama) |
|
||||
| `/api/restart` | ĐĂNG | Kích hoạt khởi động lại máy chủ duyên dáng |
|
||||
| `/api/shutdown` | ĐĂNG | Kích hoạt tắt máy chủ duyên dáng |
|
||||
|
||||
> **Lưu ý:** Các điểm cuối này được hệ thống sử dụng nội bộ hoặc để tương thích với máy khách Ollama. Chúng thường không được người dùng cuối gọi.
|
||||
|
||||
---
|
||||
|
||||
## Phiên âm âm thanh
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
Phiên âm các tệp âm thanh bằng Deepgram hoặc AssemblyAI.
|
||||
|
||||
**Yêu cầu:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**Trả lời:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
**Nhà cung cấp được hỗ trợ:** `deepgram/nova-3`, `assemblyai/best`.
|
||||
|
||||
**Các định dạng được hỗ trợ:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
## Khả năng tương thích của Ollama
|
||||
|
||||
Đối với khách hàng sử dụng định dạng API của Ollama:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
Các yêu cầu được dịch tự động giữa Ollama và các định dạng nội bộ.
|
||||
|
||||
---
|
||||
|
||||
## Đo từ xa
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**Trả lời:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ngân sách
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mẫu có sẵn
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Xử lý yêu cầu
|
||||
|
||||
1. Khách hàng gửi yêu cầu tới `/v1/*`
|
||||
2. Lệnh gọi trình xử lý tuyến `handleChat`, `handleEmbedding`, `handleAudioTranscription` hoặc `handleImageGeneration`
|
||||
3. Mô hình đã được giải quyết (nhà cung cấp/mô hình trực tiếp hoặc bí danh/combo)
|
||||
4. Thông tin xác thực được chọn từ DB cục bộ với tính năng lọc tính khả dụng của tài khoản
|
||||
5. Để trò chuyện: `handleChatCore` — phát hiện định dạng, dịch, kiểm tra bộ đệm, kiểm tra idempotency
|
||||
6. Người thực thi nhà cung cấp gửi yêu cầu ngược dòng
|
||||
7. Phản hồi được dịch trở lại định dạng máy khách (trò chuyện) hoặc trả về nguyên trạng (nhúng/hình ảnh/âm thanh)
|
||||
8. Việc sử dụng/ghi nhật ký được ghi lại
|
||||
9. Dự phòng áp dụng cho các lỗi theo quy tắc kết hợp
|
||||
|
||||
Tham khảo kiến trúc đầy đủ: [**OMNI_TOKEN_119**](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## Xác thực
|
||||
|
||||
- Các tuyến trên trang tổng quan (`/dashboard/*`) sử dụng cookie `auth_token`
|
||||
- Đăng nhập sử dụng hàm băm mật khẩu đã lưu; dự phòng cho `INITIAL_PASSWORD`
|
||||
- `requireLogin` có thể chuyển đổi qua `/api/settings/require-login`
|
||||
- Các tuyến `/v1/*` tùy chọn yêu cầu khóa API Bearer khi `REQUIRE_API_KEY=true`
|
||||
781
docs/i18n/vi/ARCHITECTURE.md
Normal file
781
docs/i18n/vi/ARCHITECTURE.md
Normal file
@@ -0,0 +1,781 @@
|
||||
# Kiến trúc OmniRoute
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
|
||||
|
||||
_Cập nhật lần cuối: 2026-02-18_
|
||||
|
||||
## Tóm tắt điều hành
|
||||
|
||||
OmniRoute là cổng định tuyến và bảng thông tin AI cục bộ được xây dựng trên Next.js.
|
||||
Nó cung cấp một điểm cuối tương thích với OpenAI (`/v1/*`) và định tuyến lưu lượng truy cập trên nhiều nhà cung cấp ngược dòng với tính năng dịch thuật, dự phòng, làm mới mã thông báo và theo dõi việc sử dụng.
|
||||
|
||||
Khả năng cốt lõi:
|
||||
|
||||
- Bề mặt API tương thích OpenAI cho CLI/công cụ (28 nhà cung cấp)
|
||||
- Dịch yêu cầu/phản hồi trên các định dạng của nhà cung cấp
|
||||
- Dự phòng kết hợp mô hình (chuỗi nhiều mô hình)
|
||||
- Dự phòng cấp tài khoản (nhiều tài khoản cho mỗi nhà cung cấp)
|
||||
- Quản lý kết nối nhà cung cấp khóa OAuth + API
|
||||
- Tạo nhúng thông qua `/v1/embeddings` (6 nhà cung cấp, 9 mô hình)
|
||||
- Tạo hình ảnh qua `/v1/images/generations` (4 nhà cung cấp, 9 kiểu máy)
|
||||
- Suy nghĩ phân tích thẻ (`<think>...</think>`) cho các mô hình suy luận
|
||||
- Dọn dẹp phản hồi để tương thích nghiêm ngặt với OpenAI SDK
|
||||
- Chuẩn hóa vai trò (nhà phát triển→hệ thống, hệ thống→người dùng) để tương thích giữa các nhà cung cấp
|
||||
- Chuyển đổi đầu ra có cấu trúc (json_schema → GeminiResponseSchema)
|
||||
- Tính bền vững cục bộ cho nhà cung cấp, khóa, bí danh, tổ hợp, cài đặt, giá cả
|
||||
- Theo dõi việc sử dụng/chi phí và ghi nhật ký yêu cầu
|
||||
- Đồng bộ hóa đám mây tùy chọn để đồng bộ hóa nhiều thiết bị/trạng thái
|
||||
- Danh sách cho phép/danh sách chặn IP để kiểm soát truy cập API
|
||||
- Tư duy quản lý ngân sách (passthrough/auto/custom/adaptive)
|
||||
- Tiêm nhắc nhở hệ thống toàn cầu
|
||||
- Theo dõi phiên và lấy dấu vân tay
|
||||
- Giới hạn tỷ lệ nâng cao cho mỗi tài khoản với hồ sơ dành riêng cho nhà cung cấp
|
||||
- Mô hình ngắt mạch cho khả năng phục hồi của nhà cung cấp
|
||||
- Bảo vệ đàn chống sét bằng khóa mutex
|
||||
- Bộ đệm chống trùng lặp yêu cầu dựa trên chữ ký
|
||||
- Lớp miền: tính khả dụng của mô hình, quy tắc chi phí, chính sách dự phòng, chính sách khóa
|
||||
- Tính bền vững của trạng thái miền (bộ đệm ghi SQLite dành cho dự phòng, ngân sách, khóa, bộ ngắt mạch)
|
||||
- Công cụ chính sách để đánh giá yêu cầu tập trung (khóa → ngân sách → dự phòng)
|
||||
- Yêu cầu đo từ xa với tổng hợp độ trễ p50/p95/p99
|
||||
- ID tương quan (X-Request-Id) để theo dõi từ đầu đến cuối
|
||||
- Ghi nhật ký kiểm tra tuân thủ với tính năng chọn không tham gia trên mỗi khóa API
|
||||
- Khung đánh giá để đảm bảo chất lượng LLM
|
||||
- Bảng điều khiển UI có khả năng phục hồi với trạng thái ngắt mạch theo thời gian thực
|
||||
- Nhà cung cấp OAuth mô-đun (12 mô-đun riêng lẻ trong `src/lib/oauth/providers/`)
|
||||
|
||||
Mô hình thời gian chạy chính:
|
||||
|
||||
- Các tuyến ứng dụng Next.js trong `src/app/api/*` triển khai cả API trang tổng quan và API tương thích
|
||||
- Lõi định tuyến/SSE được chia sẻ trong `src/sse/*` + `open-sse/*` xử lý việc thực thi, dịch thuật, phát trực tuyến, dự phòng và sử dụng của nhà cung cấp
|
||||
|
||||
## Phạm vi và ranh giới
|
||||
|
||||
### Trong phạm vi
|
||||
|
||||
- Thời gian chạy cổng cục bộ
|
||||
- API quản lý bảng điều khiển
|
||||
- Xác thực nhà cung cấp và làm mới mã thông báo
|
||||
- Yêu cầu dịch và truyền phát SSE
|
||||
- Trạng thái cục bộ + kiên trì sử dụng
|
||||
- Phối hợp đồng bộ hóa đám mây tùy chọn
|
||||
|
||||
### Ngoài phạm vi
|
||||
|
||||
- Triển khai dịch vụ đám mây đằng sau `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Nhà cung cấp SLA/mặt phẳng điều khiển bên ngoài quy trình cục bộ
|
||||
- Bản thân các tệp nhị phân CLI bên ngoài (Claude CLI, Codex CLI, v.v.)
|
||||
|
||||
## Bối cảnh hệ thống cấp cao
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(db.json)]
|
||||
UDB[(usage.json + log.txt)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## Thành phần thời gian chạy cốt lõi
|
||||
|
||||
## 1) API và Lớp định tuyến (Tuyến ứng dụng Next.js)
|
||||
|
||||
Các thư mục chính:
|
||||
|
||||
- `src/app/api/v1/*` và `src/app/api/v1beta/*` cho các API tương thích
|
||||
- `src/app/api/*` dành cho API quản lý/cấu hình
|
||||
- Viết lại tiếp theo trong `next.config.mjs` bản đồ `/v1/*` tới `/api/v1/*`
|
||||
|
||||
Các tuyến tương thích quan trọng:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — bao gồm các mô hình tùy chỉnh với `custom: true`
|
||||
- `src/app/api/v1/embeddings/route.ts` — thế hệ nhúng (6 nhà cung cấp)
|
||||
- `src/app/api/v1/images/generations/route.ts` — tạo hình ảnh (4+ nhà cung cấp bao gồm Anti Gravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — cuộc trò chuyện dành riêng cho từng nhà cung cấp
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — phần nhúng dành riêng cho mỗi nhà cung cấp
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — hình ảnh dành riêng cho mỗi nhà cung cấp
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
Các miền quản lý:
|
||||
|
||||
- Xác thực/cài đặt: `src/app/api/auth/*`, `src/app/api/settings/*`
|
||||
- Nhà cung cấp/kết nối: `src/app/api/providers*`
|
||||
- Nút nhà cung cấp: `src/app/api/provider-nodes*`
|
||||
- Mẫu tùy chỉnh: `src/app/api/provider-models` (GET/POST/DELETE)
|
||||
- Danh mục mẫu: `src/app/api/models/catalog` (GET)
|
||||
- Cấu hình proxy: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth: `src/app/api/oauth/*`
|
||||
- Khóa/bí danh/combo/giá: `src/app/api/keys*`, `src/app/api/models/alias`, `src/app/api/combos*`, `src/app/api/pricing`
|
||||
- Cách sử dụng: `src/app/api/usage/*`
|
||||
- Đồng bộ hóa/đám mây: `src/app/api/sync/*`, `src/app/api/cloud/*`
|
||||
- Người trợ giúp công cụ CLI: `src/app/api/cli-tools/*`
|
||||
- Bộ lọc IP: `src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- Ngân sách suy nghĩ: `src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- Lời nhắc hệ thống: `src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- Phiên: `src/app/api/sessions` (GET)
|
||||
- Giới hạn tỷ lệ: `src/app/api/rate-limits` (GET)
|
||||
- Khả năng phục hồi: `src/app/api/resilience` (GET/PATCH) — hồ sơ nhà cung cấp, bộ ngắt mạch, trạng thái giới hạn tốc độ
|
||||
- Đặt lại khả năng phục hồi: `src/app/api/resilience/reset` (POST) — đặt lại bộ ngắt + thời gian hồi chiêu
|
||||
- Thống kê bộ đệm: `src/app/api/cache/stats` (GET/DELETE)
|
||||
- Tính sẵn có của mẫu: `src/app/api/models/availability` (GET/POST)
|
||||
- Đo từ xa: `src/app/api/telemetry/summary` (GET)
|
||||
- Ngân sách: `src/app/api/usage/budget` (GET/POST)
|
||||
- Chuỗi dự phòng: `src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- Kiểm tra tuân thủ: `src/app/api/compliance/audit-log` (GET)
|
||||
- Đánh giá: `src/app/api/evals` (GET/POST), `src/app/api/evals/[suiteId]` (GET)
|
||||
- Chính sách: `src/app/api/policies` (GET/POST)
|
||||
|
||||
##2) SSE + Lõi dịch thuật
|
||||
|
||||
Các mô-đun dòng chảy chính:
|
||||
|
||||
- Mục nhập: `src/sse/handlers/chat.ts`
|
||||
- Điều phối cốt lõi: `open-sse/handlers/chatCore.ts`
|
||||
- Bộ điều hợp thực thi của nhà cung cấp: `open-sse/executors/*`
|
||||
- Cấu hình nhà cung cấp/phát hiện định dạng: `open-sse/services/provider.ts`
|
||||
- Phân tích/giải quyết mô hình: `src/sse/services/model.ts`, `open-sse/services/model.ts`
|
||||
- Logic dự phòng tài khoản: `open-sse/services/accountFallback.ts`
|
||||
- Đăng ký dịch thuật: `open-sse/translator/index.ts`
|
||||
- Chuyển đổi luồng: `open-sse/utils/stream.ts`, `open-sse/utils/streamHandler.ts`
|
||||
- Trích xuất/chuẩn hóa cách sử dụng: `open-sse/utils/usageTracking.ts`
|
||||
- Trình phân tích cú pháp thẻ suy nghĩ: `open-sse/utils/thinkTagParser.ts`
|
||||
- Trình xử lý nhúng: `open-sse/handlers/embeddings.ts`
|
||||
- Đăng ký nhà cung cấp nhúng: `open-sse/config/embeddingRegistry.ts`
|
||||
- Trình xử lý tạo ảnh: `open-sse/handlers/imageGeneration.ts`
|
||||
- Đăng ký nhà cung cấp hình ảnh: `open-sse/config/imageRegistry.ts`
|
||||
- Khử trùng phản hồi: `open-sse/handlers/responseSanitizer.ts`
|
||||
- Chuẩn hóa vai trò: `open-sse/services/roleNormalizer.ts`
|
||||
|
||||
Dịch vụ (logic nghiệp vụ):
|
||||
|
||||
- Lựa chọn/chấm điểm tài khoản: `open-sse/services/accountSelector.ts`
|
||||
- Quản lý vòng đời bối cảnh: `open-sse/services/contextManager.ts`
|
||||
- Thực thi bộ lọc IP: `open-sse/services/ipFilter.ts`
|
||||
- Theo dõi phiên: `open-sse/services/sessionManager.ts`
|
||||
- Yêu cầu loại bỏ trùng lặp: `open-sse/services/signatureCache.ts`
|
||||
- Nội dung nhắc nhở của hệ thống: `open-sse/services/systemPrompt.ts`
|
||||
- Tư duy quản lý ngân sách: `open-sse/services/thinkingBudget.ts`
|
||||
- Định tuyến mô hình ký tự đại diện: `open-sse/services/wildcardRouter.ts`
|
||||
- Quản lý giới hạn tỷ lệ: `open-sse/services/rateLimitManager.ts`
|
||||
- Cầu dao: `open-sse/services/circuitBreaker.ts`
|
||||
|
||||
Các mô-đun lớp miền:
|
||||
|
||||
- Tính sẵn có của mẫu: `src/lib/domain/modelAvailability.ts`
|
||||
- Quy tắc chi phí/ngân sách: `src/lib/domain/costRules.ts`
|
||||
- Chính sách dự phòng: `src/lib/domain/fallbackPolicy.ts`
|
||||
- Trình giải quyết kết hợp: `src/lib/domain/comboResolver.ts`
|
||||
- Chính sách khóa: `src/lib/domain/lockoutPolicy.ts`
|
||||
- Công cụ chính sách: `src/domain/policyEngine.ts` — khóa tập trung → ngân sách → đánh giá dự phòng
|
||||
- Danh mục mã lỗi: `src/lib/domain/errorCodes.ts`
|
||||
- ID yêu cầu: `src/lib/domain/requestId.ts`
|
||||
- Thời gian chờ tìm nạp: `src/lib/domain/fetchTimeout.ts`
|
||||
- Yêu cầu đo từ xa: `src/lib/domain/requestTelemetry.ts`
|
||||
- Tuân thủ/kiểm toán: `src/lib/domain/compliance/index.ts`
|
||||
- Người chạy đánh giá: `src/lib/domain/evalRunner.ts`
|
||||
- Tính bền vững của trạng thái miền: `src/lib/db/domainState.ts` — SQLite CRUD dành cho chuỗi dự phòng, ngân sách, lịch sử chi phí, trạng thái khóa, bộ ngắt mạch
|
||||
|
||||
Mô-đun nhà cung cấp OAuth (12 tệp riêng lẻ trong `src/lib/oauth/providers/`):
|
||||
|
||||
- Chỉ số đăng ký: `src/lib/oauth/providers/index.ts`
|
||||
- Nhà cung cấp cá nhân: `claude.ts`, `codex.ts`, `gemini.ts`, `antigravity.ts`, `iflow.ts`, `qwen.ts`, `kimi-coding.ts`, `github.ts`, `kiro.ts`, `cursor.ts`, `kilocode.ts`, `cline.ts`
|
||||
- Trình bao bọc mỏng: `src/lib/oauth/providers.ts` — tái xuất từ các mô-đun riêng lẻ
|
||||
|
||||
## 3) Lớp kiên trì
|
||||
|
||||
DB trạng thái chính:
|
||||
|
||||
- `src/lib/localDb.ts`
|
||||
- tệp: `${DATA_DIR}/db.json` (hoặc `$XDG_CONFIG_HOME/omniroute/db.json` khi được đặt, nếu không thì `~/.omniroute/db.json`)
|
||||
- thực thể: nhà cung cấpKết nối, nhà cung cấpNodes, modelAliases, combo, apiKeys, cài đặt, giá cả, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
|
||||
|
||||
Cách sử dụng cơ sở dữ liệu:
|
||||
|
||||
- `src/lib/usageDb.ts`
|
||||
- tập tin: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- tuân theo chính sách thư mục cơ sở giống như `localDb` (`DATA_DIR`, sau đó `XDG_CONFIG_HOME/omniroute` khi được đặt)
|
||||
- được phân tách thành các mô-đun phụ tập trung: `migrations.ts`, `usageHistory.ts`, `costCalculator.ts`, `usageStats.ts`, `callLogs.ts`
|
||||
|
||||
Cơ sở dữ liệu trạng thái miền (SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — Thao tác CRUD cho trạng thái miền
|
||||
- Các bảng (được tạo trong `src/lib/db/core.ts`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
|
||||
- Mẫu bộ đệm ghi qua: Bản đồ trong bộ nhớ có thẩm quyền trong thời gian chạy; các đột biến được ghi đồng bộ vào SQLite; trạng thái được khôi phục từ DB khi khởi động nguội
|
||||
|
||||
##4) Xác thực + Bề mặt bảo mật
|
||||
|
||||
- Xác thực cookie trang tổng quan: `src/proxy.ts`, `src/app/api/auth/login/route.ts`
|
||||
- Tạo/xác minh khóa API: `src/shared/utils/apiKey.ts`
|
||||
- Bí mật của nhà cung cấp vẫn tồn tại trong mục `providerConnections`
|
||||
- Hỗ trợ proxy gửi đi thông qua `open-sse/utils/proxyFetch.ts` (env vars) và `open-sse/utils/networkProxy.ts` (có thể định cấu hình cho mỗi nhà cung cấp hoặc toàn cầu)
|
||||
|
||||
## 5) Đồng bộ đám mây
|
||||
|
||||
- Khởi tạo bộ lập lịch: `src/lib/initCloudSync.ts`, `src/shared/services/initializeCloudSync.ts`
|
||||
- Nhiệm vụ định kỳ: `src/shared/services/cloudSyncScheduler.ts`
|
||||
- Lộ trình điều khiển: `src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## Vòng đời yêu cầu (`/v1/chat/completions`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## Combo + Luồng dự phòng tài khoản
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
Các quyết định dự phòng được điều khiển bởi `open-sse/services/accountFallback.ts` bằng cách sử dụng mã trạng thái và phương pháp phỏng đoán thông báo lỗi.
|
||||
|
||||
## Vòng đời giới thiệu OAuth và làm mới mã thông báo
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
Làm mới trong khi lưu lượng truy cập trực tiếp được thực thi bên trong `open-sse/handlers/chatCore.ts` thông qua người thực thi `refreshCredentials()`.
|
||||
|
||||
## Vòng đời đồng bộ hóa đám mây (Bật / Đồng bộ hóa / Tắt)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
Đồng bộ hóa định kỳ được kích hoạt bởi `CloudSyncScheduler` khi bật đám mây.
|
||||
|
||||
## Mô hình dữ liệu và bản đồ lưu trữ
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
Tệp lưu trữ vật lý:
|
||||
|
||||
- trạng thái chính: `${DATA_DIR}/db.json` (hoặc `$XDG_CONFIG_HOME/omniroute/db.json` khi được đặt, nếu không thì `~/.omniroute/db.json`)
|
||||
- số liệu thống kê sử dụng: `${DATA_DIR}/usage.json`
|
||||
- dòng nhật ký yêu cầu: `${DATA_DIR}/log.txt`
|
||||
- phiên gỡ lỗi yêu cầu/trình dịch tùy chọn: `<repo>/logs/...`
|
||||
|
||||
## Cấu trúc liên kết triển khai
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(db.json)]
|
||||
UsageDB[(usage.json/log.txt)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## Ánh xạ mô-đun (Quyết định quan trọng)
|
||||
|
||||
### Mô-đun tuyến đường và API
|
||||
|
||||
- `src/app/api/v1/*`, `src/app/api/v1beta/*`: API tương thích
|
||||
- `src/app/api/v1/providers/[provider]/*`: các tuyến dành riêng cho mỗi nhà cung cấp (trò chuyện, nội dung nhúng, hình ảnh)
|
||||
- `src/app/api/providers*`: CRUD của nhà cung cấp, xác thực, kiểm tra
|
||||
- `src/app/api/provider-nodes*`: quản lý nút tương thích tùy chỉnh
|
||||
- `src/app/api/provider-models`: quản lý mô hình tùy chỉnh (CRUD)
|
||||
- `src/app/api/models/catalog`: API danh mục mô hình đầy đủ (tất cả các loại được nhóm theo nhà cung cấp)
|
||||
- `src/app/api/oauth/*`: Luồng OAuth/mã thiết bị
|
||||
- `src/app/api/keys*`: vòng đời khóa API cục bộ
|
||||
- `src/app/api/models/alias`: quản lý bí danh
|
||||
- `src/app/api/combos*`: quản lý kết hợp dự phòng
|
||||
- `src/app/api/pricing`: ghi đè giá để tính chi phí
|
||||
- `src/app/api/settings/proxy`: cấu hình proxy (GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test`: kiểm tra kết nối proxy gửi đi (POST)
|
||||
- `src/app/api/usage/*`: API sử dụng và nhật ký
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*`: trợ giúp đồng bộ hóa đám mây và hướng tới đám mây
|
||||
- `src/app/api/cli-tools/*`: trình soạn thảo/kiểm tra cấu hình CLI cục bộ
|
||||
- `src/app/api/settings/ip-filter`: Danh sách cho phép/danh sách chặn IP (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget`: cấu hình ngân sách mã thông báo suy nghĩ (GET/PUT)
|
||||
- `src/app/api/settings/system-prompt`: lời nhắc hệ thống toàn cầu (GET/PUT)
|
||||
- `src/app/api/sessions`: danh sách phiên hoạt động (GET)
|
||||
- `src/app/api/rate-limits`: trạng thái giới hạn tỷ lệ cho mỗi tài khoản (GET)
|
||||
|
||||
### Lõi định tuyến và thực thi
|
||||
|
||||
- `src/sse/handlers/chat.ts`: phân tích cú pháp yêu cầu, xử lý kết hợp, vòng lặp chọn tài khoản
|
||||
- `open-sse/handlers/chatCore.ts`: dịch, gửi người thực thi, xử lý thử lại/làm mới, thiết lập luồng
|
||||
- `open-sse/executors/*`: hành vi định dạng và mạng dành riêng cho nhà cung cấp
|
||||
|
||||
### Bộ chuyển đổi định dạng và đăng ký dịch thuật
|
||||
|
||||
- `open-sse/translator/index.ts`: đăng ký dịch giả và điều phối
|
||||
- Yêu cầu người dịch: `open-sse/translator/request/*`
|
||||
- Người dịch phản hồi: `open-sse/translator/response/*`
|
||||
- Hằng định dạng: `open-sse/translator/formats.ts`
|
||||
|
||||
### Kiên trì
|
||||
|
||||
- `src/lib/localDb.ts`: trạng thái/cấu hình liên tục
|
||||
- `src/lib/usageDb.ts`: lịch sử sử dụng và nhật ký yêu cầu luân phiên
|
||||
|
||||
## Bảo hiểm người thực thi nhà cung cấp (Mẫu chiến lược)
|
||||
|
||||
Mỗi nhà cung cấp có một trình thực thi chuyên biệt mở rộng `BaseExecutor` (trong `open-sse/executors/base.ts`), cung cấp việc xây dựng URL, xây dựng tiêu đề, thử lại với thời gian chờ theo cấp số nhân, móc làm mới thông tin xác thực và phương thức điều phối `execute()`.
|
||||
|
||||
| Người thi hành | (Các) nhà cung cấp | Xử lý đặc biệt |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
||||
| `DefaultExecutor` | OpenAI, Claude, Gemini, Qwen, iFlow, OpenRouter, GLM, Kimi, MiniMax, DeepSeek, Groq, xAI, Mistral, Perplexity, Together, Fireworks, Cerebras, Cohere, NVIDIA | Cấu hình URL/tiêu đề động cho mỗi nhà cung cấp |
|
||||
| `AntigravityExecutor` | Google phản lực hấp dẫn | ID dự án/phiên tùy chỉnh, Thử lại sau khi phân tích cú pháp |
|
||||
| `CodexExecutor` | OpenAI Codex | Đưa vào các hướng dẫn hệ thống, buộc nỗ lực suy luận |
|
||||
| `CursorExecutor` | IDE con trỏ | Giao thức ConnectRPC, mã hóa Protobuf, ký yêu cầu qua tổng kiểm tra |
|
||||
| `GithubExecutor` | Phi công phụ GitHub | Làm mới mã thông báo Copilot, tiêu đề bắt chước VSCode |
|
||||
| `KiroExecutor` | AWS CodeWhisperer/Kiro | Định dạng nhị phân AWS EventStream → Chuyển đổi SSE |
|
||||
| `GeminiCLIExecutor` | Song Tử CLI | Chu kỳ làm mới mã thông báo Google OAuth |
|
||||
|
||||
Tất cả các nhà cung cấp khác (bao gồm các nút tương thích tùy chỉnh) đều sử dụng `DefaultExecutor`.
|
||||
|
||||
## Ma trận tương thích của nhà cung cấp
|
||||
|
||||
| Nhà cung cấp | Định dạng | Xác thực | Truyền phát | Không phát trực tuyến | Làm mới mã thông báo | API sử dụng |
|
||||
| ------------------- | --------------- | ----------------------------- | ---------------- | --------------------- | -------------------- | ----------------------------- |
|
||||
| Claude | Claude | Khóa API / OAuth | ✅ | ✅ | ✅ | ⚠️ Chỉ dành cho quản trị viên |
|
||||
| Song Tử | song tử | Khóa API / OAuth | ✅ | ✅ | ✅ | ⚠️ Bảng điều khiển đám mây |
|
||||
| Song Tử CLI | gemini-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ Bảng điều khiển đám mây |
|
||||
| Phản lực hấp dẫn | phản trọng lực | OAuth | ✅ | ✅ | ✅ | ✅ API hạn ngạch đầy đủ |
|
||||
| OpenAI | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Codex | phản hồi openai | OAuth | ✅ ép buộc | ❌ | ✅ | ✅ Giới hạn tỷ lệ |
|
||||
| Phi công phụ GitHub | mở | OAuth + Mã thông báo đồng lái | ✅ | ✅ | ✅ | ✅ Ảnh chụp nhanh hạn ngạch |
|
||||
| Con trỏ | con trỏ | Tổng kiểm tra tùy chỉnh | ✅ | ✅ | ❌ | ❌ |
|
||||
| Kiro | kiro | AWS SSO OIDC | ✅ (EventStream) | ❌ | ✅ | ✅ Giới hạn sử dụng |
|
||||
| Qwen | mở | OAuth | ✅ | ✅ | ✅ | ⚠️ Theo yêu cầu |
|
||||
| iFlow | mở | OAuth (Cơ bản) | ✅ | ✅ | ✅ | ⚠️ Theo yêu cầu |
|
||||
| OpenRouter | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| GLM/Kimi/MiniMax | Claude | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| DeepSeek | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Groq | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| xAI (Grok) | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Mistral | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Lúng túng | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Cùng AI | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Pháo hoa AI | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Não | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| Kết hợp | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
| NVIDIA NIM | mở | Khóa API | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
## Phạm vi dịch định dạng
|
||||
|
||||
Các định dạng nguồn được phát hiện bao gồm:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
Các định dạng mục tiêu bao gồm:
|
||||
|
||||
- Trò chuyện/Phản hồi OpenAI
|
||||
- Claude
|
||||
- Phong bì Song Tử/Song Tử-CLI/Phản trọng lực
|
||||
- Kiro
|
||||
- Con trỏ
|
||||
|
||||
Các bản dịch sử dụng **OpenAI làm định dạng trung tâm** — tất cả các chuyển đổi đều thông qua OpenAI dưới dạng trung gian:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
Các bản dịch được chọn linh hoạt dựa trên hình dạng tải trọng nguồn và định dạng mục tiêu của nhà cung cấp.
|
||||
|
||||
Các lớp xử lý bổ sung trong quy trình dịch thuật:
|
||||
|
||||
- **Sạch hóa phản hồi** — Loại bỏ các trường không chuẩn khỏi phản hồi ở định dạng OpenAI (cả phát trực tuyến và không phát trực tuyến) để đảm bảo tuân thủ nghiêm ngặt SDK
|
||||
- **Chuẩn hóa vai trò** — Chuyển đổi `developer` → `system` cho các mục tiêu không phải OpenAI; hợp nhất `system` → `user` cho các mô hình từ chối vai trò hệ thống (GLM, ERNIE)
|
||||
- **Suy nghĩ trích xuất thẻ** — Phân tích cú pháp `<think>...</think>` chặn nội dung vào trường `reasoning_content`
|
||||
- **Đầu ra có cấu trúc** — Chuyển đổi OpenAI `response_format.json_schema` thành `responseMimeType` + `responseSchema` của Gemini
|
||||
|
||||
## Điểm cuối API được hỗ trợ
|
||||
|
||||
| Điểm cuối | Định dạng | Người xử lý |
|
||||
| -------------------------------------------------- | ---------------------------- | ------------------------------------------------------------- |
|
||||
| `POST /v1/chat/completions` | Trò chuyện OpenAI | `src/sse/handlers/chat.ts` |
|
||||
| `POST /v1/messages` | Tin nhắn Claude | Trình xử lý tương tự (tự động phát hiện) |
|
||||
| `POST /v1/responses` | Phản hồi OpenAI | `open-sse/handlers/responsesHandler.ts` |
|
||||
| `POST /v1/embeddings` | Nhúng OpenAI | `open-sse/handlers/embeddings.ts` |
|
||||
| `GET /v1/embeddings` | Danh sách mô hình | Tuyến đường API |
|
||||
| `POST /v1/images/generations` | Hình ảnh OpenAI | `open-sse/handlers/imageGeneration.ts` |
|
||||
| `GET /v1/images/generations` | Danh sách mô hình | Tuyến đường API |
|
||||
| `POST /v1/providers/{provider}/chat/completions` | Trò chuyện OpenAI | Dành riêng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `POST /v1/providers/{provider}/embeddings` | Nhúng OpenAI | Dành riêng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `POST /v1/providers/{provider}/images/generations` | Hình ảnh OpenAI | Dành riêng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `POST /v1/messages/count_tokens` | Số lượng mã thông báo Claude | Tuyến đường API |
|
||||
| `GET /v1/models` | Danh sách mô hình OpenAI | Tuyến API (trò chuyện + nhúng + hình ảnh + mô hình tùy chỉnh) |
|
||||
| `GET /api/models/catalog` | Danh mục | Tất cả các mô hình được nhóm theo nhà cung cấp + loại |
|
||||
| `POST /v1beta/models/*:streamGenerateContent` | Song Tử bản địa | Tuyến đường API |
|
||||
| `GET/PUT/DELETE /api/settings/proxy` | Cấu hình proxy | Cấu hình proxy mạng |
|
||||
| `POST /api/settings/proxy/test` | Kết nối proxy | Điểm cuối kiểm tra tình trạng/kết nối proxy |
|
||||
| `GET/POST/DELETE /api/provider-models` | Mô hình tùy chỉnh | Quản lý mô hình tùy chỉnh cho mỗi nhà cung cấp |
|
||||
|
||||
## Trình xử lý bỏ qua
|
||||
|
||||
Trình xử lý bỏ qua (`open-sse/utils/bypassHandler.ts`) chặn các yêu cầu "loại bỏ" đã biết từ Claude CLI — ping khởi động, trích xuất tiêu đề và số lượng mã thông báo — và trả về **phản hồi giả** mà không tiêu tốn mã thông báo của nhà cung cấp ngược dòng. Điều này chỉ được kích hoạt khi `User-Agent` chứa `claude-cli`.
|
||||
|
||||
## Yêu cầu đường dẫn trình ghi nhật ký
|
||||
|
||||
Trình ghi nhật ký yêu cầu (`open-sse/utils/requestLogger.ts`) cung cấp quy trình ghi nhật ký gỡ lỗi gồm 7 giai đoạn, bị tắt theo mặc định, được bật qua `ENABLE_REQUEST_LOGS=true`:
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
Các tệp được ghi vào `<repo>/logs/<session>/` cho mỗi phiên yêu cầu.
|
||||
|
||||
## Các chế độ thất bại và khả năng phục hồi
|
||||
|
||||
## 1) Tính khả dụng của tài khoản/nhà cung cấp
|
||||
|
||||
- thời gian hồi chiêu của tài khoản nhà cung cấp đối với các lỗi tạm thời/tỷ lệ/xác thực
|
||||
- dự phòng tài khoản trước khi yêu cầu không thành công
|
||||
- dự phòng mô hình kết hợp khi đường dẫn mô hình/nhà cung cấp hiện tại đã hết
|
||||
|
||||
## 2) Mã thông báo hết hạn
|
||||
|
||||
- kiểm tra trước và làm mới bằng cách thử lại đối với các nhà cung cấp có thể làm mới
|
||||
- Thử lại 401/403 sau lần thử làm mới trong đường dẫn lõi
|
||||
|
||||
##3) An toàn khi truyền phát
|
||||
|
||||
- bộ điều khiển luồng nhận biết ngắt kết nối
|
||||
- luồng dịch với tính năng xóa cuối luồng và xử lý `[DONE]`
|
||||
- dự phòng ước tính sử dụng khi thiếu siêu dữ liệu sử dụng của nhà cung cấp
|
||||
|
||||
## 4) Suy giảm đồng bộ đám mây
|
||||
|
||||
- lỗi đồng bộ hóa xuất hiện nhưng thời gian chạy cục bộ vẫn tiếp tục
|
||||
- bộ lập lịch có logic có khả năng thử lại, nhưng việc thực thi định kỳ hiện gọi đồng bộ hóa một lần thử theo mặc định
|
||||
|
||||
## 5) Toàn vẹn dữ liệu
|
||||
|
||||
- Di chuyển/sửa chữa hình dạng DB cho các khóa bị thiếu
|
||||
- các biện pháp bảo vệ đặt lại JSON bị hỏng cho localDb và useDb
|
||||
|
||||
## Tín hiệu quan sát và hoạt động
|
||||
|
||||
Nguồn hiển thị thời gian chạy:
|
||||
|
||||
- nhật ký bảng điều khiển từ `src/sse/utils/logger.ts`
|
||||
- tổng mức sử dụng theo yêu cầu trong `usage.json`
|
||||
- nhật ký trạng thái yêu cầu bằng văn bản trong `log.txt`
|
||||
- nhật ký dịch/yêu cầu sâu tùy chọn trong `logs/` khi `ENABLE_REQUEST_LOGS=true`
|
||||
- điểm cuối sử dụng trang tổng quan (`/api/usage/*`) để sử dụng giao diện người dùng
|
||||
|
||||
## Ranh giới nhạy cảm về bảo mật
|
||||
|
||||
- Bí mật JWT (`JWT_SECRET`) bảo mật việc xác minh/ký cookie phiên bảng điều khiển
|
||||
- Dự phòng mật khẩu ban đầu (`INITIAL_PASSWORD`, mặc định `123456`) phải được ghi đè trong quá trình triển khai thực tế
|
||||
- Khóa API Bí mật HMAC (`API_KEY_SECRET`) bảo mật định dạng khóa API cục bộ được tạo
|
||||
- Bí mật của nhà cung cấp (khóa API/mã thông báo) được lưu giữ trong DB cục bộ và phải được bảo vệ ở cấp hệ thống tệp
|
||||
- Điểm cuối đồng bộ hóa đám mây dựa vào ngữ nghĩa xác thực khóa API + id máy
|
||||
|
||||
## Ma trận môi trường và thời gian chạy
|
||||
|
||||
Các biến môi trường được mã sử dụng tích cực:
|
||||
|
||||
- Ứng dụng/xác thực: `JWT_SECRET`, `INITIAL_PASSWORD`
|
||||
- Bộ nhớ: `DATA_DIR`
|
||||
- Hành vi của nút tương thích: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- Ghi đè cơ sở lưu trữ tùy chọn (Linux/macOS khi `DATA_DIR` không được đặt): `XDG_CONFIG_HOME`
|
||||
- Băm bảo mật: `API_KEY_SECRET`, `MACHINE_ID_SALT`
|
||||
- Ghi nhật ký: `ENABLE_REQUEST_LOGS`
|
||||
- URL đồng bộ hóa/đám mây: `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Proxy gửi đi: `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` và các biến thể chữ thường
|
||||
- Cờ tính năng SOCKS5: `ENABLE_SOCKS5_PROXY`, `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- Trình trợ giúp nền tảng/thời gian chạy (không phải cấu hình dành riêng cho ứng dụng): `APPDATA`, `NODE_ENV`, `PORT`, `HOSTNAME`
|
||||
|
||||
## Ghi chú kiến trúc đã biết
|
||||
|
||||
1. `usageDb` và `localDb` hiện chia sẻ cùng một chính sách thư mục cơ sở (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) với việc di chuyển tệp cũ.
|
||||
2. `/api/v1/route.ts` trả về danh sách mô hình tĩnh và không phải là nguồn mô hình chính được `/v1/models` sử dụng.
|
||||
3. Trình ghi yêu cầu ghi toàn bộ tiêu đề/nội dung khi được bật; coi thư mục nhật ký là nhạy cảm.
|
||||
4. Hoạt động của đám mây phụ thuộc vào `NEXT_PUBLIC_BASE_URL` chính xác và khả năng tiếp cận điểm cuối của đám mây.
|
||||
5. Thư mục `open-sse/` được xuất bản dưới dạng `@omniroute/open-sse` **gói không gian làm việc npm**. Mã nguồn nhập nó qua `@omniroute/open-sse/...` (được giải quyết bởi Next.js `transpilePackages`). Đường dẫn tệp trong tài liệu này vẫn sử dụng tên thư mục `open-sse/` để đảm bảo tính nhất quán.
|
||||
6. Các biểu đồ trong trang tổng quan sử dụng **Recharts** (dựa trên SVG) để hiển thị trực quan hóa phân tích tương tác, có thể truy cập (biểu đồ thanh sử dụng mô hình, bảng phân tích nhà cung cấp với tỷ lệ thành công).
|
||||
7. Kiểm tra E2E sử dụng **Playwright** (`tests/e2e/`), chạy qua `npm run test:e2e`. Kiểm thử đơn vị sử dụng **Trình chạy thử nghiệm Node.js** (`tests/unit/`), chạy qua `npm run test:plan3`. Mã nguồn trong `src/` là **TypeScript** (`.ts`/`.tsx`); không gian làm việc `open-sse/` vẫn là JavaScript (`.js`).
|
||||
8. Trang cài đặt được tổ chức thành 5 tab: Bảo mật, Định tuyến (6 chiến lược toàn cầu: điền trước, quay vòng, p2c, ngẫu nhiên, ít sử dụng nhất, tối ưu hóa chi phí), Khả năng phục hồi (giới hạn tốc độ có thể chỉnh sửa, ngắt mạch, chính sách), AI (ngân sách suy nghĩ, lời nhắc hệ thống, bộ nhớ đệm nhắc nhở), Nâng cao (proxy).
|
||||
|
||||
## Danh sách kiểm tra xác minh hoạt động
|
||||
|
||||
- Xây dựng từ nguồn: `npm run build`
|
||||
- Xây dựng Docker image: `docker build -t omniroute .`
|
||||
- Bắt đầu dịch vụ và xác minh:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- URL cơ sở mục tiêu CLI phải là `http://<host>:20128/v1` khi `PORT=20128`
|
||||
589
docs/i18n/vi/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/vi/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# omniroute — Tài liệu cơ sở mã
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](../pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](../es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](../fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](../it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](../ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](../zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](../de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](../in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](../th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](../uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](../ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](../ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](../vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](../bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](../da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](../fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](../he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](../hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](../id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](../ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](../ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](../nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](../no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](../pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](../ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](../pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](../sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](../sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](../phi/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> Hướng dẫn toàn diện, thân thiện với người mới bắt đầu về bộ định tuyến proxy AI đa nhà cung cấp **omnroute**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Omniroute là gì?
|
||||
|
||||
omniroute là **bộ định tuyến proxy** nằm giữa các máy khách AI (Claude CLI, Codex, Cursor IDE, v.v.) và các nhà cung cấp AI (Anthropic, Google, OpenAI, AWS, GitHub, v.v.). Nó giải quyết một vấn đề lớn:
|
||||
|
||||
> **Các ứng dụng khách AI khác nhau nói những "ngôn ngữ" (định dạng API) khác nhau và các nhà cung cấp AI khác nhau cũng mong đợi những "ngôn ngữ" khác nhau.** omniroute dịch tự động giữa chúng.
|
||||
|
||||
Hãy nghĩ về nó giống như một dịch giả phổ quát tại Liên hợp quốc - bất kỳ đại biểu nào cũng có thể nói bất kỳ ngôn ngữ nào và người phiên dịch sẽ chuyển đổi ngôn ngữ đó cho bất kỳ đại biểu nào khác.
|
||||
|
||||
---
|
||||
|
||||
## 2. Tổng quan về kiến trúc
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### Nguyên tắc cốt lõi: Dịch Hub-and-Spoke
|
||||
|
||||
Tất cả các bản dịch định dạng đều đi qua **định dạng OpenAI làm trung tâm**:
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
Điều này có nghĩa là bạn chỉ cần **N người dịch** (một người cho mỗi định dạng) thay vì **N²** (mỗi cặp).
|
||||
|
||||
---
|
||||
|
||||
## 3. Cấu trúc dự án
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Phân tích theo từng mô-đun
|
||||
|
||||
### Cấu hình 4.1 (`open-sse/config/`)
|
||||
|
||||
**nguồn tin cậy duy nhất** cho tất cả cấu hình của nhà cung cấp.
|
||||
|
||||
| Tập tin | Mục đích |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `constants.ts` | `PROVIDERS` có URL cơ sở, thông tin xác thực OAuth (mặc định), tiêu đề và lời nhắc hệ thống mặc định cho mọi nhà cung cấp. Đồng thời xác định `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG` và `SKIP_PATTERNS`. |
|
||||
| `credentialLoader.ts` | Tải thông tin xác thực bên ngoài từ `data/provider-credentials.json` và hợp nhất chúng theo giá trị mặc định được mã hóa cứng trong `PROVIDERS`. Giữ bí mật ngoài tầm kiểm soát nguồn trong khi vẫn duy trì khả năng tương thích ngược. |
|
||||
| `providerModels.ts` | Cơ quan đăng ký mô hình trung tâm: bí danh của nhà cung cấp bản đồ → ID mô hình. Các chức năng như `getModels()`, `getProviderByAlias()`. |
|
||||
| `codexInstructions.ts` | Hướng dẫn hệ thống được đưa vào các yêu cầu Codex (chỉnh sửa các ràng buộc, quy tắc hộp cát, chính sách phê duyệt). |
|
||||
| `defaultThinkingSignature.ts` | Chữ ký "suy nghĩ" mặc định cho mô hình Claude và Gemini. |
|
||||
| `ollamaModels.ts` | Định nghĩa lược đồ cho các mô hình Ollama cục bộ (tên, kích thước, họ, lượng tử hóa). |
|
||||
|
||||
#### Luồng tải thông tin xác thực
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Người thực thi (`open-sse/executors/`)
|
||||
|
||||
Người thực thi gói gọn **logic dành riêng cho nhà cung cấp** bằng cách sử dụng **Mẫu chiến lược**. Mỗi người thi hành ghi đè các phương thức cơ bản nếu cần.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
| Người thi hành | Nhà cung cấp | Chuyên ngành chính |
|
||||
| ---------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `base.ts` | — | Cơ sở trừu tượng: Xây dựng URL, tiêu đề, logic thử lại, làm mới thông tin xác thực |
|
||||
| `default.ts` | Claude, Song Tử, OpenAI, GLM, Kimi, MiniMax | Làm mới mã thông báo OAuth chung cho các nhà cung cấp tiêu chuẩn |
|
||||
| `antigravity.ts` | Mã đám mây của Google | Tạo ID dự án/phiên, dự phòng nhiều URL, phân tích cú pháp thử lại tùy chỉnh từ thông báo lỗi ("đặt lại sau 2h7m23 giây") |
|
||||
| `cursor.ts` | IDE con trỏ | **Phức tạp nhất**: Xác thực tổng kiểm tra SHA-256, mã hóa yêu cầu Protobuf, EventStream nhị phân → phân tích cú pháp phản hồi SSE |
|
||||
| `codex.ts` | OpenAI Codex | Đưa vào các hướng dẫn hệ thống, quản lý các cấp độ tư duy, loại bỏ các tham số không được hỗ trợ |
|
||||
| `gemini-cli.ts` | Google Song Tử CLI | Xây dựng URL tùy chỉnh (`streamGenerateContent`), làm mới mã thông báo Google OAuth |
|
||||
| `github.ts` | Phi công phụ GitHub | Hệ thống mã thông báo kép (GitHub OAuth + mã thông báo Copilot), bắt chước tiêu đề VSCode |
|
||||
| `kiro.ts` | AWS CodeWhisperer | Phân tích cú pháp nhị phân AWS EventStream, khung sự kiện AMZN, ước tính mã thông báo |
|
||||
| `index.ts` | — | Nhà máy: tên nhà cung cấp bản đồ → lớp người thực thi, với dự phòng mặc định |
|
||||
|
||||
---
|
||||
|
||||
### Trình xử lý 4.3 (`open-sse/handlers/`)
|
||||
|
||||
**Lớp điều phối** — điều phối việc dịch, thực thi, phát trực tuyến và xử lý lỗi.
|
||||
|
||||
| Tập tin | Mục đích |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `chatCore.ts` | **Dàn nhạc trung tâm** (~600 dòng). Xử lý vòng đời yêu cầu hoàn chỉnh: phát hiện định dạng → dịch → gửi người thực thi → phản hồi truyền trực tuyến/không truyền phát → làm mới mã thông báo → xử lý lỗi → ghi nhật ký sử dụng. |
|
||||
| `responsesHandler.ts` | Bộ điều hợp cho API phản hồi của OpenAI: chuyển đổi định dạng Phản hồi → Hoàn thành cuộc trò chuyện → gửi tới `chatCore` → chuyển đổi SSE trở lại định dạng Phản hồi. |
|
||||
| `embeddings.ts` | Trình xử lý tạo nhúng: giải quyết mô hình nhúng → nhà cung cấp, gửi tới API của nhà cung cấp, trả về phản hồi nhúng tương thích với OpenAI. Hỗ trợ hơn 6 nhà cung cấp. |
|
||||
| `imageGeneration.ts` | Trình xử lý tạo hình ảnh: phân giải mô hình hình ảnh → nhà cung cấp, hỗ trợ các chế độ tương thích với OpenAI, hình ảnh Gemini (Chống trọng lực) và dự phòng (Nebius). Trả về hình ảnh base64 hoặc URL. |
|
||||
|
||||
#### Vòng đời yêu cầu (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dịch vụ 4.4 (`open-sse/services/`)
|
||||
|
||||
Logic nghiệp vụ hỗ trợ các trình xử lý và thực thi.
|
||||
|
||||
| Tập tin | Mục đích |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `provider.ts` | **Phát hiện định dạng** (`detectFormat`): phân tích cấu trúc nội dung yêu cầu để xác định các định dạng Claude/OpenAI/Gemini/AntiGravity/Responses (bao gồm `max_tokens` heuristic cho Claude). Ngoài ra: xây dựng URL, xây dựng tiêu đề, chuẩn hóa cấu hình tư duy. Hỗ trợ các nhà cung cấp động `openai-compatible-*` và `anthropic-compatible-*`. |
|
||||
| `model.ts` | Phân tích cú pháp chuỗi mô hình (`claude/model-name` → `{provider: "claude", model: "model-name"}`), phân giải bí danh với khả năng phát hiện xung đột, dọn dẹp đầu vào (từ chối ký tự điều khiển/truyền tải đường dẫn) và phân giải thông tin mô hình với hỗ trợ getter bí danh không đồng bộ. |
|
||||
| `accountFallback.ts` | Xử lý giới hạn tốc độ: thời gian chờ theo cấp số nhân (1 giây → 2 giây → 4 giây → tối đa 2 phút), quản lý thời gian hồi chiêu của tài khoản, phân loại lỗi (lỗi nào kích hoạt dự phòng so với không). |
|
||||
| `tokenRefresh.ts` | Làm mới mã thông báo OAuth cho **mọi nhà cung cấp**: Google (Gemini, AntiGravity), Claude, Codex, Qwen, iFlow, GitHub (Mã thông báo kép OAuth + Copilot), Kiro (AWS SSO OIDC + Social Auth). Bao gồm bộ nhớ đệm chống trùng lặp lời hứa trong quá trình thực hiện và thử lại với thời gian chờ theo cấp số nhân. |
|
||||
| `combo.ts` | **Mô hình kết hợp**: chuỗi mô hình dự phòng. Nếu mô hình A không thành công với lỗi đủ điều kiện dự phòng, hãy thử mô hình B, sau đó là C, v.v. Trả về mã trạng thái ngược dòng thực tế. |
|
||||
| `usage.ts` | Tìm nạp hạn ngạch/dữ liệu sử dụng từ API của nhà cung cấp (hạn ngạch GitHub Copilot, hạn ngạch mô hình AntiGravity, giới hạn tốc độ Codex, phân tích sử dụng Kiro, cài đặt Claude). |
|
||||
| `accountSelector.ts` | Lựa chọn tài khoản thông minh với thuật toán tính điểm: xem xét mức độ ưu tiên, trạng thái sức khỏe, vị trí luân chuyển và trạng thái thời gian hồi chiêu để chọn tài khoản tối ưu cho từng yêu cầu. |
|
||||
| `contextManager.ts` | Quản lý vòng đời ngữ cảnh yêu cầu: tạo và theo dõi các đối tượng ngữ cảnh theo yêu cầu bằng siêu dữ liệu (ID yêu cầu, dấu thời gian, thông tin nhà cung cấp) để gỡ lỗi và ghi nhật ký. |
|
||||
| `ipFilter.ts` | Kiểm soát truy cập dựa trên IP: hỗ trợ chế độ danh sách cho phép và danh sách chặn. Xác thực IP của khách hàng dựa trên các quy tắc đã định cấu hình trước khi xử lý các yêu cầu API. |
|
||||
| `sessionManager.ts` | Theo dõi phiên bằng dấu vân tay của khách hàng: theo dõi các phiên hoạt động bằng cách sử dụng mã định danh khách hàng được băm, theo dõi số lượng yêu cầu và cung cấp số liệu phiên. |
|
||||
| `signatureCache.ts` | Bộ đệm chống trùng lặp dựa trên chữ ký yêu cầu: ngăn chặn các yêu cầu trùng lặp bằng cách lưu vào bộ đệm các chữ ký yêu cầu gần đây và trả về các phản hồi được lưu trong bộ nhớ đệm cho các yêu cầu giống hệt nhau trong một khoảng thời gian. |
|
||||
| `systemPrompt.ts` | Chèn lời nhắc hệ thống toàn cầu: thêm vào trước hoặc thêm lời nhắc hệ thống có thể định cấu hình cho tất cả các yêu cầu, với khả năng xử lý khả năng tương thích của mỗi nhà cung cấp. |
|
||||
| `thinkingBudget.ts` | Quản lý ngân sách mã thông báo lý luận: hỗ trợ các chế độ chuyển tiếp, tự động (cấu hình tư duy dải), tùy chỉnh (ngân sách cố định) và chế độ thích ứng (theo tỷ lệ phức tạp) để kiểm soát mã thông báo suy nghĩ/lý luận. |
|
||||
| `wildcardRouter.ts` | Định tuyến mẫu mô hình ký tự đại diện: phân giải các mẫu ký tự đại diện (ví dụ: `*/claude-*`) thành các cặp nhà cung cấp/mô hình cụ thể dựa trên tính khả dụng và mức độ ưu tiên. |
|
||||
|
||||
#### Chống trùng lặp làm mới mã thông báo
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### Máy trạng thái dự phòng tài khoản
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### Chuỗi Model Combo
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Trình dịch 4.5 (`open-sse/translator/`)
|
||||
|
||||
**Công cụ dịch định dạng** sử dụng hệ thống plugin tự đăng ký.
|
||||
|
||||
#### Kiến trúc
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
| Thư mục | Tập tin | Mô tả |
|
||||
| ------------ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `request/` | 8 dịch giả | Chuyển đổi nội dung yêu cầu giữa các định dạng. Mỗi tệp tự đăng ký thông qua `register(from, to, fn)` khi nhập. |
|
||||
| `response/` | 7 dịch giả | Chuyển đổi các đoạn phản hồi phát trực tuyến giữa các định dạng. Xử lý các loại sự kiện SSE, khối suy nghĩ, lệnh gọi công cụ. |
|
||||
| `helpers/` | 6 người giúp việc | Các tiện ích được chia sẻ: `claudeHelper` (trích xuất lời nhắc hệ thống, cấu hình tư duy), `geminiHelper` (ánh xạ các bộ phận/nội dung), `openaiHelper` (lọc định dạng), `toolCallHelper` (tạo ID, chèn phản hồi bị thiếu), `maxTokensHelper`, `responsesApiHelper`. |
|
||||
| `index.ts` | — | Công cụ dịch thuật: `translateRequest()`, `translateResponse()`, quản lý nhà nước, đăng ký. |
|
||||
| `formats.ts` | — | Hằng số định dạng: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
|
||||
|
||||
#### Thiết kế Key: Plugin tự đăng ký
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 Tiện ích (`open-sse/utils/`)
|
||||
|
||||
| Tập tin | Mục đích |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
|
||||
| `error.ts` | Xây dựng phản hồi lỗi (định dạng tương thích với OpenAI), phân tích lỗi ngược dòng, trích xuất thời gian thử lại AntiGravity từ các thông báo lỗi, phát trực tuyến lỗi SSE. |
|
||||
| `stream.ts` | **SSE Transform Stream** — đường truyền phát trực tuyến cốt lõi. Hai chế độ: `TRANSLATE` (dịch định dạng đầy đủ) và `PASSTHROUGH` (chuẩn hóa + trích xuất cách sử dụng). Xử lý việc lưu vào bộ đệm, ước tính mức sử dụng, theo dõi độ dài nội dung. Các phiên bản bộ mã hóa/giải mã mỗi luồng tránh trạng thái chia sẻ. |
|
||||
| `streamHelpers.ts` | Các tiện ích SSE cấp thấp: `parseSSELine` (không chịu khoảng trắng), `hasValuableContent` (lọc các đoạn trống cho OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (tuần tự hóa SSE nhận biết định dạng với tính năng dọn dẹp `perf_metrics`). |
|
||||
| `usageTracking.ts` | Trích xuất mức sử dụng mã thông báo từ bất kỳ định dạng nào (Claude/OpenAI/Gemini/Responses), ước tính với tỷ lệ ký tự trên mỗi mã thông báo của công cụ/thông báo riêng biệt, bổ sung bộ đệm (giới hạn an toàn 2000 mã thông báo), lọc trường theo định dạng cụ thể, ghi nhật ký bảng điều khiển với màu ANSI. |
|
||||
| `requestLogger.ts` | Ghi nhật ký yêu cầu dựa trên tệp (chọn tham gia qua `ENABLE_REQUEST_LOGS=true`). Tạo thư mục phiên với các tệp được đánh số: `1_req_client.json` → `7_res_client.txt`. Tất cả I/O đều không đồng bộ (bắn và quên). Mặt nạ tiêu đề nhạy cảm. |
|
||||
| `bypassHandler.ts` | Chặn các mẫu cụ thể từ Claude CLI (trích xuất tiêu đề, khởi động, đếm) và trả về các phản hồi giả mạo mà không cần gọi cho bất kỳ nhà cung cấp nào. Hỗ trợ cả phát trực tuyến và không phát trực tuyến. Cố ý giới hạn trong phạm vi Claude CLI. |
|
||||
| `networkProxy.ts` | Phân giải URL proxy gửi đi cho một nhà cung cấp nhất định với mức độ ưu tiên: cấu hình dành riêng cho nhà cung cấp → cấu hình chung → biến môi trường (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Hỗ trợ loại trừ `NO_PROXY`. Cấu hình bộ nhớ đệm trong 30 giây. |
|
||||
|
||||
#### Đường ống truyền phát SSE
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### Cấu trúc phiên ghi nhật ký yêu cầu
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 Lớp ứng dụng (`src/`)
|
||||
|
||||
| Thư mục | Mục đích |
|
||||
| ------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `src/app/` | Giao diện người dùng web, tuyến API, phần mềm trung gian Express, trình xử lý gọi lại OAuth |
|
||||
| `src/lib/` | Truy cập cơ sở dữ liệu (`localDb.ts`, `usageDb.ts`), xác thực, chia sẻ |
|
||||
| `src/mitm/` | Tiện ích proxy trung gian để chặn lưu lượng truy cập của nhà cung cấp |
|
||||
| `src/models/` | Định nghĩa mô hình cơ sở dữ liệu |
|
||||
| `src/shared/` | Trình bao bọc xung quanh các hàm open-sse (nhà cung cấp, luồng, lỗi, v.v.) |
|
||||
| `src/sse/` | Trình xử lý điểm cuối SSE kết nối thư viện open-sse với các tuyến Express |
|
||||
| `src/store/` | Quản lý trạng thái ứng dụng |
|
||||
|
||||
#### Các tuyến API đáng chú ý
|
||||
|
||||
| Tuyến đường | Phương pháp | Mục đích |
|
||||
| --------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `/api/provider-models` | NHẬN/ĐĂNG/XÓA | CRUD cho các mô hình tùy chỉnh cho mỗi nhà cung cấp |
|
||||
| `/api/models/catalog` | NHẬN | Danh mục tổng hợp của tất cả các mô hình (trò chuyện, nhúng, hình ảnh, tùy chỉnh) được nhóm theo nhà cung cấp |
|
||||
| `/api/settings/proxy` | NHẬN/ĐẶT/XÓA | Cấu hình proxy gửi đi theo cấp bậc (`global/providers/combos/keys`) |
|
||||
| `/api/settings/proxy/test` | ĐĂNG | Xác thực kết nối proxy và trả về IP công cộng/độ trễ |
|
||||
| `/v1/providers/[provider]/chat/completions` | ĐĂNG | Hoàn thành trò chuyện dành riêng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `/v1/providers/[provider]/embeddings` | ĐĂNG | Phần nhúng dành riêng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `/v1/providers/[provider]/images/generations` | ĐĂNG | Tạo hình ảnh chuyên dụng cho mỗi nhà cung cấp với xác thực mô hình |
|
||||
| `/api/settings/ip-filter` | NHẬN/ĐẶT | Quản lý danh sách chặn/danh sách IP cho phép |
|
||||
| `/api/settings/thinking-budget` | NHẬN/ĐẶT | Cấu hình ngân sách mã thông báo hợp lý (chuyển qua/tự động/tùy chỉnh/thích ứng) |
|
||||
| `/api/settings/system-prompt` | NHẬN/ĐẶT | Hệ thống nhắc nhở toàn cầu cho tất cả các yêu cầu |
|
||||
| `/api/sessions` | NHẬN | Theo dõi và đo lường phiên hoạt động |
|
||||
| `/api/rate-limits` | NHẬN | Trạng thái giới hạn tỷ lệ cho mỗi tài khoản |
|
||||
|
||||
---
|
||||
|
||||
## 5. Các mẫu thiết kế chính
|
||||
|
||||
### 5.1 Dịch Hub-and-Spoke
|
||||
|
||||
Tất cả các định dạng đều dịch qua **định dạng OpenAI làm trung tâm**. Việc thêm nhà cung cấp mới chỉ yêu cầu viết **một cặp** người dịch (đến/từ OpenAI), chứ không phải N cặp.
|
||||
|
||||
### 5.2 Mẫu chiến lược thực thi
|
||||
|
||||
Mỗi nhà cung cấp có một lớp người thực thi chuyên dụng kế thừa từ `BaseExecutor`. Nhà máy ở `executors/index.ts` chọn đúng nhà máy khi chạy.
|
||||
|
||||
### 5.3 Hệ thống Plugin tự đăng ký
|
||||
|
||||
Mô-đun dịch tự đăng ký khi nhập qua `register()`. Việc thêm người dịch mới chỉ là tạo một tệp và nhập tệp đó.
|
||||
|
||||
### 5.4 Dự phòng tài khoản với thời gian chờ theo cấp số nhân
|
||||
|
||||
Khi nhà cung cấp trả về 429/401/500, hệ thống có thể chuyển sang tài khoản tiếp theo, áp dụng thời gian hồi chiêu theo cấp số nhân (1 giây → 2 giây → 4 giây → tối đa 2 phút).
|
||||
|
||||
### 5.5 Chuỗi mô hình kết hợp
|
||||
|
||||
Một "combo" nhóm nhiều chuỗi `provider/model`. Nếu lần đầu tiên không thành công, hãy tự động chuyển sang lần tiếp theo.
|
||||
|
||||
### 5.6 Bản dịch phát trực tuyến có trạng thái
|
||||
|
||||
Bản dịch phản hồi duy trì trạng thái trên các khối SSE (theo dõi khối suy nghĩ, tích lũy lệnh gọi công cụ, lập chỉ mục khối nội dung) thông qua cơ chế `initState()`.
|
||||
|
||||
### 5.7 Bộ đệm an toàn sử dụng
|
||||
|
||||
Bộ đệm 2000 mã thông báo được thêm vào mức sử dụng được báo cáo để ngăn khách hàng đạt đến giới hạn cửa sổ ngữ cảnh do quá tải từ lời nhắc hệ thống và dịch định dạng.
|
||||
|
||||
---
|
||||
|
||||
## 6. Các định dạng được hỗ trợ
|
||||
|
||||
| Định dạng | Hướng | Mã định danh |
|
||||
| ---------------------------- | ------------ | ------------------ |
|
||||
| Hoàn thành trò chuyện OpenAI | nguồn + đích | `openai` |
|
||||
| API phản hồi OpenAI | nguồn + đích | `openai-responses` |
|
||||
| Claude nhân loại | nguồn + đích | `claude` |
|
||||
| Google Song Tử | nguồn + đích | `gemini` |
|
||||
| Google Song Tử CLI | chỉ mục tiêu | `gemini-cli` |
|
||||
| Phản lực hấp dẫn | nguồn + đích | `antigravity` |
|
||||
| AWS Kiro | chỉ mục tiêu | `kiro` |
|
||||
| Con trỏ | chỉ mục tiêu | `cursor` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Nhà cung cấp được hỗ trợ
|
||||
|
||||
| Nhà cung cấp | Phương thức xác thực | Người thi hành | Ghi chú chính |
|
||||
| ------------------------ | ---------------------------- | ---------------- | ------------------------------------------------------- |
|
||||
| Claude nhân loại | Khóa API hoặc OAuth | Mặc định | Sử dụng tiêu đề `x-api-key` |
|
||||
| Google Song Tử | Khóa API hoặc OAuth | Mặc định | Sử dụng tiêu đề `x-goog-api-key` |
|
||||
| Google Song Tử CLI | OAuth | Song TửCLI | Sử dụng điểm cuối `streamGenerateContent` |
|
||||
| Phản lực hấp dẫn | OAuth | Phản lực hấp dẫn | Dự phòng nhiều URL, phân tích cú pháp thử lại tùy chỉnh |
|
||||
| OpenAI | Khóa API | Mặc định | Xác thực Bearer tiêu chuẩn |
|
||||
| Codex | OAuth | Codex | Đưa ra hướng dẫn hệ thống, quản lý tư duy |
|
||||
| Phi công phụ GitHub | Mã thông báo OAuth + Copilot | Github | Mã thông báo kép, bắt chước tiêu đề VSCode |
|
||||
| Kiro (AWS) | AWS SSO OIDC hoặc Xã hội | Kiro | Phân tích cú pháp luồng sự kiện nhị phân |
|
||||
| IDE con trỏ | Xác thực tổng kiểm tra | Con trỏ | Mã hóa Protobuf, tổng kiểm tra SHA-256 |
|
||||
| Qwen | OAuth | Mặc định | Xác thực chuẩn |
|
||||
| iFlow | OAuth (Cơ bản + Mang) | Mặc định | Tiêu đề xác thực kép |
|
||||
| OpenRouter | Khóa API | Mặc định | Xác thực Bearer tiêu chuẩn |
|
||||
| GLM, Kimi, MiniMax | Khóa API | Mặc định | Tương thích với Claude, sử dụng `x-api-key` |
|
||||
| `openai-compatible-*` | Khóa API | Mặc định | Động: mọi điểm cuối tương thích với OpenAI |
|
||||
| `anthropic-compatible-*` | Khóa API | Mặc định | Động: mọi điểm cuối tương thích với Claude |
|
||||
|
||||
---
|
||||
|
||||
## 8. Tóm tắt luồng dữ liệu
|
||||
|
||||
### Yêu cầu phát trực tuyến
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### Yêu cầu không phát trực tuyến
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### Luồng bỏ qua (Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
77
docs/i18n/vi/FEATURES.md
Normal file
77
docs/i18n/vi/FEATURES.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OmniRoute — Thư viện tính năng bảng điều khiển
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../FEATURES.md) | 🇧🇷 [Português (Brasil)](../pt-BR/FEATURES.md) | 🇪🇸 [Español](../es/FEATURES.md) | 🇫🇷 [Français](../fr/FEATURES.md) | 🇮🇹 [Italiano](../it/FEATURES.md) | 🇷🇺 [Русский](../ru/FEATURES.md) | 🇨🇳 [中文 (简体)](../zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](../de/FEATURES.md) | 🇮🇳 [हिन्दी](../in/FEATURES.md) | 🇹🇭 [ไทย](../th/FEATURES.md) | 🇺🇦 [Українська](../uk-UA/FEATURES.md) | 🇸🇦 [العربية](../ar/FEATURES.md) | 🇯🇵 [日本語](../ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](../vi/FEATURES.md) | 🇧🇬 [Български](../bg/FEATURES.md) | 🇩🇰 [Dansk](../da/FEATURES.md) | 🇫🇮 [Suomi](../fi/FEATURES.md) | 🇮🇱 [עברית](../he/FEATURES.md) | 🇭🇺 [Magyar](../hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](../id/FEATURES.md) | 🇰🇷 [한국어](../ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](../ms/FEATURES.md) | 🇳🇱 [Nederlands](../nl/FEATURES.md) | 🇳🇴 [Norsk](../no/FEATURES.md) | 🇵🇹 [Português (Portugal)](../pt/FEATURES.md) | 🇷🇴 [Română](../ro/FEATURES.md) | 🇵🇱 [Polski](../pl/FEATURES.md) | 🇸🇰 [Slovenčina](../sk/FEATURES.md) | 🇸🇪 [Svenska](../sv/FEATURES.md) | 🇵🇭 [Filipino](../phi/FEATURES.md)
|
||||
|
||||
Hướng dẫn trực quan cho mọi phần của bảng điều khiển OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Nhà cung cấp
|
||||
|
||||
Quản lý kết nối của nhà cung cấp AI: Nhà cung cấp OAuth (Claude Code, Codex, Gemini CLI), nhà cung cấp khóa API (Groq, DeepSeek, OpenRouter) và nhà cung cấp miễn phí (iFlow, Qwen, Kiro).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 Combo
|
||||
|
||||
Tạo các tổ hợp định tuyến mô hình với 6 chiến lược: điền trước, quay vòng, sức mạnh của hai lựa chọn, ngẫu nhiên, ít sử dụng nhất và tối ưu hóa chi phí. Mỗi combo kết hợp nhiều mô hình với tính năng dự phòng tự động.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 Phân tích
|
||||
|
||||
Phân tích sử dụng toàn diện với mức tiêu thụ mã thông báo, ước tính chi phí, bản đồ nhiệt hoạt động, biểu đồ phân phối hàng tuần và phân tích theo từng nhà cung cấp.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 Sức khỏe hệ thống
|
||||
|
||||
Giám sát thời gian thực: thời gian hoạt động, bộ nhớ, phiên bản, phần trăm độ trễ (p50/p95/p99), thống kê bộ đệm và trạng thái ngắt mạch của nhà cung cấp.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Sân chơi dịch thuật
|
||||
|
||||
Bốn chế độ để gỡ lỗi các bản dịch API: **Playground** (trình chuyển đổi định dạng), **Chat Test** (yêu cầu trực tiếp), **Test Bench** (kiểm tra hàng loạt) và **Live Monitor** (luồng thời gian thực).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Cài đặt
|
||||
|
||||
Cài đặt chung, lưu trữ hệ thống, quản lý sao lưu (xuất/nhập cơ sở dữ liệu), giao diện (chế độ tối/sáng), bảo mật (bao gồm bảo vệ điểm cuối API và chặn nhà cung cấp tùy chỉnh), định tuyến, khả năng phục hồi và cấu hình nâng cao.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Công cụ CLI
|
||||
|
||||
Cấu hình bằng một cú nhấp chuột cho các công cụ mã hóa AI: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code và AntiGravity.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📝 Nhật ký yêu cầu
|
||||
|
||||
Ghi nhật ký yêu cầu theo thời gian thực với tính năng lọc theo nhà cung cấp, kiểu máy, tài khoản và khóa API. Hiển thị mã trạng thái, mức sử dụng mã thông báo, độ trễ và chi tiết phản hồi.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 Điểm cuối API
|
||||
|
||||
Điểm cuối API hợp nhất của bạn với phân tích khả năng: Hoàn thành trò chuyện, Nhúng, Tạo hình ảnh, Xếp hạng lại, Phiên âm âm thanh và khóa API đã đăng ký.
|
||||
|
||||

|
||||
219
docs/i18n/vi/TROUBLESHOOTING.md
Normal file
219
docs/i18n/vi/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Khắc phục sự cố
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](../pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](../es/TROUBLESHOOTING.md) | 🇫🇷 [Français](../fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](../it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](../ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](../zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](../de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](../in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](../th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](../uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](../ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](../ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](../vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](../bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](../da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](../fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](../he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](../hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](../id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](../ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](../ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](../nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](../no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](../pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](../ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](../pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](../sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](../sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](../phi/TROUBLESHOOTING.md)
|
||||
|
||||
Các vấn đề thường gặp và giải pháp cho OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Sửa nhanh
|
||||
|
||||
| Vấn đề | Giải pháp |
|
||||
| ----------------------------------------- | ----------------------------------------------------------------- |
|
||||
| Đăng nhập lần đầu không hoạt động | Kiểm tra `INITIAL_PASSWORD` trong `.env` (mặc định: `123456`) |
|
||||
| Bảng điều khiển mở sai cổng | Đặt `PORT=20128` và `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| Không có nhật ký yêu cầu nào dưới `logs/` | Đặt `ENABLE_REQUEST_LOGS=true` |
|
||||
| EACCES: quyền bị từ chối | Đặt `DATA_DIR=/path/to/writable/dir` để ghi đè `~/.omniroute` |
|
||||
| Chiến lược định tuyến không tiết kiệm | Cập nhật lên v1.4.11+ (Sửa lược đồ Zod để duy trì cài đặt) |
|
||||
|
||||
---
|
||||
|
||||
## Vấn đề về nhà cung cấp
|
||||
|
||||
### "Mô hình ngôn ngữ không cung cấp thông báo"
|
||||
|
||||
**Nguyên nhân:** Đã hết hạn ngạch nhà cung cấp.
|
||||
|
||||
**Sửa chữa:**
|
||||
|
||||
1. Kiểm tra trình theo dõi hạn ngạch trên trang tổng quan
|
||||
2. Sử dụng kết hợp với các tầng dự phòng
|
||||
3. Chuyển sang cấp rẻ hơn/miễn phí
|
||||
|
||||
### Giới hạn tỷ lệ
|
||||
|
||||
**Lý do:** Đã hết hạn mức đăng ký.
|
||||
|
||||
**Sửa chữa:**
|
||||
|
||||
- Thêm dự phòng: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- Sử dụng GLM/MiniMax làm bản sao lưu giá rẻ
|
||||
|
||||
### Mã thông báo OAuth đã hết hạn
|
||||
|
||||
OmniRoute tự động làm mới mã thông báo. Nếu vấn đề vẫn tiếp diễn:
|
||||
|
||||
1. Bảng điều khiển → Nhà cung cấp → Kết nối lại
|
||||
2. Xóa và thêm lại kết nối nhà cung cấp
|
||||
|
||||
---
|
||||
|
||||
## Sự cố về đám mây
|
||||
|
||||
### Lỗi đồng bộ hóa đám mây
|
||||
|
||||
1. Xác minh `BASE_URL` trỏ tới phiên bản đang chạy của bạn (ví dụ: `http://localhost:20128`)
|
||||
2. Xác minh `CLOUD_URL` trỏ đến điểm cuối đám mây của bạn (ví dụ: `https://omniroute.dev`)
|
||||
3. Giữ các giá trị `NEXT_PUBLIC_*` được căn chỉnh với các giá trị phía máy chủ
|
||||
|
||||
### Đám mây `stream=false` Trả về 500
|
||||
|
||||
**Triệu chứng:** `Unexpected token 'd'...` trên điểm cuối đám mây đối với các cuộc gọi không phát trực tuyến.
|
||||
|
||||
**Lý do:** Ngược dòng trả về tải trọng SSE trong khi khách hàng mong đợi JSON.
|
||||
|
||||
**Giải pháp:** Sử dụng `stream=true` cho cuộc gọi trực tiếp qua đám mây. Thời gian chạy cục bộ bao gồm dự phòng SSE→JSON.
|
||||
|
||||
### Cloud cho biết Đã kết nối nhưng "Khóa API không hợp lệ"
|
||||
|
||||
1. Tạo khóa mới từ bảng điều khiển cục bộ (`/api/keys`)
|
||||
2. Chạy đồng bộ đám mây: Bật Đám mây → Đồng bộ hóa ngay
|
||||
3. Khóa cũ/không được đồng bộ hóa vẫn có thể trả về `401` trên đám mây
|
||||
|
||||
---
|
||||
|
||||
## Vấn đề về Docker
|
||||
|
||||
### Công cụ CLI hiển thị chưa được cài đặt
|
||||
|
||||
1. Kiểm tra các trường thời gian chạy: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. Đối với chế độ di động: sử dụng mục tiêu hình ảnh `runner-cli` (CLI đi kèm)
|
||||
3. Đối với chế độ gắn máy chủ: đặt `CLI_EXTRA_PATHS` và gắn thư mục bin máy chủ ở chế độ chỉ đọc
|
||||
4. Nếu `installed=true` và `runnable=false`: đã tìm thấy nhị phân nhưng kiểm tra tình trạng không thành công
|
||||
|
||||
### Xác thực thời gian chạy nhanh
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vấn đề về chi phí
|
||||
|
||||
### Chi phí cao
|
||||
|
||||
1. Kiểm tra số liệu thống kê sử dụng trong Bảng điều khiển → Mức sử dụng
|
||||
2. Chuyển model chính sang GLM/MiniMax
|
||||
3. Sử dụng bậc miễn phí (Gemini CLI, iFlow) cho các tác vụ không quan trọng
|
||||
4. Đặt ngân sách chi phí cho mỗi khóa API: Bảng điều khiển → Khóa API → Ngân sách
|
||||
|
||||
---
|
||||
|
||||
## Gỡ lỗi
|
||||
|
||||
### Kích hoạt nhật ký yêu cầu
|
||||
|
||||
Đặt `ENABLE_REQUEST_LOGS=true` trong tệp `.env` của bạn. Nhật ký xuất hiện trong thư mục `logs/`.
|
||||
|
||||
### Kiểm tra sức khỏe nhà cung cấp
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### Bộ nhớ thời gian chạy
|
||||
|
||||
- Trạng thái chính: `${DATA_DIR}/db.json` (nhà cung cấp, tổ hợp, bí danh, khóa, cài đặt)
|
||||
- Cách sử dụng: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
|
||||
- Nhật ký yêu cầu: `<repo>/logs/...` (khi `ENABLE_REQUEST_LOGS=true`)
|
||||
|
||||
---
|
||||
|
||||
## Sự cố ngắt mạch
|
||||
|
||||
### Nhà cung cấp bị kẹt ở trạng thái MỞ
|
||||
|
||||
Khi cầu dao của nhà cung cấp MỞ, các yêu cầu sẽ bị chặn cho đến khi hết thời gian hồi chiêu.
|
||||
|
||||
**Sửa chữa:**
|
||||
|
||||
1. Đi tới **Bảng điều khiển → Cài đặt → Khả năng phục hồi**
|
||||
2. Kiểm tra thẻ cầu dao của nhà cung cấp bị ảnh hưởng
|
||||
3. Nhấp vào **Đặt lại tất cả** để xóa tất cả các bộ ngắt hoặc đợi hết thời gian hồi chiêu
|
||||
4. Xác minh nhà cung cấp thực sự có sẵn trước khi đặt lại
|
||||
|
||||
### Nhà cung cấp liên tục ngắt cầu dao
|
||||
|
||||
Nếu nhà cung cấp liên tục chuyển sang trạng thái MỞ:
|
||||
|
||||
1. Kiểm tra **Bảng điều khiển → Sức khỏe → Tình trạng nhà cung cấp** để biết kiểu lỗi
|
||||
2. Đi tới **Cài đặt → Khả năng phục hồi → Hồ sơ nhà cung cấp** và tăng ngưỡng thất bại
|
||||
3. Kiểm tra xem nhà cung cấp có thay đổi giới hạn API hay yêu cầu xác thực lại không
|
||||
4. Xem lại phép đo từ xa về độ trễ - độ trễ cao có thể gây ra lỗi dựa trên thời gian chờ
|
||||
|
||||
---
|
||||
|
||||
## Sự cố phiên âm âm thanh
|
||||
|
||||
### Lỗi "Mẫu máy không được hỗ trợ"
|
||||
|
||||
- Đảm bảo bạn đang sử dụng đúng tiền tố: `deepgram/nova-3` hoặc `assemblyai/best`
|
||||
- Xác minh nhà cung cấp được kết nối trong **Bảng điều khiển → Nhà cung cấp**
|
||||
|
||||
### Phiên âm trả về trống hoặc không thành công
|
||||
|
||||
- Kiểm tra các định dạng âm thanh được hỗ trợ: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
|
||||
- Xác minh kích thước tệp nằm trong giới hạn của nhà cung cấp (thường < 25 MB)
|
||||
- Kiểm tra tính hợp lệ của khóa API nhà cung cấp trong thẻ nhà cung cấp
|
||||
|
||||
---
|
||||
|
||||
## Gỡ lỗi trình dịch
|
||||
|
||||
Sử dụng **Trang tổng quan → Trình dịch** để gỡ lỗi các vấn đề dịch định dạng:
|
||||
|
||||
| Chế độ | Khi nào nên sử dụng |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| **Sân chơi** | So sánh các định dạng đầu vào/đầu ra cạnh nhau — dán một yêu cầu không thành công để xem nó dịch như thế nào |
|
||||
| **Người kiểm tra trò chuyện** | Gửi tin nhắn trực tiếp và kiểm tra toàn bộ tải trọng yêu cầu/phản hồi bao gồm các tiêu đề |
|
||||
| **Bàn thử nghiệm** | Chạy thử nghiệm hàng loạt trên các kết hợp định dạng để tìm ra bản dịch nào bị lỗi |
|
||||
| **Màn hình trực tiếp** | Xem luồng yêu cầu theo thời gian thực để nắm bắt các vấn đề dịch thuật không liên tục |
|
||||
|
||||
### Các vấn đề định dạng thường gặp
|
||||
|
||||
- **Thẻ tư duy không xuất hiện** — Kiểm tra xem nhà cung cấp mục tiêu có hỗ trợ tư duy và cài đặt ngân sách tư duy hay không
|
||||
- **Giảm cuộc gọi công cụ** — Một số bản dịch định dạng có thể loại bỏ các trường không được hỗ trợ; xác minh ở chế độ Playground
|
||||
- **Thiếu lời nhắc hệ thống** — Claude và Gemini xử lý lời nhắc hệ thống theo cách khác nhau; kiểm tra đầu ra bản dịch
|
||||
- **SDK trả về chuỗi thô thay vì đối tượng** — Đã sửa trong v1.1.0: trình khử trùng phản hồi hiện loại bỏ các trường không chuẩn (`x_groq`, `usage_breakdown`, v.v.) gây ra lỗi xác thực OpenAI SDK Pydantic
|
||||
- **GLM/ERNIE từ chối vai trò `system`** — Đã sửa trong v1.1.0: bộ chuẩn hóa vai trò tự động hợp nhất các thông báo hệ thống thành thông báo người dùng cho các kiểu máy không tương thích
|
||||
- **`developer` vai trò không được nhận dạng** — Đã sửa trong v1.1.0: tự động chuyển đổi thành `system` cho các nhà cung cấp không phải OpenAI
|
||||
- **`json_schema` không hoạt động với Gemini** — Đã sửa trong v1.1.0: `response_format` hiện được chuyển đổi thành `responseMimeType` + `responseSchema` của Gemini
|
||||
|
||||
---
|
||||
|
||||
## Cài đặt khả năng phục hồi
|
||||
|
||||
### Giới hạn tỷ lệ tự động không kích hoạt
|
||||
|
||||
- Giới hạn tỷ lệ tự động chỉ áp dụng cho nhà cung cấp khóa API (không phải OAuth/đăng ký)
|
||||
- Xác minh **Cài đặt → Khả năng phục hồi → Hồ sơ nhà cung cấp** đã bật giới hạn tỷ lệ tự động
|
||||
- Kiểm tra xem nhà cung cấp có trả về `429` mã trạng thái hoặc tiêu đề `Retry-After` không
|
||||
|
||||
### Điều chỉnh độ trễ theo cấp số nhân
|
||||
|
||||
Hồ sơ nhà cung cấp hỗ trợ các cài đặt này:
|
||||
|
||||
- **Độ trễ cơ bản** — Thời gian chờ ban đầu sau lần thất bại đầu tiên (mặc định: 1 giây)
|
||||
- **Độ trễ tối đa** — Giới hạn thời gian chờ tối đa (mặc định: 30 giây)
|
||||
- **Hệ số** — Độ trễ tăng lên bao nhiêu cho mỗi lần thất bại liên tiếp (mặc định: 2x)
|
||||
|
||||
### Đàn chống sấm sét
|
||||
|
||||
Khi nhiều yêu cầu đồng thời gặp phải một nhà cung cấp có tỷ lệ giới hạn, OmniRoute sử dụng mutex + giới hạn tốc độ tự động để tuần tự hóa các yêu cầu và ngăn chặn lỗi xếp tầng. Điều này là tự động đối với các nhà cung cấp khóa API.
|
||||
|
||||
---
|
||||
|
||||
## Vẫn bị kẹt?
|
||||
|
||||
- **Vấn đề về GitHub**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **Kiến trúc**: Xem [**OMNI_TOKEN_55**](ARCHITECTURE.md) để biết chi tiết nội bộ
|
||||
- **Tham khảo API**: Xem [**OMNI_TOKEN_56**](API_REFERENCE.md) để biết tất cả các điểm cuối
|
||||
- **Bảng điều khiển sức khỏe**: Kiểm tra **Bảng điều khiển → Sức khỏe** để biết trạng thái hệ thống theo thời gian thực
|
||||
- **Trình dịch**: Sử dụng **Bảng điều khiển → Trình dịch** để gỡ lỗi các vấn đề về định dạng
|
||||
698
docs/i18n/vi/USER_GUIDE.md
Normal file
698
docs/i18n/vi/USER_GUIDE.md
Normal file
@@ -0,0 +1,698 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](../es/USER_GUIDE.md) | 🇫🇷 [Français](../fr/USER_GUIDE.md) | 🇮🇹 [Italiano](../it/USER_GUIDE.md) | 🇷🇺 [Русский](../ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](../de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](../in/USER_GUIDE.md) | 🇹🇭 [ไทย](../th/USER_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](../ar/USER_GUIDE.md) | 🇯🇵 [日本語](../ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/USER_GUIDE.md) | 🇧🇬 [Български](../bg/USER_GUIDE.md) | 🇩🇰 [Dansk](../da/USER_GUIDE.md) | 🇫🇮 [Suomi](../fi/USER_GUIDE.md) | 🇮🇱 [עברית](../he/USER_GUIDE.md) | 🇭🇺 [Magyar](../hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/USER_GUIDE.md) | 🇰🇷 [한국어](../ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](../nl/USER_GUIDE.md) | 🇳🇴 [Norsk](../no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/USER_GUIDE.md) | 🇷🇴 [Română](../ro/USER_GUIDE.md) | 🇵🇱 [Polski](../pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/USER_GUIDE.md) | 🇸🇪 [Svenska](../sv/USER_GUIDE.md) | 🇵🇭 [Filipino](../phi/USER_GUIDE.md)
|
||||
|
||||
#Hướng dẫn sử dụng
|
||||
|
||||
Hướng dẫn đầy đủ về cách định cấu hình nhà cung cấp, tạo tổ hợp, tích hợp công cụ CLI và triển khai OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Mục lục
|
||||
|
||||
- [Pricing at a Glance](#-pricing-at-a-glance)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Provider Setup](#-provider-setup)
|
||||
- [CLI Integration](#-cli-integration)
|
||||
- [Deployment](#-deployment)
|
||||
- [Available Models](#-available-models)
|
||||
- [Advanced Features](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 Sơ lược về giá
|
||||
|
||||
| Bậc | Nhà cung cấp | Chi phí | Đặt lại hạn ngạch | Tốt nhất cho |
|
||||
| --------------- | ------------------- | ---------------------------- | --------------------- | -------------------------- |
|
||||
| **💳 ĐĂNG KÝ** | Mã Claude (Pro) | $20/tháng | 5h + hàng tuần | Đã đăng ký |
|
||||
| | Codex (Plus/Pro) | $20-200/tháng | 5h + hàng tuần | Người dùng OpenAI |
|
||||
| | Song Tử CLI | **MIỄN PHÍ** | 180K/tháng + 1K/ngày | Mọi người! |
|
||||
| | Phi công phụ GitHub | $10-19/tháng | Hàng tháng | Người dùng GitHub |
|
||||
| **🔑 KHÓA API** | DeepSeek | Trả tiền cho mỗi lần sử dụng | Không có | Lý luận giá rẻ |
|
||||
| | Groq | Trả tiền cho mỗi lần sử dụng | Không có | Suy luận cực nhanh |
|
||||
| | xAI (Grok) | Trả tiền cho mỗi lần sử dụng | Không có | Lý luận Grok 4 |
|
||||
| | Mistral | Trả tiền cho mỗi lần sử dụng | Không có | Các mô hình do EU đăng cai |
|
||||
| | Lúng túng | Trả tiền cho mỗi lần sử dụng | Không có | Tăng cường tìm kiếm |
|
||||
| | Cùng AI | Trả tiền cho mỗi lần sử dụng | Không có | Mô hình nguồn mở |
|
||||
| | Pháo hoa AI | Trả tiền cho mỗi lần sử dụng | Không có | Hình ảnh FLUX nhanh |
|
||||
| | Não | Trả tiền cho mỗi lần sử dụng | Không có | Tốc độ quy mô wafer |
|
||||
| | Kết hợp | Trả tiền cho mỗi lần sử dụng | Không có | Lệnh R+ RAG |
|
||||
| | NVIDIA NIM | Trả tiền cho mỗi lần sử dụng | Không có | Mô hình doanh nghiệp |
|
||||
| **💰 RẺ** | GLM-4.7 | 0,6 USD/1 triệu USD | 10 giờ sáng hàng ngày | Dự phòng ngân sách |
|
||||
| | MiniMax M2.1 | 0,2 USD/1 triệu USD | lăn 5 giờ | Lựa chọn rẻ nhất |
|
||||
| | Kimi K2 | $9/tháng căn hộ | 10 triệu token/tháng | Chi phí dự đoán |
|
||||
| **🆓 MIỄN PHÍ** | iFlow | $0 | Không giới hạn | 8 mẫu miễn phí |
|
||||
| | Qwen | $0 | Không giới hạn | 3 mẫu miễn phí |
|
||||
| | Kiro | $0 | Không giới hạn | Claude miễn phí |
|
||||
|
||||
**💡 Mẹo chuyên nghiệp:** Bắt đầu với Gemini CLI (180K miễn phí/tháng) + combo iFlow (miễn phí không giới hạn) = chi phí $0!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Trường hợp sử dụng
|
||||
|
||||
### Trường hợp 1: "Tôi có đăng ký Claude Pro"
|
||||
|
||||
**Vấn đề:** Hạn ngạch hết hạn không được sử dụng, giới hạn tốc độ trong quá trình mã hóa nặng
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Trường hợp 2: "Tôi muốn chi phí bằng 0"
|
||||
|
||||
**Vấn đề:** Không đủ khả năng đăng ký, cần mã hóa AI đáng tin cậy
|
||||
|
||||
```
|
||||
Combo: "free-forever"
|
||||
1. gc/gemini-3-flash (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited free)
|
||||
3. qw/qwen3-coder-plus (unlimited free)
|
||||
|
||||
Monthly cost: $0
|
||||
Quality: Production-ready models
|
||||
```
|
||||
|
||||
### Trường hợp 3: "Tôi cần code 24/7, không bị gián đoạn"
|
||||
|
||||
**Vấn đề:** Thời hạn, không đủ khả năng cho thời gian ngừng hoạt động
|
||||
|
||||
```
|
||||
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. minimax/MiniMax-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)
|
||||
```
|
||||
|
||||
### Trường hợp 4: "Tôi muốn AI MIỄN PHÍ trong OpenClaw"
|
||||
|
||||
**Vấn đề:** Cần trợ lý AI trong ứng dụng nhắn tin, hoàn toàn miễn phí
|
||||
|
||||
```
|
||||
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...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Thiết lập nhà cung cấp
|
||||
|
||||
### 🔐 Nhà cung cấp đăng ký
|
||||
|
||||
#### Mã Claude (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
|
||||
```
|
||||
|
||||
**Mẹo chuyên nghiệp:** Sử dụng Opus cho các tác vụ phức tạp, Sonnet cho tốc độ. OmniRoute theo dõi hạn ngạch cho mỗi mô hình!
|
||||
|
||||
#### OpenAI Codex (Plus/Pro)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/gpt-5.2-codex
|
||||
cx/gpt-5.1-codex-max
|
||||
```
|
||||
|
||||
#### Gemini CLI (MIỄN PHÍ 180K/tháng!)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Gemini CLI
|
||||
→ Google OAuth
|
||||
→ 180K completions/month + 1K/day
|
||||
|
||||
Models:
|
||||
gc/gemini-3-flash-preview
|
||||
gc/gemini-2.5-pro
|
||||
```
|
||||
|
||||
**Giá trị tốt nhất:** Cấp miễn phí rất lớn! Sử dụng điều này trước các bậc trả phí.
|
||||
|
||||
#### GitHub Copilot
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ Monthly reset (1st of month)
|
||||
|
||||
Models:
|
||||
gh/gpt-5
|
||||
gh/claude-4.5-sonnet
|
||||
gh/gemini-3-pro
|
||||
```
|
||||
|
||||
### 💰 Nhà cung cấp giá rẻ
|
||||
|
||||
#### GLM-4.7 (Đặt lại hàng ngày, 0,6 USD/1 triệu USD)
|
||||
|
||||
1. Đăng ký: [Zhipu AI](https://open.bigmodel.cn/)
|
||||
2. Nhận khóa API từ Gói mã hóa
|
||||
3. Bảng điều khiển → Thêm khóa API: Nhà cung cấp: `glm`, Khóa API: `your-key`
|
||||
|
||||
**Sử dụng:** `glm/glm-4.7` — **Mẹo chuyên nghiệp:** Gói mã hóa cung cấp hạn ngạch 3× với chi phí 1/7! Đặt lại vào 10:00 sáng hàng ngày.
|
||||
|
||||
#### MiniMax M2.1 (đặt lại 5 giờ, 0,20 USD/1 triệu)
|
||||
|
||||
1. Đăng ký: [MiniMax](https://www.minimax.io/)
|
||||
2. Nhận khóa API → Bảng điều khiển → Thêm khóa API
|
||||
|
||||
**Sử dụng:** `minimax/MiniMax-M2.1` — **Mẹo chuyên nghiệp:** Tùy chọn rẻ nhất cho bối cảnh dài (1 triệu mã thông báo)!
|
||||
|
||||
#### Kimi K2 ($9/tháng cố định)
|
||||
|
||||
1. Đăng ký: [Moonshot AI](https://platform.moonshot.ai/)
|
||||
2. Nhận khóa API → Bảng điều khiển → Thêm khóa API
|
||||
|
||||
**Sử dụng:** `kimi/kimi-latest` — **Mẹo chuyên nghiệp:** Đã sửa lỗi 9 USD/tháng cho 10 triệu mã thông báo = 0,90 USD/1 triệu chi phí hiệu quả!
|
||||
|
||||
### 🆓 Nhà cung cấp MIỄN PHÍ
|
||||
|
||||
#### iFlow (8 mẫu MIỄN PHÍ)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect 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 mẫu MIỄN PHÍ)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Qwen → Device code auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### Kiro (Claude MIỄN PHÍ)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
Models: kr/claude-sonnet-4.5, kr/claude-haiku-4.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Combo
|
||||
|
||||
### Ví dụ 1: Tối đa hóa đăng ký → Sao lưu giá rẻ
|
||||
|
||||
```
|
||||
Dashboard → Combos → Create New
|
||||
|
||||
Name: premium-coding
|
||||
Models:
|
||||
1. cc/claude-opus-4-6 (Subscription primary)
|
||||
2. glm/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### Ví dụ 2: Chỉ miễn phí (Không mất phí)
|
||||
|
||||
```
|
||||
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!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Tích hợp CLI
|
||||
|
||||
### IDE con trỏ
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### Mã Claude
|
||||
|
||||
Chỉnh sửa `~/.claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### OpenClaw
|
||||
|
||||
Chỉnh sửa `~/.openclaw/openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Hoặc sử dụng Bảng điều khiển:** Công cụ CLI → OpenClaw → Tự động cấu hình
|
||||
|
||||
### Cline / Tiếp tục / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Triển khai
|
||||
|
||||
### Triển khai VPS
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
Để biết chế độ tích hợp máy chủ với các tệp nhị phân CLI, hãy xem phần Docker trong tài liệu chính.
|
||||
|
||||
### Biến môi trường
|
||||
|
||||
| Biến | Mặc định | Mô tả |
|
||||
| --------------------- | ------------------------------------ | ---------------------------------------------------------- |
|
||||
| `JWT_SECRET` | `omniroute-default-secret-change-me` | Bí mật ký kết JWT (**thay đổi trong sản xuất**) |
|
||||
| `INITIAL_PASSWORD` | `123456` | Mật khẩu đăng nhập lần đầu |
|
||||
| `DATA_DIR` | `~/.omniroute` | Thư mục dữ liệu (db, cách sử dụng, nhật ký) |
|
||||
| `PORT` | mặc định khung | Cổng dịch vụ (`20128` trong ví dụ) |
|
||||
| `HOSTNAME` | mặc định khung | Máy chủ liên kết (Docker mặc định là `0.0.0.0`) |
|
||||
| `NODE_ENV` | mặc định thời gian chạy | Đặt `production` để triển khai |
|
||||
| `BASE_URL` | `http://localhost:20128` | URL cơ sở nội bộ phía máy chủ |
|
||||
| `CLOUD_URL` | `https://omniroute.dev` | URL cơ sở điểm cuối đồng bộ hóa đám mây |
|
||||
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | Bí mật HMAC cho các khóa API được tạo |
|
||||
| `REQUIRE_API_KEY` | `false` | Thực thi khóa API Bearer trên `/v1/*` |
|
||||
| `ENABLE_REQUEST_LOGS` | `false` | Bật nhật ký yêu cầu/phản hồi |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | Buộc `Secure` cookie xác thực (đằng sau proxy ngược HTTPS) |
|
||||
|
||||
Để biết tham chiếu đầy đủ về biến môi trường, hãy xem [README](../README.md).
|
||||
|
||||
---
|
||||
|
||||
## 📊 Mẫu có sẵn
|
||||
|
||||
<details>
|
||||
<summary><b>Xem tất cả các mẫu có sẵn</b></summary>
|
||||
|
||||
**Mã Claude (`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/`)** — MIỄN PHÍ: `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 USD/1 triệu: `glm/glm-4.7`
|
||||
|
||||
**MiniMax (`minimax/`)** — 0,2 USD/1 triệu: `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/qwen3-coder-plus`, `qw/qwen3-coder-flash`
|
||||
|
||||
**Kiro (`kr/`)** — MIỄN PHÍ: `kr/claude-sonnet-4.5`, `kr/claude-haiku-4.5`
|
||||
|
||||
**DeepSeek (`ds/`)**: `ds/deepseek-chat`, `ds/deepseek-reasoner`
|
||||
|
||||
**Groq (`groq/`)**: `groq/llama-3.3-70b-versatile`, `groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI (`xai/`)**: `xai/grok-4`, `xai/grok-4-0709-fast-reasoning`, `xai/grok-code-mini`
|
||||
|
||||
**Mistral (`mistral/`)**: `mistral/mistral-large-2501`, `mistral/codestral-2501`
|
||||
|
||||
**Bối rối (`pplx/`)**: `pplx/sonar-pro`, `pplx/sonar`
|
||||
|
||||
**Cùng AI (`together/`)**: `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**Pháo hoa AI (`fireworks/`)**: `fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**Não (`cerebras/`)**: `cerebras/llama-3.3-70b`
|
||||
|
||||
**Cohere (`cohere/`)**: `cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM (`nvidia/`)**: `nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Tính năng nâng cao
|
||||
|
||||
### Mẫu tùy chỉnh
|
||||
|
||||
Thêm bất kỳ ID mẫu nào vào bất kỳ nhà cung cấp nào mà không cần chờ cập nhật ứng dụng:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
Hoặc sử dụng Trang tổng quan: **Nhà cung cấp → [Nhà cung cấp] → Mô hình tùy chỉnh**.
|
||||
|
||||
### Tuyến đường dành riêng cho nhà cung cấp
|
||||
|
||||
Định tuyến các yêu cầu trực tiếp đến một nhà cung cấp cụ thể với xác thực mô hình:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
Tiền tố nhà cung cấp được tự động thêm vào nếu thiếu. Các mô hình không khớp trả về `400`.
|
||||
|
||||
### Cấu hình proxy mạng
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**Ưu tiên:** Dành riêng cho khóa → Dành riêng cho tổ hợp → Dành riêng cho nhà cung cấp → Toàn cầu → Môi trường.
|
||||
|
||||
### API danh mục mẫu
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
Trả về các mô hình được nhóm theo nhà cung cấp với các loại (`chat`, `embedding`, `image`).
|
||||
|
||||
### Đồng bộ đám mây
|
||||
|
||||
- Đồng bộ hóa nhà cung cấp, combo và cài đặt trên các thiết bị
|
||||
- Đồng bộ hóa nền tự động với thời gian chờ + không nhanh
|
||||
- Ưu tiên phía máy chủ `BASE_URL`/`CLOUD_URL` phía máy chủ trong sản xuất
|
||||
|
||||
### LLM Gateway Intelligence (Giai đoạn 9)
|
||||
|
||||
- **Bộ nhớ đệm ngữ nghĩa** — Tự động lưu vào bộ nhớ đệm khi không phát trực tuyến, phản hồi nhiệt độ=0 (bỏ qua bằng `X-OmniRoute-No-Cache: true`)
|
||||
- **Yêu cầu Idempotency** — Loại bỏ các yêu cầu trùng lặp trong vòng 5 giây thông qua tiêu đề `Idempotency-Key` hoặc `X-Request-Id`
|
||||
- **Theo dõi tiến trình** — Chọn tham gia các sự kiện SSE `event: progress` qua tiêu đề `X-OmniRoute-Progress: true`
|
||||
|
||||
---
|
||||
|
||||
### Sân chơi dịch thuật
|
||||
|
||||
Truy cập qua **Bảng điều khiển → Trình dịch**. Gỡ lỗi và trực quan hóa cách OmniRoute dịch các yêu cầu API giữa các nhà cung cấp.
|
||||
|
||||
| Chế độ | Mục đích |
|
||||
| ----------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| **Sân chơi** | Chọn định dạng nguồn/đích, dán yêu cầu và xem bản dịch ngay lập tức |
|
||||
| **Người kiểm tra trò chuyện** | Gửi tin nhắn trò chuyện trực tiếp qua proxy và kiểm tra toàn bộ chu trình yêu cầu/phản hồi |
|
||||
| **Bàn thử nghiệm** | Chạy thử nghiệm hàng loạt trên nhiều kết hợp định dạng để xác minh tính chính xác của bản dịch |
|
||||
| **Màn hình trực tiếp** | Xem các bản dịch theo thời gian thực khi các yêu cầu chuyển qua proxy |
|
||||
|
||||
**Trường hợp sử dụng:**
|
||||
|
||||
- Gỡ lỗi tại sao kết hợp khách hàng/nhà cung cấp cụ thể không thành công
|
||||
- Xác minh rằng thẻ tư duy, lệnh gọi công cụ và lời nhắc hệ thống được dịch chính xác
|
||||
- So sánh sự khác biệt về định dạng giữa các định dạng API OpenAI, Claude, Gemini và Responses
|
||||
|
||||
---
|
||||
|
||||
### Chiến lược định tuyến
|
||||
|
||||
Định cấu hình qua **Bảng điều khiển → Cài đặt → Định tuyến**.
|
||||
|
||||
| Chiến lược | Mô tả |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Điền đầu tiên** | Sử dụng các tài khoản theo thứ tự ưu tiên - tài khoản chính xử lý tất cả các yêu cầu cho đến khi không có sẵn |
|
||||
| **Vòng tròn** | Xoay vòng qua tất cả các tài khoản với giới hạn cố định có thể định cấu hình (mặc định: 3 cuộc gọi cho mỗi tài khoản) |
|
||||
| **P2C (Sức mạnh của hai lựa chọn)** | Chọn 2 tài khoản ngẫu nhiên và hướng đến tài khoản lành mạnh hơn — cân bằng tải trọng với nhận thức về sức khỏe |
|
||||
| **Ngẫu nhiên** | Chọn ngẫu nhiên một tài khoản cho mỗi yêu cầu bằng cách sử dụng tính năng ngẫu nhiên Fisher-Yates |
|
||||
| **Ít sử dụng nhất** | Định tuyến tới tài khoản có dấu thời gian `lastUsedAt` cũ nhất, phân bổ lưu lượng truy cập đồng đều |
|
||||
| **Tối ưu hóa chi phí** | Định tuyến đến tài khoản có giá trị ưu tiên thấp nhất, tối ưu hóa cho nhà cung cấp có chi phí thấp nhất |
|
||||
|
||||
#### Bí danh mô hình ký tự đại diện
|
||||
|
||||
Tạo các mẫu ký tự đại diện để ánh xạ lại tên mô hình:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
Hỗ trợ ký tự đại diện `*` (bất kỳ ký tự nào) và `?` (ký tự đơn).
|
||||
|
||||
#### Chuỗi dự phòng
|
||||
|
||||
Xác định chuỗi dự phòng toàn cầu áp dụng cho tất cả các yêu cầu:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Khả năng phục hồi & Bộ ngắt mạch
|
||||
|
||||
Định cấu hình qua **Bảng điều khiển → Cài đặt → Khả năng phục hồi**.
|
||||
|
||||
OmniRoute triển khai khả năng phục hồi cấp nhà cung cấp với bốn thành phần:
|
||||
|
||||
1. **Hồ sơ nhà cung cấp** — Cấu hình cho mỗi nhà cung cấp cho:
|
||||
- Ngưỡng thất bại (có bao nhiêu lần thất bại trước khi mở)
|
||||
- Thời gian hồi chiêu
|
||||
- Độ nhạy phát hiện giới hạn tốc độ
|
||||
- Thông số backoff theo cấp số nhân
|
||||
|
||||
2. **Giới hạn tỷ lệ có thể chỉnh sửa** — Giá trị mặc định ở cấp hệ thống có thể định cấu hình trong trang tổng quan:
|
||||
- **Số yêu cầu mỗi phút (RPM)** — Số yêu cầu tối đa mỗi phút cho mỗi tài khoản
|
||||
- **Thời gian tối thiểu giữa các yêu cầu** — Khoảng cách tối thiểu tính bằng mili giây giữa các yêu cầu
|
||||
- **Số yêu cầu đồng thời tối đa** — Số yêu cầu đồng thời tối đa cho mỗi tài khoản
|
||||
- Nhấp vào **Chỉnh sửa** để sửa đổi, sau đó nhấp vào **Lưu** hoặc **Hủy**. Các giá trị vẫn tồn tại thông qua API khả năng phục hồi.
|
||||
|
||||
3. **Bộ ngắt mạch** — Theo dõi lỗi của mỗi nhà cung cấp và tự động mở mạch khi đạt đến ngưỡng:
|
||||
- **ĐÓNG** (Khỏe mạnh) — Yêu cầu diễn ra bình thường
|
||||
- **OPEN** — Nhà cung cấp bị chặn tạm thời sau nhiều lần thất bại
|
||||
- **HALF_OPEN** — Kiểm tra xem nhà cung cấp đã phục hồi chưa
|
||||
|
||||
4. **Chính sách & Mã định danh bị khóa** — Hiển thị trạng thái cầu dao và mã định danh bị khóa với khả năng buộc mở khóa.
|
||||
|
||||
5. **Tự động phát hiện giới hạn tốc độ** — Giám sát các tiêu đề `429` và `Retry-After` để chủ động tránh chạm tới giới hạn tốc độ của nhà cung cấp.
|
||||
|
||||
**Mẹo chuyên nghiệp:** Sử dụng nút **Đặt lại tất cả** để xóa tất cả cầu dao và thời gian hồi chiêu khi nhà cung cấp khôi phục sau khi ngừng hoạt động.
|
||||
|
||||
---
|
||||
|
||||
### Xuất/Nhập cơ sở dữ liệu
|
||||
|
||||
Quản lý sao lưu cơ sở dữ liệu trong **Bảng điều khiển → Cài đặt → Hệ thống & Bộ lưu trữ**.
|
||||
|
||||
| Hành động | Mô tả |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Xuất cơ sở dữ liệu** | Tải xuống cơ sở dữ liệu SQLite hiện tại dưới dạng tệp `.sqlite` |
|
||||
| **Xuất tất cả (.tar.gz)** | Tải xuống kho lưu trữ sao lưu đầy đủ bao gồm: cơ sở dữ liệu, cài đặt, tổ hợp, kết nối nhà cung cấp (không có thông tin xác thực), siêu dữ liệu khóa API |
|
||||
| **Nhập cơ sở dữ liệu** | Tải tệp `.sqlite` lên để thay thế cơ sở dữ liệu hiện tại. Bản sao lưu trước khi nhập được tự động tạo |
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**Xác thực nhập:** Tệp đã nhập được xác thực về tính toàn vẹn (kiểm tra pragma SQLite), các bảng bắt buộc (`provider_connections`, `provider_nodes`, `combos`, `api_keys`) và kích thước (tối đa 100MB).
|
||||
|
||||
**Trường hợp sử dụng:**
|
||||
|
||||
- Di chuyển OmniRoute giữa các máy
|
||||
- Tạo bản sao lưu bên ngoài để khắc phục thảm họa
|
||||
- Chia sẻ cấu hình giữa các thành viên trong nhóm (xuất tất cả → chia sẻ kho lưu trữ)
|
||||
|
||||
---
|
||||
|
||||
### Bảng điều khiển cài đặt
|
||||
|
||||
Trang cài đặt được tổ chức thành 5 tab để dễ dàng điều hướng:
|
||||
|
||||
| Tab | Nội dung |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| **An ninh** | Cài đặt đăng nhập/mật khẩu, Kiểm soát truy cập IP, xác thực API cho `/models` và Chặn nhà cung cấp |
|
||||
| **Định tuyến** | Chiến lược định tuyến toàn cầu (6 tùy chọn), bí danh mô hình ký tự đại diện, chuỗi dự phòng, mặc định kết hợp |
|
||||
| **Khả năng phục hồi** | Hồ sơ nhà cung cấp, giới hạn tỷ lệ có thể chỉnh sửa, trạng thái ngắt mạch, chính sách và số nhận dạng bị khóa |
|
||||
| **AI** | Suy nghĩ về cấu hình ngân sách, tiêm nhắc hệ thống toàn cầu, thống kê bộ nhớ đệm nhanh chóng |
|
||||
| **Nâng cao** | Cấu hình proxy toàn cầu (HTTP/SOCKS5) |
|
||||
|
||||
---
|
||||
|
||||
### Quản lý chi phí & ngân sách
|
||||
|
||||
Truy cập qua **Bảng điều khiển → Chi phí**.
|
||||
|
||||
| Tab | Mục đích |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| **Ngân sách** | Đặt giới hạn chi tiêu cho mỗi khóa API với ngân sách hàng ngày/hàng tuần/hàng tháng và theo dõi thời gian thực |
|
||||
| **Giá** | Xem và chỉnh sửa các mục định giá mô hình — chi phí cho mỗi 1K mã thông báo đầu vào/đầu ra cho mỗi nhà cung cấp |
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**Theo dõi chi phí:** Mọi yêu cầu đều ghi lại việc sử dụng mã thông báo và tính toán chi phí bằng bảng giá. Xem thông tin chi tiết trong **Trang tổng quan → Mức sử dụng** theo nhà cung cấp, kiểu máy và khóa API.
|
||||
|
||||
---
|
||||
|
||||
### Phiên âm âm thanh
|
||||
|
||||
OmniRoute hỗ trợ sao chép âm thanh thông qua điểm cuối tương thích với OpenAI:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
Các nhà cung cấp hiện có: **Deepgram** (`deepgram/`), **AssemblyAI** (`assemblyai/`).
|
||||
|
||||
Các định dạng âm thanh được hỗ trợ: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
|
||||
|
||||
---
|
||||
|
||||
### Chiến lược cân bằng kết hợp
|
||||
|
||||
Định cấu hình cân bằng trên mỗi kết hợp trong **Bảng điều khiển → Tổ hợp → Tạo/Chỉnh sửa → Chiến lược**.
|
||||
|
||||
| Chiến lược | Mô tả |
|
||||
| ------------------------ | --------------------------------------------------------------------------- |
|
||||
| **Vòng tròn** | Xoay qua các mô hình một cách tuần tự |
|
||||
| **Ưu tiên** | Luôn thử mẫu đầu tiên; chỉ quay lại khi có lỗi |
|
||||
| **Ngẫu nhiên** | Chọn một mô hình ngẫu nhiên từ combo cho mỗi yêu cầu |
|
||||
| **Có trọng số** | Các tuyến đường tương ứng dựa trên trọng số được chỉ định cho mỗi mô hình |
|
||||
| **Ít được sử dụng nhất** | Định tuyến đến mô hình có ít yêu cầu gần đây nhất (sử dụng số liệu kết hợp) |
|
||||
| **Tối ưu hóa chi phí** | Hướng đến mô hình có sẵn rẻ nhất (sử dụng bảng giá) |
|
||||
|
||||
Mặc định kết hợp chung có thể được đặt trong **Bảng điều khiển → Cài đặt → Định tuyến → Mặc định kết hợp**.
|
||||
|
||||
---
|
||||
|
||||
### Bảng thông tin sức khỏe
|
||||
|
||||
Truy cập qua **Bảng điều khiển → Sức khỏe**. Tổng quan về tình trạng hệ thống theo thời gian thực với 6 thẻ:
|
||||
|
||||
| Thẻ | Nó hiển thị những gì |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **Trạng thái hệ thống** | Thời gian hoạt động, phiên bản, mức sử dụng bộ nhớ, thư mục dữ liệu |
|
||||
| **Sức khỏe của nhà cung cấp** | Trạng thái ngắt mạch của mỗi nhà cung cấp (Đóng/Mở/Nửa mở) |
|
||||
| **Giới hạn tỷ lệ** | Thời gian hồi chiêu giới hạn tốc độ kích hoạt cho mỗi tài khoản với thời gian còn lại |
|
||||
| **Khóa hoạt động** | Nhà cung cấp bị chặn tạm thời bởi chính sách khóa |
|
||||
| **Bộ nhớ đệm chữ ký** | Số liệu thống kê bộ đệm chống trùng lặp (khóa hoạt động, tỷ lệ truy cập) |
|
||||
| **Từ xa độ trễ** | tổng hợp độ trễ p50/p95/p99 cho mỗi nhà cung cấp |
|
||||
|
||||
**Mẹo chuyên nghiệp:** Trang Sức khỏe tự động làm mới sau mỗi 10 giây. Sử dụng thẻ ngắt mạch để xác định nhà cung cấp nào đang gặp sự cố.
|
||||
441
docs/i18n/zh-CN/API_REFERENCE.md
Normal file
441
docs/i18n/zh-CN/API_REFERENCE.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# API 参考
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
|
||||
|
||||
所有 OmniRoute API 端点的完整参考。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Image Generation](#image-generation)
|
||||
- [List Models](#list-models)
|
||||
- [Compatibility Endpoints](#compatibility-endpoints)
|
||||
- [Semantic Cache](#semantic-cache)
|
||||
- [Dashboard & Management](#dashboard--management)
|
||||
- [Request Processing](#request-processing)
|
||||
- [Authentication](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## 聊天完成
|
||||
|
||||
```bash
|
||||
POST /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
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义标头
|
||||
|
||||
| 标题 | 方向 | 描述 |
|
||||
| ------------------------ | ---- | ----------------------------- |
|
||||
| `X-OmniRoute-No-Cache` | 请求 | 设置为 `true` 以绕过缓存 |
|
||||
| `X-OmniRoute-Progress` | 请求 | 对于进度事件设置为 `true` |
|
||||
| `Idempotency-Key` | 请求 | Dedup 密钥(5 秒窗口) |
|
||||
| `X-Request-Id` | 请求 | 替代重复数据删除密钥 |
|
||||
| `X-OmniRoute-Cache` | 回应 | `HIT` 或 `MISS`(非流式传输) |
|
||||
| `X-OmniRoute-Idempotent` | 回应 | `true` 如果已进行重复数据删除 |
|
||||
| `X-OmniRoute-Progress` | 回应 | `enabled` 如果进度跟踪开启 |
|
||||
|
||||
---
|
||||
|
||||
## 嵌入
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
可用的提供商:Nebius、OpenAI、Mistral、Together AI、Fireworks、NVIDIA。
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 图像生成
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
可用的提供商:OpenAI (DALL-E)、xAI (Grok Image)、Together AI (FLUX)、Fireworks AI。
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 列出型号
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 兼容性端点
|
||||
|
||||
|方法|路径|格式|
|
||||
| ------ | ------------------------ | | ---------------------- |
|
||||
|发布 | `/v1/chat/completions` |开放人工智能 |
|
||||
|发布 | `/v1/messages` |人择 |
|
||||
|发布 | `/v1/responses` | OpenAI 回应 |
|
||||
|发布 | `/v1/embeddings` |开放人工智能 |
|
||||
|发布 | `/v1/images/generations` |开放人工智能 |
|
||||
|获取 | `/v1/models` |开放人工智能 |
|
||||
|发布 | `/v1/messages/count_tokens` |人择 |
|
||||
|获取 | `/v1beta/models` |双子座|
|
||||
|发布 | `/v1beta/models/{...path}` |双子座生成内容 |
|
||||
|发布 | `/v1/api/chat` |奥拉玛 |
|
||||
|
||||
### 专用提供商路线
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
如果缺少提供商前缀,则会自动添加。不匹配的模型返回 `400`。
|
||||
|
||||
---
|
||||
|
||||
## 语义缓存
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 仪表板和管理
|
||||
|
||||
### 身份验证
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ----------------------------- | --------- | ------------ |
|
||||
| `/api/auth/login` | 发布 | 登录 |
|
||||
| `/api/auth/logout` | 发布 | 退出 |
|
||||
| `/api/settings/require-login` | 获取/放置 | 切换需要登录 |
|
||||
|
||||
### 提供商管理
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ---------------------------- | -------------- | --------------- |
|
||||
| `/api/providers` | 获取/发布 | 列出/创建提供商 |
|
||||
| `/api/providers/[id]` | 获取/放置/删除 | 管理提供商 |
|
||||
| `/api/providers/[id]/test` | 发布 | 测试提供商连接 |
|
||||
| `/api/providers/[id]/models` | 获取 | 列出供应商型号 |
|
||||
| `/api/providers/validate` | 发布 | 验证提供商配置 |
|
||||
| `/api/provider-nodes*` | 各种 | 提供商节点管理 |
|
||||
| `/api/provider-models` | 获取/发布/删除 | 定制型号 |
|
||||
|
||||
### OAuth 流程
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| -------------------------------- | ---- | -------------------- |
|
||||
| `/api/oauth/[provider]/[action]` | 各种 | 特定于提供商的 OAuth |
|
||||
|
||||
### 路由和配置
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| --------------------- | --------- | --------------------------- |
|
||||
| `/api/models/alias` | 获取/发布 | 模型别名 |
|
||||
| `/api/models/catalog` | 获取 | 按提供商+类型列出的所有型号 |
|
||||
| `/api/combos*` | 各种 | 组合管理 |
|
||||
| `/api/keys*` | 各种 | API 密钥管理 |
|
||||
| `/api/pricing` | 获取 | 型号定价 |
|
||||
|
||||
### 使用与分析
|
||||
|
||||
|端点 |方法|描述 |
|
||||
| ------------------------ | | ------ | -------------------- |
|
||||
| `/api/usage/history` |获取 |使用历史 |
|
||||
| `/api/usage/logs` |获取 |使用日志 |
|
||||
| `/api/usage/request-logs` |获取 |请求级日志 |
|
||||
| `/api/usage/[connectionId]` |获取 |每个连接的使用情况 |
|
||||
|
||||
### 设置
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ------------------------------- | --------- | -------------------- |
|
||||
| `/api/settings` | 获取/放置 | 常规设置 |
|
||||
| `/api/settings/proxy` | 获取/放置 | 网络代理配置 |
|
||||
| `/api/settings/proxy/test` | 发布 | 测试代理连接 |
|
||||
| `/api/settings/ip-filter` | 获取/放置 | IP 允许列表/阻止列表 |
|
||||
| `/api/settings/thinking-budget` | 获取/放置 | 推理代币预算 |
|
||||
| `/api/settings/system-prompt` | 获取/放置 | 全局系统提示 |
|
||||
|
||||
### 监控
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ------------------------ | --------- | ------------------ |
|
||||
| `/api/sessions` | 获取 | 活动会话跟踪 |
|
||||
| `/api/rate-limits` | 获取 | 每个帐户的费率限制 |
|
||||
| `/api/monitoring/health` | 获取 | 健康检查 |
|
||||
| `/api/cache` | 获取/删除 | 缓存统计/清除 |
|
||||
|
||||
### 备份和导出/导入
|
||||
|
||||
|端点 |方法|描述 |
|
||||
| ------------------------ | | ------ | --------------------------------------- |
|
||||
| `/api/db-backups` |获取 |列出可用备份 |
|
||||
| `/api/db-backups` |放置 |创建手动备份 |
|
||||
| `/api/db-backups` |发布 |从特定备份恢复|
|
||||
| `/api/db-backups/export` |获取 |将数据库下载为 .sqlite 文件 |
|
||||
| `/api/db-backups/import` |发布 |上传.sqlite 文件来替换数据库 |
|
||||
| `/api/db-backups/exportAll` |获取 |下载完整备份为 .tar.gz 存档 |
|
||||
|
||||
### 云同步
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ---------------------- | ---- | ---------- |
|
||||
| `/api/sync/cloud` | 各种 | 云同步操作 |
|
||||
| `/api/sync/initialize` | 发布 | 初始化同步 |
|
||||
| `/api/cloud/*` | 各种 | 云管理 |
|
||||
|
||||
### CLI 工具
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ---------------------------------- | ---- | ----------------- |
|
||||
| `/api/cli-tools/claude-settings` | 获取 | 克劳德 CLI 状态 |
|
||||
| `/api/cli-tools/codex-settings` | 获取 | Codex CLI 状态 |
|
||||
| `/api/cli-tools/droid-settings` | 获取 | Droid CLI 状态 |
|
||||
| `/api/cli-tools/openclaw-settings` | 获取 | OpenClaw CLI 状态 |
|
||||
| `/api/cli-tools/runtime/[toolId]` | 获取 | 通用 CLI 运行时 |
|
||||
|
||||
CLI 响应包括:`installed`、`runnable`、`command`、`commandPath`、`runtimeMode`、`reason`。
|
||||
|
||||
### 弹性和速率限制
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ----------------------- | --------- | ---------------------- |
|
||||
| `/api/resilience` | 获取/放置 | 获取/更新弹性配置文件 |
|
||||
| `/api/resilience/reset` | 发布 | 重置断路器 |
|
||||
| `/api/rate-limits` | 获取 | 每个帐户的速率限制状态 |
|
||||
| `/api/rate-limit` | 获取 | 全局限速配置 |
|
||||
|
||||
### 评估
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| ------------ | --------- | --------------------- |
|
||||
| `/api/evals` | 获取/发布 | 列出评估套件/运行评估 |
|
||||
|
||||
### 政策
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| --------------- | -------------- | ------------ |
|
||||
| `/api/policies` | 获取/发布/删除 | 管理路由策略 |
|
||||
|
||||
### 合规性
|
||||
|
||||
|端点 |方法|描述 |
|
||||
| ------------------------ | | ------ | -------------------------------------- |
|
||||
| `/api/compliance/audit-log` |获取 |合规审核日志(最后 N)|
|
||||
|
||||
### v1beta(Gemini 兼容)
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| -------------------------- | ---- | ----------------------------- |
|
||||
| `/v1beta/models` | 获取 | 以 Gemini 格式列出模型 |
|
||||
| `/v1beta/models/{...path}` | 发布 | Gemini `generateContent` 端点 |
|
||||
|
||||
这些端点反映了 Gemini 的 API 格式,适用于期望本机 Gemini SDK 兼容性的客户端。
|
||||
|
||||
### 内部/系统 API
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
| --------------- | ---- | ------------------------------------------- |
|
||||
| `/api/init` | 获取 | 应用程序初始化检查(首次运行时使用) |
|
||||
| `/api/tags` | 获取 | Ollama 兼容模型标签(适用于 Ollama 客户端) |
|
||||
| `/api/restart` | 发布 | 触发服务器优雅重启 |
|
||||
| `/api/shutdown` | 发布 | 触发服务器正常关闭 |
|
||||
|
||||
> **注意:** 这些端点由系统内部使用或用于 Ollama 客户端兼容性。最终用户通常不会调用它们。
|
||||
|
||||
---
|
||||
|
||||
## 音频转录
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
使用 Deepgram 或 AssemblyAI 转录音频文件。
|
||||
|
||||
**要求:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**回应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
**支持的提供商:** `deepgram/nova-3`、`assemblyai/best`。
|
||||
|
||||
**支持的格式:** `mp3`、`wav`、`m4a`、`flac`、`ogg`、`webm`。
|
||||
|
||||
---
|
||||
|
||||
## 奥拉马兼容性
|
||||
|
||||
对于使用 Ollama 的 API 格式的客户端:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
请求会在 Ollama 和内部格式之间自动转换。
|
||||
|
||||
---
|
||||
|
||||
## 遥测
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**回应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 预算
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 型号可用性
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 请求处理
|
||||
|
||||
1. 客户端向`/v1/*`发送请求
|
||||
2. 路由处理程序调用 `handleChat`、`handleEmbedding`、`handleAudioTranscription` 或 `handleImageGeneration`
|
||||
3. 模型已解析(直接提供者/模型或别名/组合)
|
||||
4. 通过帐户可用性过滤从本地数据库中选择凭证
|
||||
5. 对于聊天:`handleChatCore` — 格式检测、翻译、缓存检查、幂等性检查
|
||||
6. Provider执行器发送上游请求
|
||||
7. 响应转换回客户端格式(聊天)或按原样返回(嵌入/图像/音频)
|
||||
8. 使用/日志记录
|
||||
9. 根据组合规则对错误应用回退
|
||||
|
||||
完整架构参考:[**OMNI_TOKEN_119**](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## 身份验证
|
||||
|
||||
- 仪表板路由 (`/dashboard/*`) 使用 `auth_token` cookie
|
||||
- 登录使用保存的密码哈希;回退到 `INITIAL_PASSWORD`
|
||||
- `requireLogin` 可通过 `/api/settings/require-login` 切换
|
||||
- `/v1/*` 路由在 `REQUIRE_API_KEY=true` 时可选择需要 Bearer API 密钥
|
||||
781
docs/i18n/zh-CN/ARCHITECTURE.md
Normal file
781
docs/i18n/zh-CN/ARCHITECTURE.md
Normal file
@@ -0,0 +1,781 @@
|
||||
# OmniRoute 架构
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
|
||||
|
||||
_最后更新:2026-02-18_
|
||||
|
||||
## 执行摘要
|
||||
|
||||
OmniRoute 是基于 Next.js 构建的本地 AI 路由网关和仪表板。
|
||||
它提供单个 OpenAI 兼容端点 (`/v1/*`),并通过转换、回退、令牌刷新和使用跟踪在多个上游提供商之间路由流量。
|
||||
|
||||
核心能力:
|
||||
|
||||
- 用于 CLI/工具的 OpenAI 兼容 API 界面(28 个提供商)
|
||||
- 跨提供商格式的请求/响应翻译
|
||||
- 模型组合后备(多模型序列)
|
||||
- 账户级回退(每个提供商多个账户)
|
||||
- OAuth + API 密钥提供商连接管理
|
||||
- 通过 `/v1/embeddings` 嵌入生成(6 个提供商,9 个模型)
|
||||
- 通过 `/v1/images/generations` 生成图像(4 个提供商,9 个模型)
|
||||
- 为推理模型考虑标签解析(`<think>...</think>`)
|
||||
- 响应清理以实现严格的 OpenAI SDK 兼容性
|
||||
- 角色标准化(开发人员→系统、系统→用户)以实现跨提供商兼容性
|
||||
- 结构化输出转换(json_schema→Gemini responseSchema)
|
||||
- 提供商、密钥、别名、组合、设置、定价的本地持久性
|
||||
- 使用/成本跟踪和请求记录
|
||||
- 可选的云同步用于多设备/状态同步
|
||||
- API 访问控制的 IP 允许列表/阻止列表
|
||||
- 思考预算管理(直通/自动/自定义/自适应)
|
||||
- 全局系统提示注入
|
||||
- 会话跟踪和指纹识别
|
||||
- 使用特定于提供商的配置文件增强每个帐户的速率限制
|
||||
- 提供者弹性的断路器模式
|
||||
- 具有互斥锁的防雷群保护
|
||||
- 基于签名的请求重复数据删除缓存
|
||||
- 领域层:模型可用性、成本规则、后备策略、锁定策略
|
||||
- 域状态持久性(用于回退、预算、锁定、断路器的 SQLite 直写式缓存)
|
||||
- 用于集中请求评估的策略引擎(锁定→预算→后备)
|
||||
- 使用 p50/p95/p99 延迟聚合请求遥测
|
||||
- 用于端到端跟踪的关联 ID (X-Request-Id)
|
||||
- 合规性审核日志记录,可根据 API 密钥选择退出
|
||||
- LLM质量保证评估框架
|
||||
- 具有实时断路器状态的 Resilience UI 仪表板
|
||||
- 模块化 OAuth 提供程序(`src/lib/oauth/providers/` 下有 12 个单独的模块)
|
||||
|
||||
主要运行时模型:
|
||||
|
||||
- `src/app/api/*` 下的 Next.js 应用程序路由同时实现仪表板 API 和兼容性 API
|
||||
- `src/sse/*` + `open-sse/*` 中的共享 SSE/路由核心处理提供程序执行、转换、流式传输、回退和使用
|
||||
|
||||
## 范围和边界
|
||||
|
||||
### 在范围内
|
||||
|
||||
- 本地网关运行时
|
||||
- 仪表板管理 API
|
||||
- 提供商身份验证和令牌刷新
|
||||
- 请求翻译和 SSE 流媒体
|
||||
- 本地状态+使用持久性
|
||||
- 可选的云同步编排
|
||||
|
||||
### 超出范围
|
||||
|
||||
- `NEXT_PUBLIC_CLOUD_URL` 背后的云服务实现
|
||||
- 本地流程之外的提供商 SLA/控制平面
|
||||
- 外部 CLI 二进制文件本身(Claude CLI、Codex CLI 等)
|
||||
|
||||
## 高级系统上下文
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(db.json)]
|
||||
UDB[(usage.json + log.txt)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## 核心运行时组件
|
||||
|
||||
## 1) API 和路由层(Next.js 应用程序路由)
|
||||
|
||||
主要目录:
|
||||
|
||||
- `src/app/api/v1/*` 和 `src/app/api/v1beta/*` 用于兼容性 API
|
||||
- `src/app/api/*` 用于管理/配置 API
|
||||
- 接下来重写 `next.config.mjs` 将 `/v1/*` 映射到 `/api/v1/*`
|
||||
|
||||
重要的兼容性路线:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — 包括带有 `custom: true` 的自定义模型
|
||||
- `src/app/api/v1/embeddings/route.ts` — 嵌入生成(6 个提供商)
|
||||
- `src/app/api/v1/images/generations/route.ts` — 图像生成(4 个以上提供商,包括 Antigravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — 每个提供商专用的聊天
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — 每个提供商专用的嵌入
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — 每个提供商专用的图像
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
管理域:
|
||||
|
||||
- 身份验证/设置:`src/app/api/auth/*`、`src/app/api/settings/*`
|
||||
- 提供商/连接:`src/app/api/providers*`
|
||||
- 提供商节点:`src/app/api/provider-nodes*`
|
||||
- 自定义模型:`src/app/api/provider-models`(获取/发布/删除)
|
||||
- 模型目录:`src/app/api/models/catalog` (GET)
|
||||
- 代理配置:`src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth:`src/app/api/oauth/*`
|
||||
- 密钥/别名/组合/定价:`src/app/api/keys*`、`src/app/api/models/alias`、`src/app/api/combos*`、`src/app/api/pricing`
|
||||
- 用法:`src/app/api/usage/*`
|
||||
- 同步/云:`src/app/api/sync/*`、`src/app/api/cloud/*`
|
||||
- CLI 工具助手:`src/app/api/cli-tools/*`
|
||||
- IP 过滤器:`src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- 思考预算:`src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- 系统提示:`src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- 会话:`src/app/api/sessions` (GET)
|
||||
- 速率限制:`src/app/api/rate-limits` (GET)
|
||||
- 弹性:`src/app/api/resilience` (GET/PATCH) — 提供商配置文件、断路器、速率限制状态
|
||||
- 弹性重置:`src/app/api/resilience/reset` (POST) — 重置断路器 + 冷却时间
|
||||
- 缓存统计信息:`src/app/api/cache/stats`(获取/删除)
|
||||
- 模型可用性:`src/app/api/models/availability` (GET/POST)
|
||||
- 遥测:`src/app/api/telemetry/summary` (GET)
|
||||
- 预算:`src/app/api/usage/budget`(获取/发布)
|
||||
- 后备链:`src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- 合规审核:`src/app/api/compliance/audit-log` (GET)
|
||||
- 评估:`src/app/api/evals` (GET/POST)、`src/app/api/evals/[suiteId]` (GET)
|
||||
- 政策:`src/app/api/policies` (GET/POST)
|
||||
|
||||
## 2) SSE + 翻译核心
|
||||
|
||||
主要流程模块:
|
||||
|
||||
- 条目:`src/sse/handlers/chat.ts`
|
||||
- 核心编排:`open-sse/handlers/chatCore.ts`
|
||||
- 提供者执行适配器:`open-sse/executors/*`
|
||||
- 格式检测/提供商配置:`open-sse/services/provider.ts`
|
||||
- 模型解析/解析:`src/sse/services/model.ts`、`open-sse/services/model.ts`
|
||||
- 账户后备逻辑:`open-sse/services/accountFallback.ts`
|
||||
- 翻译注册表:`open-sse/translator/index.ts`
|
||||
- 流转换:`open-sse/utils/stream.ts`、`open-sse/utils/streamHandler.ts`
|
||||
- 使用提取/标准化:`open-sse/utils/usageTracking.ts`
|
||||
- 思考标签解析器:`open-sse/utils/thinkTagParser.ts`
|
||||
- 嵌入处理程序:`open-sse/handlers/embeddings.ts`
|
||||
- 嵌入提供程序注册表:`open-sse/config/embeddingRegistry.ts`
|
||||
- 图像生成处理程序:`open-sse/handlers/imageGeneration.ts`
|
||||
- 图像提供者注册表:`open-sse/config/imageRegistry.ts`
|
||||
- 响应清理:`open-sse/handlers/responseSanitizer.ts`
|
||||
- 角色规范化:`open-sse/services/roleNormalizer.ts`
|
||||
|
||||
服务(业务逻辑):
|
||||
|
||||
- 账户选择/评分:`open-sse/services/accountSelector.ts`
|
||||
- 上下文生命周期管理:`open-sse/services/contextManager.ts`
|
||||
- IP 过滤器强制执行:`open-sse/services/ipFilter.ts`
|
||||
- 会话跟踪:`open-sse/services/sessionManager.ts`
|
||||
- 请求重复数据删除:`open-sse/services/signatureCache.ts`
|
||||
- 系统提示注入:`open-sse/services/systemPrompt.ts`
|
||||
- 思考预算管理:`open-sse/services/thinkingBudget.ts`
|
||||
- 通配符模型路由:`open-sse/services/wildcardRouter.ts`
|
||||
- 速率限制管理:`open-sse/services/rateLimitManager.ts`
|
||||
- 断路器:`open-sse/services/circuitBreaker.ts`
|
||||
|
||||
领域层模块:
|
||||
|
||||
- 型号可用性:`src/lib/domain/modelAvailability.ts`
|
||||
- 成本规则/预算:`src/lib/domain/costRules.ts`
|
||||
- 后备政策:`src/lib/domain/fallbackPolicy.ts`
|
||||
- 组合解析器:`src/lib/domain/comboResolver.ts`
|
||||
- 锁定政策:`src/lib/domain/lockoutPolicy.ts`
|
||||
- 策略引擎:`src/domain/policyEngine.ts` — 集中锁定→预算→后备评估
|
||||
- 错误代码目录:`src/lib/domain/errorCodes.ts`
|
||||
- 请求 ID:`src/lib/domain/requestId.ts`
|
||||
- 获取超时:`src/lib/domain/fetchTimeout.ts`
|
||||
- 请求遥测:`src/lib/domain/requestTelemetry.ts`
|
||||
- 合规/审计:`src/lib/domain/compliance/index.ts`
|
||||
- 评估跑步者:`src/lib/domain/evalRunner.ts`
|
||||
- 域状态持久性:`src/lib/db/domainState.ts` — 用于后备链、预算、成本历史记录、锁定状态、断路器的 SQLite CRUD
|
||||
|
||||
OAuth 提供程序模块(`src/lib/oauth/providers/` 下有 12 个单独的文件):
|
||||
|
||||
- 注册表索引:`src/lib/oauth/providers/index.ts`
|
||||
- 个人提供商:`claude.ts`、`codex.ts`、`gemini.ts`、`antigravity.ts`、`iflow.ts`、`qwen.ts`、`kimi-coding.ts`、`github.ts`、 `kiro.ts`、`cursor.ts`、`kilocode.ts`、`cline.ts`
|
||||
- 薄包装器:`src/lib/oauth/providers.ts` — 从各个模块重新导出
|
||||
|
||||
## 3) 持久层
|
||||
|
||||
主状态数据库:
|
||||
|
||||
- `src/lib/localDb.ts`
|
||||
- 文件:`${DATA_DIR}/db.json`(或设置时为 `$XDG_CONFIG_HOME/omniroute/db.json`,否则为 `~/.omniroute/db.json`)
|
||||
- 实体:providerConnections、providerNodes、modelAliases、组合、apiKeys、设置、定价、**customModels**、**proxyConfig**、**ipFilter**、**thinkingBudget**、**systemPrompt**
|
||||
|
||||
使用数据库:
|
||||
|
||||
- `src/lib/usageDb.ts`
|
||||
- 文件:`${DATA_DIR}/usage.json`、`${DATA_DIR}/log.txt`、`${DATA_DIR}/call_logs/`
|
||||
- 遵循与 `localDb` 相同的基本目录策略(`DATA_DIR`,然后设置时为 `XDG_CONFIG_HOME/omniroute`)
|
||||
- 分解为重点子模块:`migrations.ts`、`usageHistory.ts`、`costCalculator.ts`、`usageStats.ts`、`callLogs.ts`
|
||||
|
||||
域状态数据库(SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — 域状态的 CRUD 操作
|
||||
- 表(在 `src/lib/db/core.ts` 中创建):`domain_fallback_chains`、`domain_budgets`、`domain_cost_history`、`domain_lockout_state`、`domain_circuit_breakers`
|
||||
- 直写式缓存模式:内存中的Map在运行时具有权威性;突变同步写入SQLite;冷启动时从数据库恢复状态
|
||||
|
||||
## 4) 身份验证 + 安全表面
|
||||
|
||||
- 仪表板 cookie 身份验证:`src/proxy.ts`、`src/app/api/auth/login/route.ts`
|
||||
- API 密钥生成/验证:`src/shared/utils/apiKey.ts`
|
||||
- 提供商机密保留在 `providerConnections` 条目中
|
||||
- 通过 `open-sse/utils/proxyFetch.ts` (环境变量)和 `open-sse/utils/networkProxy.ts` (可按提供商配置或全局配置)提供出站代理支持
|
||||
|
||||
## 5) 云同步
|
||||
|
||||
- 调度程序初始化:`src/lib/initCloudSync.ts`、`src/shared/services/initializeCloudSync.ts`
|
||||
- 定期任务:`src/shared/services/cloudSyncScheduler.ts`
|
||||
- 控制路线:`src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## 请求生命周期 (`/v1/chat/completions`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## 组合 + 账户回退流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
回退决策由 `open-sse/services/accountFallback.ts` 使用状态代码和错误消息启发法驱动。
|
||||
|
||||
## OAuth 加入和令牌刷新生命周期
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
实时流量期间的刷新通过执行器 `refreshCredentials()` 在 `open-sse/handlers/chatCore.ts` 内执行。
|
||||
|
||||
## 云同步生命周期(启用/同步/禁用)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
启用云时,定期同步由 `CloudSyncScheduler` 触发。
|
||||
|
||||
## 数据模型和存储映射
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
物理存储文件:
|
||||
|
||||
- 主状态:`${DATA_DIR}/db.json`(或设置时为 `$XDG_CONFIG_HOME/omniroute/db.json`,否则为 `~/.omniroute/db.json`)
|
||||
- 使用统计数据:`${DATA_DIR}/usage.json`
|
||||
- 请求日志行:`${DATA_DIR}/log.txt`
|
||||
- 可选转换器/请求调试会话:`<repo>/logs/...`
|
||||
|
||||
## 部署拓扑
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(db.json)]
|
||||
UsageDB[(usage.json/log.txt)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## 模块映射(决策关键)
|
||||
|
||||
### 路由和 API 模块
|
||||
|
||||
- `src/app/api/v1/*`、`src/app/api/v1beta/*`:兼容性 API
|
||||
- `src/app/api/v1/providers/[provider]/*`:每个提供商的专用路由(聊天、嵌入、图像)
|
||||
- `src/app/api/providers*`:提供商 CRUD、验证、测试
|
||||
- `src/app/api/provider-nodes*`:自定义兼容节点管理
|
||||
- `src/app/api/provider-models`:自定义模型管理(CRUD)
|
||||
- `src/app/api/models/catalog`:完整模型目录 API(所有类型按提供商分组)
|
||||
- `src/app/api/oauth/*`:OAuth/设备代码流
|
||||
- `src/app/api/keys*`:本地 API 密钥生命周期
|
||||
- `src/app/api/models/alias`:别名管理
|
||||
- `src/app/api/combos*`:后备组合管理
|
||||
- `src/app/api/pricing`:成本计算的定价覆盖
|
||||
- `src/app/api/settings/proxy`:代理配置(GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test`:出站代理连接测试 (POST)
|
||||
- `src/app/api/usage/*`:使用和日志 API
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*`:云同步和面向云的助手
|
||||
- `src/app/api/cli-tools/*`:本地 CLI 配置编写器/检查器
|
||||
- `src/app/api/settings/ip-filter`:IP 允许列表/阻止列表 (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget`:思考代币预算配置(GET/PUT)
|
||||
- `src/app/api/settings/system-prompt`:全局系统提示符(GET/PUT)
|
||||
- `src/app/api/sessions`:活动会话列表 (GET)
|
||||
- `src/app/api/rate-limits`:每个账户的速率限制状态 (GET)
|
||||
|
||||
### 路由和执行核心
|
||||
|
||||
- `src/sse/handlers/chat.ts`:请求解析、组合处理、帐户选择循环
|
||||
- `open-sse/handlers/chatCore.ts`:翻译、执行程序调度、重试/刷新处理、流设置
|
||||
- `open-sse/executors/*`:提供商特定的网络和格式行为
|
||||
|
||||
### 翻译注册表和格式转换器
|
||||
|
||||
- `open-sse/translator/index.ts`:翻译器注册和编排
|
||||
- 请求翻译:`open-sse/translator/request/*`
|
||||
- 回复翻译器:`open-sse/translator/response/*`
|
||||
- 格式常量:`open-sse/translator/formats.ts`
|
||||
|
||||
### 坚持
|
||||
|
||||
- `src/lib/localDb.ts`:持久配置/状态
|
||||
- `src/lib/usageDb.ts`:使用历史记录和滚动请求日志
|
||||
|
||||
## 提供者执行者覆盖范围(策略模式)
|
||||
|
||||
每个提供程序都有一个扩展 `BaseExecutor`(在 `open-sse/executors/base.ts` 中)的专用执行器,它提供 URL 构建、标头构建、指数退避重试、凭证刷新挂钩和 `execute()` 编排方法。
|
||||
|
||||
| 执行人 | 提供商 | 特殊处理 |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| `DefaultExecutor` | OpenAI、Claude、Gemini、Qwen、iFlow、OpenRouter、GLM、Kimi、MiniMax、DeepSeek、Groq、xAI、Mistral、Perplexity、Together、Fireworks、Cerebras、Cohere、NVIDIA | 每个提供商的动态 URL/标头配置 |
|
||||
| `AntigravityExecutor` | 谷歌反重力 | 自定义项目/会话 ID,解析后重试 |
|
||||
| `CodexExecutor` | OpenAI 法典 | 注入系统指令,强制推理工作 |
|
||||
| `CursorExecutor` | 光标IDE | ConnectRPC 协议、Protobuf 编码、通过校验和进行请求签名 |
|
||||
| `GithubExecutor` | GitHub 副驾驶 | Copilot 令牌刷新,模仿 VSCode 标头 |
|
||||
| `KiroExecutor` | AWS CodeWhisperer/Kiro | AWS CodeWhisperer/Kiro | AWS CodeWhisperer/Kiro AWS EventStream 二进制格式 → SSE 转换 |
|
||||
| `GeminiCLIExecutor` | 双子座 CLI | Google OAuth 令牌刷新周期 |
|
||||
|
||||
所有其他提供商(包括自定义兼容节点)都使用 `DefaultExecutor`。
|
||||
|
||||
## 提供商兼容性矩阵
|
||||
|
||||
| 供应商 | 格式 | 授权 | 流 | 非流 | 令牌刷新 | 使用API |
|
||||
| ---------------- | ----------- | ------------------ | ------------ | ------------------------- | -------- | -------------- | ----------- |
|
||||
| 克劳德 | 克劳德 | API 密钥/OAuth | ✅ | ✅ | ✅ | ⚠️ 仅限管理员 |
|
||||
| 双子座 | 双子座 | API 密钥/OAuth | ✅ | ✅ | ✅ | ⚠️ 云控制台 |
|
||||
| 双子座 CLI | Gemini-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ 云控制台 |
|
||||
| 反重力 | 反重力 | OAuth | ✅ | ✅ | ✅ | ✅ 完整配额API |
|
||||
| 开放人工智能 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 法典 | openai-回应 | OAuth | ✅ 强迫 | ❌ | ✅ | ✅ 速率限制 |
|
||||
| GitHub 副驾驶 | 开放 | OAuth + 副驾驶令牌 | ✅ | ✅ | ✅ | ✅ 配额快照 |
|
||||
| 光标 | 光标 | 自定义校验和 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 基罗 | 基罗 | AWS SSO OIDC | AWS SSO OIDC | AWS SSO OIDC ✅(事件流) | ❌ | ✅ | ✅ 使用限制 |
|
||||
| 奎文 | 开放 | OAuth | ✅ | ✅ | ✅ | ⚠️ 根据要求 |
|
||||
| iFlow | 开放 | OAuth(基本) | ✅ | ✅ | ✅ | ⚠️ 根据要求 |
|
||||
| 开放路由器 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| GLM/Kimi/MiniMax | 克劳德 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 深度搜索 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 格罗克 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| xAI (Grok) | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 米斯特拉尔 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 困惑 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 一起人工智能 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 烟花人工智能 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 大脑 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| 连贯 | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
| NVIDIA NIM | 开放 | API 密钥 | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
## 格式翻译覆盖范围
|
||||
|
||||
检测到的源格式包括:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
目标格式包括:
|
||||
|
||||
- OpenAI 聊天/回复
|
||||
——克劳德
|
||||
- Gemini/Gemini-CLI/反重力信封
|
||||
- 基罗
|
||||
- 光标
|
||||
|
||||
翻译使用 **OpenAI 作为中心格式** - 所有转换都通过 OpenAI 作为中间:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
根据源有效负载形状和提供程序目标格式动态选择翻译。
|
||||
|
||||
翻译管道中的附加处理层:
|
||||
|
||||
- **响应清理** — 从 OpenAI 格式响应(流式和非流式)中去除非标准字段,以确保严格的 SDK 合规性
|
||||
- **角色标准化** — 对于非 OpenAI 目标,将 `developer` → `system` 转换;对于拒绝系统角色的模型(GLM、ERNIE),合并 `system` → `user`
|
||||
- **思考标签提取** — 将内容中的 `<think>...</think>` 块解析为 `reasoning_content` 字段
|
||||
- **结构化输出** — 将 OpenAI `response_format.json_schema` 转换为 Gemini 的 `responseMimeType` + `responseSchema`
|
||||
|
||||
## 支持的 API 端点
|
||||
|
||||
| 端点 | 格式 | 处理程序 |
|
||||
| -------------------------------------------------- | --------------- | --------------------------------------- |
|
||||
| `POST /v1/chat/completions` | OpenAI 聊天 | `src/sse/handlers/chat.ts` |
|
||||
| `POST /v1/messages` | 克劳德消息 | 相同的处理程序(自动检测) |
|
||||
| `POST /v1/responses` | OpenAI 回应 | `open-sse/handlers/responsesHandler.ts` |
|
||||
| `POST /v1/embeddings` | OpenAI 嵌入 | `open-sse/handlers/embeddings.ts` |
|
||||
| `GET /v1/embeddings` | 型号列表 | API路线 |
|
||||
| `POST /v1/images/generations` | OpenAI 图像 | `open-sse/handlers/imageGeneration.ts` |
|
||||
| `GET /v1/images/generations` | 型号列表 | API路线 |
|
||||
| `POST /v1/providers/{provider}/chat/completions` | OpenAI 聊天 | 专用于每个提供商的模型验证 |
|
||||
| `POST /v1/providers/{provider}/embeddings` | OpenAI 嵌入 | 专用于每个提供商的模型验证 |
|
||||
| `POST /v1/providers/{provider}/images/generations` | OpenAI 图像 | 专用于每个提供商的模型验证 |
|
||||
| `POST /v1/messages/count_tokens` | 克劳德代币计数 | API路线 |
|
||||
| `GET /v1/models` | OpenAI 模型列表 | API路线(聊天+嵌入+图像+自定义模型) |
|
||||
| `GET /api/models/catalog` | 目录 | 所有模型按提供商+类型分组 |
|
||||
| `POST /v1beta/models/*:streamGenerateContent` | 双子座人 | API路线 |
|
||||
| `GET/PUT/DELETE /api/settings/proxy` | 代理配置 | 网络代理配置 |
|
||||
| `POST /api/settings/proxy/test` | 代理连接 | 代理运行状况/连接测试端点 |
|
||||
| `GET/POST/DELETE /api/provider-models` | 定制型号 | 每个提供商的自定义模型管理 |
|
||||
|
||||
## 绕过处理程序
|
||||
|
||||
旁路处理程序 (`open-sse/utils/bypassHandler.ts`) 拦截来自 Claude CLI 的已知“一次性”请求(预热 ping、标题提取和令牌计数),并返回 **虚假响应**,而不消耗上游提供商令牌。仅当 `User-Agent` 包含 `claude-cli` 时才会触发。
|
||||
|
||||
## 请求记录器管道
|
||||
|
||||
请求记录器 (`open-sse/utils/requestLogger.ts`) 提供 7 阶段调试日志记录管道,默认情况下禁用,通过 `ENABLE_REQUEST_LOGS=true` 启用:
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
每个请求会话的文件都会写入 `<repo>/logs/<session>/`。
|
||||
|
||||
## 故障模式和恢复能力
|
||||
|
||||
## 1) 帐户/提供商可用性
|
||||
|
||||
- 提供商帐户因瞬态/速率/身份验证错误而冷却
|
||||
- 请求失败之前的帐户回退
|
||||
- 当前模型/提供商路径耗尽时组合模型回退
|
||||
|
||||
## 2) 令牌到期
|
||||
|
||||
- 对可刷新提供程序进行预检查和刷新并重试
|
||||
- 401/403 在核心路径中尝试刷新后重试
|
||||
|
||||
## 3) 流安全
|
||||
|
||||
- 断开连接感知流控制器
|
||||
- 具有流尾刷新和 `[DONE]` 处理的翻译流
|
||||
- 当提供者使用元数据丢失时使用估计回退
|
||||
|
||||
## 4) 云同步降级
|
||||
|
||||
- 出现同步错误,但本地运行时仍在继续
|
||||
- 调度程序具有可重试的逻辑,但定期执行当前默认调用单次尝试同步
|
||||
|
||||
## 5) 数据完整性
|
||||
|
||||
- 数据库形状迁移/修复丢失的键
|
||||
- localDb 和 useDb 的损坏的 JSON 重置保护措施
|
||||
|
||||
## 可观察性和操作信号
|
||||
|
||||
运行时可见性来源:
|
||||
|
||||
- 来自 `src/sse/utils/logger.ts` 的控制台日志
|
||||
- 每个请求的使用情况汇总在 `usage.json` 中
|
||||
- `log.txt` 中的文本请求状态日志
|
||||
- 当 `ENABLE_REQUEST_LOGS=true` 时,`logs/` 下的可选深度请求/翻译日志
|
||||
- UI 使用的仪表板使用端点 (`/api/usage/*`)
|
||||
|
||||
## 安全敏感边界
|
||||
|
||||
- JWT 秘密 (`JWT_SECRET`) 确保仪表板会话 cookie 验证/签名
|
||||
- 在实际部署中必须覆盖初始密码回退(`INITIAL_PASSWORD`,默认 `123456`)
|
||||
- API 密钥 HMAC 秘密 (`API_KEY_SECRET`) 确保生成的本地 API 密钥格式的安全
|
||||
- 提供者机密(API 密钥/令牌)保留在本地数据库中,并应在文件系统级别受到保护
|
||||
- 云同步端点依赖于 API 密钥身份验证 + 机器 ID 语义
|
||||
|
||||
## 环境和运行时矩阵
|
||||
|
||||
代码主动使用的环境变量:
|
||||
|
||||
- 应用程序/身份验证:`JWT_SECRET`、`INITIAL_PASSWORD`
|
||||
- 存储:`DATA_DIR`
|
||||
- 兼容节点行为:`ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- 可选存储基础覆盖(Linux/macOS 当 `DATA_DIR` 未设置时):`XDG_CONFIG_HOME`
|
||||
- 安全哈希:`API_KEY_SECRET`、`MACHINE_ID_SALT`
|
||||
- 日志记录:`ENABLE_REQUEST_LOGS`
|
||||
- 同步/云 URL:`NEXT_PUBLIC_BASE_URL`、`NEXT_PUBLIC_CLOUD_URL`
|
||||
- 出站代理:`HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY`、`NO_PROXY` 和小写变体
|
||||
- SOCKS5 功能标志:`ENABLE_SOCKS5_PROXY`、`NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- 平台/运行时帮助程序(不是特定于应用程序的配置):`APPDATA`、`NODE_ENV`、`PORT`、`HOSTNAME`
|
||||
|
||||
## 已知的架构注释
|
||||
|
||||
1. `usageDb` 和 `localDb` 现在与旧文件迁移共享相同的基本目录策略 (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`)。
|
||||
2. `/api/v1/route.ts` 返回静态模型列表,不是 `/v1/models` 使用的主要模型源。
|
||||
3. 请求记录器在启用时写入完整的标头/正文;将日志目录视为敏感目录。
|
||||
4. 云行为取决于正确的 `NEXT_PUBLIC_BASE_URL` 和云端点可访问性。
|
||||
5. `open-sse/` 目录发布为 `@omniroute/open-sse` **npm 工作区包**。源代码通过 `@omniroute/open-sse/...` 导入它(由 Next.js `transpilePackages` 解析)。为了保持一致性,本文档中的文件路径仍使用目录名称 `open-sse/`。
|
||||
6. 仪表板中的图表使用 **Recharts**(基于 SVG)来实现可访问的交互式分析可视化(模型使用情况条形图、包含成功率的提供商细分表)。
|
||||
7. E2E 测试使用 **Playwright** (`tests/e2e/`),通过 `npm run test:e2e` 运行。单元测试使用 **Node.js 测试运行程序** (`tests/unit/`),通过 `npm run test:plan3` 运行。 `src/` 下的源代码是 **TypeScript** (`.ts`/`.tsx`); `open-sse/` 工作区仍然是 JavaScript (`.js`)。
|
||||
8. 设置页面分为 5 个选项卡:安全、路由(6 种全局策略:先填充、循环、p2c、随机、最少使用、成本优化)、弹性(可编辑速率限制、断路器、策略)、AI(思考预算、系统提示、提示缓存)、高级(代理)。
|
||||
|
||||
## 操作验证清单
|
||||
|
||||
- 从源代码构建:`npm run build`
|
||||
- 构建 Docker 镜像:`docker build -t omniroute .`
|
||||
- 启动服务并验证:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- 当 `PORT=20128` 时,CLI 目标基本 URL 应为 `http://<host>:20128/v1`
|
||||
589
docs/i18n/zh-CN/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/zh-CN/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](../pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](../es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](../fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](../it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](../ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](../zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](../de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](../in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](../th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](../uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](../ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](../ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](../vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](../bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](../da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](../fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](../he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](../hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](../id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](../ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](../ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](../nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](../no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](../pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](../ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](../pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](../sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](../sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](../phi/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
#omniroute — 代码库文档
|
||||
|
||||
> A comprehensive, beginner-friendly guide to the **omniroute** multi-provider AI proxy router.
|
||||
|
||||
---
|
||||
|
||||
## 1. 什么是全向?
|
||||
|
||||
omniroute 是一个**代理路由器**,位于 AI 客户端(Claude CLI、Codex、Cursor IDE 等)和 AI 提供商(Anthropic、Google、OpenAI、AWS、GitHub 等)之间。它解决了一个大问题:
|
||||
|
||||
> **不同的 AI 客户端使用不同的“语言”(API 格式),不同的 AI 提供商也期望不同的“语言”。**omniroute 自动在它们之间进行翻译。
|
||||
|
||||
可以将其想象为联合国的通用翻译器 - 任何代表都可以说任何语言,翻译器可以将其转换为任何其他代表。
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构概述
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### 核心原则:轴辐式翻译
|
||||
|
||||
All format translation passes through **OpenAI format as the hub**:
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
This means you only need **N translators** (one per format) instead of **N²** (every pair).
|
||||
|
||||
---
|
||||
|
||||
## 3. 项目结构
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 逐个模块细分
|
||||
|
||||
### 4.1 配置 (`open-sse/config/`)
|
||||
|
||||
The **single source of truth** for all provider configuration.
|
||||
|
||||
| 文件 | 目的 |
|
||||
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `constants.ts` | `PROVIDERS` 对象,包含每个提供商的基本 URL、OAuth 凭据(默认)、标头和默认系统提示。还定义 `HTTP_STATUS`、`ERROR_TYPES`、`COOLDOWN_MS`、`BACKOFF_CONFIG` 和 `SKIP_PATTERNS`。 |
|
||||
| `credentialLoader.ts` | 从 `data/provider-credentials.json` 加载外部凭据,并将它们合并到 `PROVIDERS` 中的硬编码默认值上。让秘密不受源代码控制,同时保持向后兼容性。 |
|
||||
| `providerModels.ts` | 中央模型注册表:映射提供者别名 → 模型 ID。类似 `getModels()`、`getProviderByAlias()` 的函数。 |
|
||||
| `codexInstructions.ts` | 系统指令注入到 Codex 请求中(编辑约束、沙箱规则、批准策略)。 |
|
||||
| `defaultThinkingSignature.ts` | 克劳德和双子座模型的默认“思考”签名。 |
|
||||
| `ollamaModels.ts` | 本地 Ollama 模型的架构定义(名称、大小、系列、量化)。 |
|
||||
|
||||
#### 凭证加载流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 执行者 (`open-sse/executors/`)
|
||||
|
||||
执行器使用**策略模式**封装**特定于提供者的逻辑**。每个执行器根据需要重写基本方法。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
| 执行人 | 供应商 | 重点专业 |
|
||||
| ---------------- | ------------------------------------------ | ------------------------------------------------------------------------------------ |
|
||||
| `base.ts` | — | 抽象基础:URL 构建、标头、重试逻辑、凭证刷新 |
|
||||
| `default.ts` | 克劳德、Gemini、OpenAI、GLM、Kimi、MiniMax | 标准提供商的通用 OAuth 令牌刷新 |
|
||||
| `antigravity.ts` | 谷歌云代码 | 项目/会话 ID 生成、多 URL 回退、自定义重试错误消息解析(“2 小时 7 分 23 秒后重置”) |
|
||||
| `cursor.ts` | 光标IDE | **最复杂**:SHA-256 校验和验证、Protobuf 请求编码、二进制 EventStream → SSE 响应解析 |
|
||||
| `codex.ts` | OpenAI 法典 | 注入系统指令、管理思维水平、删除不支持的参数 |
|
||||
| `gemini-cli.ts` | 谷歌 Gemini CLI | 自定义 URL 构建 (`streamGenerateContent`)、Google OAuth 令牌刷新 |
|
||||
| `github.ts` | GitHub 副驾驶 | 双令牌系统(GitHub OAuth + Copilot 令牌),VSCode 标头模仿 |
|
||||
| `kiro.ts` | AWS 代码耳语 | AWS EventStream 二进制解析、AMZN 事件框架、令牌估计 |
|
||||
| `index.ts` | — | 工厂:地图提供者名称 → 执行器类,具有默认后备 |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 处理程序 (`open-sse/handlers/`)
|
||||
|
||||
**编排层** — 协调翻译、执行、流式传输和错误处理。
|
||||
|
||||
| 文件 | 目的 |
|
||||
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `chatCore.ts` | **中央编排器**(约 600 行)。处理完整的请求生命周期:格式检测→转换→执行程序调度→流/非流响应→令牌刷新→错误处理→使用日志记录。 |
|
||||
| `responsesHandler.ts` | OpenAI 响应 API 的适配器:转换响应格式 → 聊天完成 → 发送到 `chatCore` → 将 SSE 转换回响应格式。 |
|
||||
| `embeddings.ts` | 嵌入生成处理程序:解析嵌入模型→提供者,分派到提供者 API,返回兼容 OpenAI 的嵌入响应。支持 6 个以上提供商。 |
|
||||
| `imageGeneration.ts` | 图像生成处理程序:解析图像模型→提供程序,支持 OpenAI 兼容、Gemini-image(反重力)和后备(Nebius)模式。返回 base64 或 URL 图像。 |
|
||||
|
||||
#### 请求生命周期 (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 服务 (`open-sse/services/`)
|
||||
|
||||
支持处理程序和执行程序的业务逻辑。
|
||||
|
||||
| 文件 | 目的 |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `provider.ts` | **格式检测** (`detectFormat`):分析请求主体结构以识别 Claude/OpenAI/Gemini/Antigravity/Responses 格式(包括 Claude 的 `max_tokens` 启发式)。另外:URL 构建、标头构建、思考配置规范化。支持 `openai-compatible-*` 和 `anthropic-compatible-*` 动态提供程序。 |
|
||||
| `model.ts` | 模型字符串解析 (`claude/model-name` → `{provider: "claude", model: "model-name"}`)、具有冲突检测的别名解析、输入清理(拒绝路径遍历/控制字符)以及具有异步别名 getter 支持的模型信息解析。 |
|
||||
| `accountFallback.ts` | 速率限制处理:指数退避(1 秒 → 2 秒 → 4 秒 → 最大 2 分钟)、帐户冷却管理、错误分类(哪些错误触发回退,哪些错误不触发)。 |
|
||||
| `tokenRefresh.ts` | **每个提供商**的 OAuth 令牌刷新:Google(Gemini、Antigravity)、Claude、Codex、Qwen、iFlow、GitHub(OAuth + Copilot 双令牌)、Kiro(AWS SSO OIDC + 社交身份验证)。包括正在进行的承诺重复数据删除缓存和指数退避重试。 |
|
||||
| `combo.ts` | **组合模型**:后备模型链。如果模型 A 因符合后备条件的错误而失败,请尝试模型 B,然后是模型 C,等等。返回实际的上游状态代码。 |
|
||||
| `usage.ts` | 从提供商 API 获取配额/使用数据(GitHub Copilot 配额、Antigravity 模型配额、Codex 速率限制、Kiro 使用细分、Claude 设置)。 |
|
||||
| `accountSelector.ts` | 具有评分算法的智能帐户选择:考虑优先级、健康状态、循环位置和冷却状态,为每个请求选择最佳帐户。 |
|
||||
| `contextManager.ts` | 请求上下文生命周期管理:使用元数据(请求 ID、时间戳、提供程序信息)创建和跟踪每个请求上下文对象,以进行调试和日志记录。 |
|
||||
| `ipFilter.ts` | 基于IP的访问控制:支持白名单和黑名单模式。在处理 API 请求之前根据配置的规则验证客户端 IP。 |
|
||||
| `sessionManager.ts` | 使用客户端指纹进行会话跟踪:使用散列客户端标识符跟踪活动会话、监视请求计数并提供会话指标。 |
|
||||
| `signatureCache.ts` | 基于请求签名的重复数据删除缓存:通过缓存最近的请求签名并在时间窗口内返回相同请求的缓存响应来防止重复请求。 |
|
||||
| `systemPrompt.ts` | 全局系统提示注入:在所有请求之前或附加一个可配置的系统提示,并进行每个提供商的兼容性处理。 |
|
||||
| `thinkingBudget.ts` | 推理令牌预算管理:支持直通、自动(条带思维配置)、自定义(固定预算)和自适应(复杂度缩放)模式来控制思维/推理令牌。 |
|
||||
| `wildcardRouter.ts` | 通配符模型模式路由:根据可用性和优先级将通配符模式(例如 `*/claude-*`)解析为具体的提供者/模型对。 |
|
||||
|
||||
#### 令牌刷新重复数据删除
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### 帐户回退状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### 组合模型链
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 翻译器 (`open-sse/translator/`)
|
||||
|
||||
使用自注册插件系统的 **格式翻译引擎**。
|
||||
|
||||
#### 架构
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
| 目录 | 文件 | 描述 |
|
||||
| ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `request/` | 8 位译员 | 在格式之间转换请求正文。每个文件在导入时通过 `register(from, to, fn)` 自行注册。 |
|
||||
| `response/` | 7 名翻译 | 在格式之间转换流响应块。处理 SSE 事件类型、思维块、工具调用。 |
|
||||
| `helpers/` | 6 帮手 | 共享实用程序:`claudeHelper`(系统提示提取、思考配置)、`geminiHelper`(部分/内容映射)、`openaiHelper`(格式过滤)、`toolCallHelper`(ID生成、缺失响应注入)、`maxTokensHelper`、`responsesApiHelper`。 |
|
||||
| `index.ts` | — | 翻译引擎:`translateRequest()`、`translateResponse()`、状态管理、注册表。 |
|
||||
| `formats.ts` | — | 格式常量:`OPENAI`、`CLAUDE`、`GEMINI`、`ANTIGRAVITY`、`KIRO`、`CURSOR`、`OPENAI_RESPONSES`。 |
|
||||
|
||||
#### 关键设计:自注册插件
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 实用程序 (`open-sse/utils/`)
|
||||
|
||||
| 文件 | 目的 |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `error.ts` | 错误响应构建(OpenAI 兼容格式)、上游错误解析、从错误消息中提取反重力重试时间、SSE 错误流。 |
|
||||
| `stream.ts` | **SSE Transform Stream** — 核心流管道。两种模式:`TRANSLATE`(完整格式翻译)和`PASSTHROUGH`(规范化+提取使用)。处理块缓冲、使用情况估计、内容长度跟踪。每个流编码器/解码器实例避免共享状态。 |
|
||||
| `streamHelpers.ts` | 低级 SSE 实用程序:`parseSSELine`(空白容忍)、`hasValuableContent`(过滤 OpenAI/Claude/Gemini 的空块)、`fixInvalidId`、`formatSSE`(具有 `perf_metrics` 清理功能的格式感知 SSE 序列化)。 |
|
||||
| `usageTracking.ts` | 从任何格式(Claude/OpenAI/Gemini/Responses)提取令牌使用情况,使用单独的工具/消息字符/令牌比率进行估计,缓冲区添加(2000 个令牌安全裕度),特定于格式的字段过滤,使用 ANSI 颜色的控制台日志记录。 |
|
||||
| `requestLogger.ts` | 基于文件的请求日志记录(通过 `ENABLE_REQUEST_LOGS=true` 选择加入)。创建包含编号文件的会话文件夹:`1_req_client.json` → `7_res_client.txt`。所有 I/O 都是异步的(即发即弃)。屏蔽敏感标头。 |
|
||||
| `bypassHandler.ts` | 拦截来自 Claude CLI 的特定模式(标题提取、预热、计数)并返回虚假响应,而无需调用任何提供者。支持流式传输和非流式传输。有意限制为 Claude CLI 范围。 |
|
||||
| `networkProxy.ts` | 优先解析给定提供程序的出站代理 URL:提供程序特定的配置 → 全局配置 → 环境变量 (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`)。支持 `NO_PROXY` 排除。缓存配置 30 秒。 |
|
||||
|
||||
#### SSE 流媒体管道
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### 请求记录器会话结构
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 应用层 (`src/`)
|
||||
|
||||
| 目录 | 目的 |
|
||||
| ------------- | -------------------------------------------------------- |
|
||||
| `src/app/` | Web UI、API 路由、Express 中间件、OAuth 回调处理程序 |
|
||||
| `src/lib/` | 数据库访问(`localDb.ts`、`usageDb.ts`)、身份验证、共享 |
|
||||
| `src/mitm/` | 用于拦截提供商流量的中间人代理实用程序 |
|
||||
| `src/models/` | 数据库模型定义 |
|
||||
| `src/shared/` | open-sse 函数(提供程序、流、错误等)的包装器 |
|
||||
| `src/sse/` | 将 open-sse 库连接到 Express 路由的 SSE 端点处理程序 |
|
||||
| `src/store/` | 应用状态管理 |
|
||||
|
||||
#### 值得注意的 API 路由
|
||||
|
||||
| 路线 | 方法 | 目的 |
|
||||
| --------------------------------------------- | -------------- | ------------------------------------------------------------ |
|
||||
| `/api/provider-models` | 获取/发布/删除 | 针对每个提供商的自定义模型的 CRUD |
|
||||
| `/api/models/catalog` | 获取 | 按提供商分组的所有模型(聊天、嵌入、图像、自定义)的聚合目录 |
|
||||
| `/api/settings/proxy` | 获取/放置/删除 | 分层出站代理配置 (`global/providers/combos/keys`) |
|
||||
| `/api/settings/proxy/test` | 发布 | 验证代理连接并返回公共 IP/延迟 |
|
||||
| `/v1/providers/[provider]/chat/completions` | 发布 | 通过模型验证完成每个提供商的专用聊天 |
|
||||
| `/v1/providers/[provider]/embeddings` | 发布 | 具有模型验证功能的专用每个提供商嵌入 |
|
||||
| `/v1/providers/[provider]/images/generations` | 发布 | 通过模型验证生成专用的每个提供商图像 |
|
||||
| `/api/settings/ip-filter` | 获取/放置 | IP 允许列表/阻止列表管理 |
|
||||
| `/api/settings/thinking-budget` | 获取/放置 | 推理代币预算配置(直通/自动/自定义/自适应) |
|
||||
| `/api/settings/system-prompt` | 获取/放置 | 所有请求的全局系统提示注入 |
|
||||
| `/api/sessions` | 获取 | 活动会话跟踪和指标 |
|
||||
| `/api/rate-limits` | 获取 | 每个帐户的速率限制状态 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 关键设计模式
|
||||
|
||||
### 5.1 轴辐式翻译
|
||||
|
||||
所有格式均通过 **OpenAI 格式作为中心**进行转换。添加新的提供者只需要编写**一对**翻译器(到/来自 OpenAI),而不是 N 对。
|
||||
|
||||
### 5.2 执行者策略模式
|
||||
|
||||
每个提供者都有一个继承自 `BaseExecutor` 的专用执行器类。 `executors/index.ts` 中的工厂在运行时选择正确的一个。
|
||||
|
||||
### 5.3 自注册插件系统
|
||||
|
||||
翻译器模块在导入时通过 `register()` 注册自身。添加新翻译器只是创建一个文件并将其导入。
|
||||
|
||||
### 5.4 具有指数退避的账户回退
|
||||
|
||||
当提供者返回 429/401/500 时,系统可以切换到下一个帐户,应用指数冷却时间(1 秒 → 2 秒 → 4 秒 → 最长 2 分钟)。
|
||||
|
||||
### 5.5 组合模型链
|
||||
|
||||
“组合”将多个 `provider/model` 字符串组合在一起。如果第一个失败,则自动回退到下一个。
|
||||
|
||||
### 5.6 有状态流式翻译
|
||||
|
||||
响应翻译通过 `initState()` 机制维护跨 SSE 块的状态(思维块跟踪、工具调用积累、内容块索引)。
|
||||
|
||||
### 5.7 使用安全缓冲区
|
||||
|
||||
在报告的使用情况中添加了 2000 个令牌缓冲区,以防止客户端由于系统提示和格式转换的开销而达到上下文窗口限制。
|
||||
|
||||
---
|
||||
|
||||
## 6. 支持的格式
|
||||
|
||||
| 格式 | 方向 | 标识符 |
|
||||
| --------------- | --------- | ------------------ |
|
||||
| OpenAI 聊天完成 | 来源+目标 | `openai` |
|
||||
| OpenAI 响应 API | 来源+目标 | `openai-responses` |
|
||||
| 人类克劳德 | 来源+目标 | `claude` |
|
||||
| 谷歌双子座 | 来源+目标 | `gemini` |
|
||||
| 谷歌 Gemini CLI | 仅目标 | `gemini-cli` |
|
||||
| 反重力 | 来源+目标 | `antigravity` |
|
||||
| AWS 基罗 | AWS仅目标 | `kiro` |
|
||||
| 光标 | 仅目标 | `cursor` |
|
||||
|
||||
---
|
||||
|
||||
## 7. 支持的提供商
|
||||
|
||||
| 供应商 | 认证方式 | 执行人 | 要点 |
|
||||
| ------------------------ | -------------------- | --------- | --------------------------------- |
|
||||
| 人类克劳德 | API 密钥或 OAuth | 默认 | 使用 `x-api-key` 标头 |
|
||||
| 谷歌双子座 | API 密钥或 OAuth | 默认 | 使用 `x-goog-api-key` 标头 |
|
||||
| 谷歌 Gemini CLI | OAuth | GeminiCLI | 使用 `streamGenerateContent` 端点 |
|
||||
| 反重力 | OAuth | 反重力 | 多 URL 回退、自定义重试解析 |
|
||||
| 开放人工智能 | API 密钥 | 默认 | 标准持有者身份验证 |
|
||||
| 法典 | OAuth | 法典 | 注入系统指令,管理思维 |
|
||||
| GitHub 副驾驶 | OAuth + Copilot 令牌 | GitHub | 双令牌,VSCode 标头模仿 |
|
||||
| 基罗 (AWS) | AWS SSO OIDC 或社交 | 基罗 | 二进制EventStream解析 |
|
||||
| 光标IDE | 校验和验证 | 光标 | Protobuf 编码、SHA-256 校验和 |
|
||||
| 奎文 | OAuth | 默认 | 标准授权 |
|
||||
| iFlow | OAuth(基本 + 承载) | 默认 | 双重身份验证标头 |
|
||||
| 开放路由器 | API 密钥 | 默认 | 标准持有者身份验证 |
|
||||
| GLM、Kimi、MiniMax | API 密钥 | 默认 | 克劳德兼容,使用 `x-api-key` |
|
||||
| `openai-compatible-*` | API 密钥 | 默认 | 动态:任何 OpenAI 兼容端点 |
|
||||
| `anthropic-compatible-*` | API 密钥 | 默认 | 动态:任何与 Claude 兼容的端点 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据流总结
|
||||
|
||||
### 流媒体请求
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### 非流式请求
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### 旁路流程(Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
77
docs/i18n/zh-CN/FEATURES.md
Normal file
77
docs/i18n/zh-CN/FEATURES.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# OmniRoute — 仪表板功能库
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../FEATURES.md) | 🇧🇷 [Português (Brasil)](../pt-BR/FEATURES.md) | 🇪🇸 [Español](../es/FEATURES.md) | 🇫🇷 [Français](../fr/FEATURES.md) | 🇮🇹 [Italiano](../it/FEATURES.md) | 🇷🇺 [Русский](../ru/FEATURES.md) | 🇨🇳 [中文 (简体)](../zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](../de/FEATURES.md) | 🇮🇳 [हिन्दी](../in/FEATURES.md) | 🇹🇭 [ไทย](../th/FEATURES.md) | 🇺🇦 [Українська](../uk-UA/FEATURES.md) | 🇸🇦 [العربية](../ar/FEATURES.md) | 🇯🇵 [日本語](../ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](../vi/FEATURES.md) | 🇧🇬 [Български](../bg/FEATURES.md) | 🇩🇰 [Dansk](../da/FEATURES.md) | 🇫🇮 [Suomi](../fi/FEATURES.md) | 🇮🇱 [עברית](../he/FEATURES.md) | 🇭🇺 [Magyar](../hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](../id/FEATURES.md) | 🇰🇷 [한국어](../ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](../ms/FEATURES.md) | 🇳🇱 [Nederlands](../nl/FEATURES.md) | 🇳🇴 [Norsk](../no/FEATURES.md) | 🇵🇹 [Português (Portugal)](../pt/FEATURES.md) | 🇷🇴 [Română](../ro/FEATURES.md) | 🇵🇱 [Polski](../pl/FEATURES.md) | 🇸🇰 [Slovenčina](../sk/FEATURES.md) | 🇸🇪 [Svenska](../sv/FEATURES.md) | 🇵🇭 [Filipino](../phi/FEATURES.md)
|
||||
|
||||
OmniRoute 仪表板每个部分的视觉指南。
|
||||
|
||||
---
|
||||
|
||||
## 🔌 提供商
|
||||
|
||||
管理 AI 提供商连接:OAuth 提供商(Claude Code、Codex、Gemini CLI)、API 密钥提供商(Groq、DeepSeek、OpenRouter)和免费提供商(iFlow、Qwen、Kiro)。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 组合
|
||||
|
||||
使用 6 种策略创建模型路由组合:填充优先、循环、二选一、随机、最少使用和成本优化。每个组合都会链接多个模型并自动回退。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 分析
|
||||
|
||||
全面的使用分析,包括代币消耗、成本估算、活动热图、每周分布图和每个提供商的细分。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 系统健康状况
|
||||
|
||||
实时监控:正常运行时间、内存、版本、延迟百分位数 (p50/p95/p99)、缓存统计数据和提供商断路器状态。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 翻译游乐场
|
||||
|
||||
用于调试 API 翻译的四种模式:**Playground**(格式转换器)、**Chat Tester**(实时请求)、**Test Bench**(批量测试)和 **Live Monitor**(实时流)。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 设置
|
||||
|
||||
常规设置、系统存储、备份管理(导出/导入数据库)、外观(深色/浅色模式)、安全性(包括 API 端点保护和自定义提供程序阻止)、路由、弹性和高级配置。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 CLI 工具
|
||||
|
||||
一键配置AI编码工具:Claude Code、Codex CLI、Gemini CLI、OpenClaw、Kilo Code、Antigravity。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📝 请求日志
|
||||
|
||||
实时请求记录,并按提供商、模型、帐户和 API 密钥进行过滤。显示状态代码、令牌使用情况、延迟和响应详细信息。
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 API 端点
|
||||
|
||||
您的统一 API 端点具有功能细分:聊天完成、嵌入、图像生成、重新排名、音频转录和注册 API 密钥。
|
||||
|
||||

|
||||
216
docs/i18n/zh-CN/TROUBLESHOOTING.md
Normal file
216
docs/i18n/zh-CN/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# 故障排除
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](../pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](../es/TROUBLESHOOTING.md) | 🇫🇷 [Français](../fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](../it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](../ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](../zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](../de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](../in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](../th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](../uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](../ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](../ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](../vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](../bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](../da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](../fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](../he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](../hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](../id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](../ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](../ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](../nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](../no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](../pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](../ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](../pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](../sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](../sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](../phi/TROUBLESHOOTING.md)
|
||||
|
||||
OmniRoute 的常见问题和解决方案。
|
||||
|
||||
---
|
||||
|
||||
## 快速修复
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
| ---------------------- | ------------------------------------------------------------------ |
|
||||
| 首次登录无法使用 | 检查 `.env` 中的 `INITIAL_PASSWORD`(默认:`123456`) |
|
||||
| 仪表板在错误端口上打开 | 设置 `PORT=20128` 和 `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| `logs/` 下没有请求日志 | 设置 `ENABLE_REQUEST_LOGS=true` |
|
||||
| EACCES:权限被拒绝 | 设置 `DATA_DIR=/path/to/writable/dir` 来覆盖 `~/.omniroute` |
|
||||
| 路由策略未保存 | 更新至 v1.4.11+(Zod 架构修复设置持久性) |
|
||||
|
||||
---
|
||||
|
||||
## 提供商问题
|
||||
|
||||
###“语言模型未提供消息”
|
||||
|
||||
**原因:** 提供商配额已用完。
|
||||
|
||||
**修复:**
|
||||
|
||||
1.检查仪表板配额跟踪器2. 使用具有后备层的组合3.切换到更便宜/免费的套餐
|
||||
|
||||
### 速率限制
|
||||
|
||||
**原因:** 订阅配额已用完。
|
||||
|
||||
**修复:**
|
||||
|
||||
- 添加后备:`cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- 使用 GLM/MiniMax 作为廉价备份
|
||||
|
||||
### OAuth 令牌已过期
|
||||
|
||||
OmniRoute 自动刷新令牌。如果问题仍然存在:
|
||||
|
||||
1. 仪表板 → 提供商 → 重新连接 2.删除并重新添加提供商连接
|
||||
|
||||
---
|
||||
|
||||
## 云问题
|
||||
|
||||
### 云同步错误
|
||||
|
||||
1. 验证 `BASE_URL` 指向您正在运行的实例(例如 `http://localhost:20128`)
|
||||
2. 验证 `CLOUD_URL` 指向您的云端点(例如 `https://omniroute.dev`)
|
||||
3. 保持 `NEXT_PUBLIC_*` 值与服务器端值一致
|
||||
|
||||
### 云 `stream=false` 返回 500
|
||||
|
||||
**症状:** 云端点上的 `Unexpected token 'd'...` 用于非流式调用。
|
||||
|
||||
**原因:** 上游返回 SSE 负载,而客户端需要 JSON。
|
||||
|
||||
**解决方法:** 使用 `stream=true` 进行云直接调用。本地运行时包括 SSE→JSON 回退。
|
||||
|
||||
### 云显示已连接但“API 密钥无效”
|
||||
|
||||
1. 从本地仪表板创建新密钥 (`/api/keys`)
|
||||
2. 运行云同步:启用云→立即同步
|
||||
3. 旧的/未同步的密钥仍然可以在云上返回 `401`
|
||||
|
||||
---
|
||||
|
||||
## Docker 问题
|
||||
|
||||
### CLI 工具显示未安装
|
||||
|
||||
1. 检查运行时字段:`curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. 对于便携模式:使用映像目标 `runner-cli`(捆绑的 CLI)
|
||||
3. 对于主机挂载模式:设置`CLI_EXTRA_PATHS`并挂载主机bin目录为只读
|
||||
4. 如果 `installed=true` 和 `runnable=false`:找到二进制文件,但运行状况检查失败
|
||||
|
||||
### 快速运行时验证
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 成本问题
|
||||
|
||||
### 高成本
|
||||
|
||||
1. 在 Dashboard → 使用情况中查看使用情况统计数据
|
||||
2. 将主模型切换为GLM/MiniMax
|
||||
3. 使用免费层(Gemini CLI、iFlow)执行非关键任务
|
||||
4. 设置每个 API 密钥的成本预算:仪表板 → API 密钥 → 预算
|
||||
|
||||
---
|
||||
|
||||
## 调试
|
||||
|
||||
### 启用请求日志
|
||||
|
||||
在 `.env` 文件中设置 `ENABLE_REQUEST_LOGS=true`。日志显示在 `logs/` 目录下。
|
||||
|
||||
### 检查提供商的健康状况
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### 运行时存储
|
||||
|
||||
- 主要状态:`${DATA_DIR}/db.json`(提供程序、组合、别名、键、设置)
|
||||
- 用法:`${DATA_DIR}/usage.json`、`${DATA_DIR}/log.txt`、`${DATA_DIR}/call_logs/`
|
||||
- 请求日志:`<repo>/logs/...`(当 `ENABLE_REQUEST_LOGS=true` 时)
|
||||
|
||||
---
|
||||
|
||||
## 断路器问题
|
||||
|
||||
### 提供程序陷入打开状态
|
||||
|
||||
当提供商的断路器打开时,请求将被阻止,直到冷却时间到期。
|
||||
|
||||
**修复:**
|
||||
|
||||
1. 转到 **仪表板 → 设置 → 弹性**
|
||||
2. 检查受影响提供商的断路器卡
|
||||
3. 单击“**全部重置**”以清除所有断路器,或等待冷却时间到期
|
||||
4. 重置前验证提供商是否确实可用
|
||||
|
||||
### 提供商不断使断路器跳闸
|
||||
|
||||
如果提供者重复进入 OPEN 状态:
|
||||
|
||||
1. 检查 **仪表板 → 运行状况 → 提供商运行状况** 以了解故障模式
|
||||
2. 转到 **设置 → 恢复能力 → 提供商配置文件** 并增加失败阈值
|
||||
3. 检查提供商是否更改了 API 限制或需要重新身份验证
|
||||
4. 检查延迟遥测 — 高延迟可能会导致基于超时的故障
|
||||
|
||||
---
|
||||
|
||||
## 音频转录问题
|
||||
|
||||
### “不支持的型号”错误
|
||||
|
||||
- 确保您使用正确的前缀:`deepgram/nova-3` 或 `assemblyai/best`
|
||||
- 验证提供商是否已在 **仪表板 → 提供商** 中连接
|
||||
|
||||
### 转录返回空或失败
|
||||
|
||||
- 检查支持的音频格式:`mp3`、`wav`、`m4a`、`flac`、`ogg`、`webm`
|
||||
- 验证文件大小是否在提供商限制内(通常< 25MB)
|
||||
- 检查提供商卡中提供商 API 密钥的有效性
|
||||
|
||||
---
|
||||
|
||||
## 翻译器调试
|
||||
|
||||
使用 **Dashboard → Translator** 调试格式转换问题:
|
||||
|
||||
| 模式 | 何时使用 |
|
||||
| -------------- | ------------------------------------------------------ |
|
||||
| **游乐场** | 并排比较输入/输出格式 — 粘贴失败的请求以查看其如何翻译 |
|
||||
| **聊天测试仪** | 发送实时消息并检查完整的请求/响应负载(包括标头) |
|
||||
| **测试台** | 跨格式组合运行批量测试以查找哪些翻译被破坏 |
|
||||
| **实时监控** | 观看实时请求流以捕获间歇性翻译问题 |
|
||||
|
||||
### 常见格式问题
|
||||
|
||||
- **思维标签未出现** — 检查目标提供商是否支持思维以及思维预算设置
|
||||
- **工具调用丢失** — 某些格式翻译可能会删除不支持的字段;在 Playground 模式下验证
|
||||
- **系统提示缺失** — Claude 和 Gemini 处理系统提示的方式不同;检查翻译输出
|
||||
- **SDK 返回原始字符串而不是对象** — 在 v1.1.0 中修复:响应清理程序现在会删除导致 OpenAI SDK Pydantic 验证失败的非标准字段(`x_groq`、`usage_breakdown` 等)
|
||||
- **GLM/ERNIE 拒绝 `system` 角色** — 在 v1.1.0 中修复:角色标准化器自动将系统消息合并到不兼容模型的用户消息中
|
||||
- **`developer` 角色无法识别** — v1.1.0 中已修复:对于非 OpenAI 提供商,自动转换为 `system`
|
||||
- **`json_schema` 不适用于 Gemini** — 在 v1.1.0 中修复:`response_format` 现在转换为 Gemini 的 `responseMimeType` + `responseSchema`
|
||||
|
||||
---
|
||||
|
||||
## 弹性设置
|
||||
|
||||
### 自动速率限制未触发
|
||||
|
||||
- 自动速率限制仅适用于 API 密钥提供商(不适用于 OAuth/订阅)
|
||||
- 验证**设置 → 弹性 → 提供商配置文件** 已启用自动速率限制
|
||||
- 检查提供商是否返回 `429` 状态代码或 `Retry-After` 标头
|
||||
|
||||
### 调整指数退避
|
||||
|
||||
提供商配置文件支持以下设置:
|
||||
|
||||
- **基本延迟** — 第一次失败后的初始等待时间(默认值:1 秒)
|
||||
- **最大延迟** — 最大等待时间上限(默认值:30 秒)
|
||||
- **乘数** — 每次连续失败增加多少延迟(默认值:2x)
|
||||
|
||||
### 抗雷兽群
|
||||
|
||||
当许多并发请求到达速率受限的提供程序时,OmniRoute 使用互斥锁 + 自动速率限制来序列化请求并防止级联故障。对于 API 密钥提供者来说,这是自动的。
|
||||
|
||||
---
|
||||
|
||||
## 仍然卡住吗?
|
||||
|
||||
- **GitHub 问题**:[github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **架构**:请参阅 [**OMNI_TOKEN_55**](ARCHITECTURE.md) 了解内部详细信息
|
||||
- **API 参考**:请参阅 [**OMNI_TOKEN_56**](API_REFERENCE.md) 了解所有端点
|
||||
- **健康仪表板**:检查**仪表板→健康**以获取实时系统状态
|
||||
- **翻译器**:使用**仪表板→翻译器**来调试格式问题
|
||||
696
docs/i18n/zh-CN/USER_GUIDE.md
Normal file
696
docs/i18n/zh-CN/USER_GUIDE.md
Normal file
@@ -0,0 +1,696 @@
|
||||
# 用户指南
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](../es/USER_GUIDE.md) | 🇫🇷 [Français](../fr/USER_GUIDE.md) | 🇮🇹 [Italiano](../it/USER_GUIDE.md) | 🇷🇺 [Русский](../ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](../de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](../in/USER_GUIDE.md) | 🇹🇭 [ไทย](../th/USER_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](../ar/USER_GUIDE.md) | 🇯🇵 [日本語](../ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/USER_GUIDE.md) | 🇧🇬 [Български](../bg/USER_GUIDE.md) | 🇩🇰 [Dansk](../da/USER_GUIDE.md) | 🇫🇮 [Suomi](../fi/USER_GUIDE.md) | 🇮🇱 [עברית](../he/USER_GUIDE.md) | 🇭🇺 [Magyar](../hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/USER_GUIDE.md) | 🇰🇷 [한국어](../ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](../nl/USER_GUIDE.md) | 🇳🇴 [Norsk](../no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/USER_GUIDE.md) | 🇷🇴 [Română](../ro/USER_GUIDE.md) | 🇵🇱 [Polski](../pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/USER_GUIDE.md) | 🇸🇪 [Svenska](../sv/USER_GUIDE.md) | 🇵🇭 [Filipino](../phi/USER_GUIDE.md)
|
||||
|
||||
有关配置提供程序、创建组合、集成 CLI 工具和部署 OmniRoute 的完整指南。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [Pricing at a Glance](#-pricing-at-a-glance)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Provider Setup](#-provider-setup)
|
||||
- [CLI Integration](#-cli-integration)
|
||||
- [Deployment](#-deployment)
|
||||
- [Available Models](#-available-models)
|
||||
- [Advanced Features](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 定价一览
|
||||
|
||||
| 等级 | 供应商 | 成本 | 配额重置 | 最适合 |
|
||||
| --------------- | ---------------------- | ------------------- | --------------- | -------------- |
|
||||
| **💳 订阅** | 克劳德代码(专业版) | $20/月 | 5 小时+ 每周 | 已经订阅 |
|
||||
| | Codex(增强版/专业版) | $20-200/月 | 5 小时+ 每周 | OpenAI 用户 |
|
||||
| | 双子座 CLI | **免费** | 180K/月 + 1K/天 | 每个人! |
|
||||
| | GitHub 副驾驶 | $10-19/月 | 每月 | GitHub 用户 |
|
||||
| **🔑 API 密钥** | 深度搜索 | 按使用付费 | 无 | 廉价推理 |
|
||||
| | 格罗克 | 按使用付费 | 无 | 超快速推理 |
|
||||
| | xAI (Grok) | 按使用付费 | 无 | Grok 4 推理 |
|
||||
| | 米斯特拉尔 | 按使用付费 | 无 | 欧盟主办的模型 |
|
||||
| | 困惑 | 按使用付费 | 无 | 搜索增强 |
|
||||
| | 一起人工智能 | 按使用付费 | 无 | 开源模型 |
|
||||
| | 烟花人工智能 | 按使用付费 | 无 | 快速通量图像 |
|
||||
| | 大脑 | 按使用付费 | 无 | 晶圆级速度 |
|
||||
| | 连贯 | 按使用付费 | 无 | 命令 R+ RAG |
|
||||
| | NVIDIA NIM | 按使用付费 | 无 | 企业典范 |
|
||||
| **💰便宜** | GLM-4.7 | 0.6 美元/100 万美元 | 每日上午 10 点 | 预算备份 |
|
||||
| | 迷你最大M2.1 | 0.2 美元/100 万美元 | 5小时滚动 | 最便宜的选择 |
|
||||
| | 基米K2 | 每月 9 美元的公寓 | 10M 代币/月 | 可预测的成本 |
|
||||
| **🆓 免费** | iFlow | 0 美元 | 无限 | 8 款免费 |
|
||||
| | 奎文 | 0 美元 | 无限 | 3 款免费 |
|
||||
| | 基罗 | 0 美元 | 无限 | 克劳德自由 |
|
||||
|
||||
**💡专业提示:** 从 Gemini CLI(180K 免费/月)+ iFlow(无限免费)组合开始 = 0 美元成本!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 使用案例
|
||||
|
||||
### 案例 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:“我想要零成本”
|
||||
|
||||
**问题:** 无力订阅,需要可靠的人工智能编码
|
||||
|
||||
```
|
||||
Combo: "free-forever"
|
||||
1. gc/gemini-3-flash (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited free)
|
||||
3. qw/qwen3-coder-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. minimax/MiniMax-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”
|
||||
|
||||
**问题:** 需要在消息应用程序中使用人工智能助手,完全免费
|
||||
|
||||
```
|
||||
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...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 提供商设置
|
||||
|
||||
### 🔐 订阅提供商
|
||||
|
||||
#### 克劳德代码(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 来提高速度。 OmniRoute 跟踪每个模型的配额!
|
||||
|
||||
#### OpenAI Codex(增强版/专业版)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/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 副驾驶
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ Monthly reset (1st of month)
|
||||
|
||||
Models:
|
||||
gh/gpt-5
|
||||
gh/claude-4.5-sonnet
|
||||
gh/gemini-3-pro
|
||||
```
|
||||
|
||||
### 💰 廉价提供商
|
||||
|
||||
#### GLM-4.7(每日重置,0.6 美元/100 万美元)
|
||||
|
||||
1. 注册:[Zhipu AI](https://open.bigmodel.cn/)
|
||||
2. 从 Coding Plan 获取 API 密钥
|
||||
3. 控制面板 → 添加 API 密钥:提供商:`glm`,API 密钥:`your-key`
|
||||
|
||||
**使用:** `glm/glm-4.7` — **专业提示:** Coding Plan 以 1/7 的成本提供 3× 配额!每天上午 10:00 重置。
|
||||
|
||||
#### MiniMax M2.1(5 小时重置,0.20 美元/100 万美元)
|
||||
|
||||
1. 注册:[MiniMax](https://www.minimax.io/) 2.获取API密钥→仪表板→添加API密钥
|
||||
|
||||
**使用:** `minimax/MiniMax-M2.1` — **专业提示:** 长上下文(1M 令牌)的最便宜选择!
|
||||
|
||||
#### Kimi K2(每月 9 美元)
|
||||
|
||||
1.订阅:[Moonshot AI](https://platform.moonshot.ai/) 2.获取API密钥→仪表板→添加API密钥
|
||||
|
||||
**使用:** `kimi/kimi-latest` — **专业提示:** 固定 9 美元/月 1000 万个代币 = 0.90 美元/100 万个有效成本!
|
||||
|
||||
### 🆓 免费提供商
|
||||
|
||||
#### iFlow(8 个免费模型)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect 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 auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### Kiro(克劳德·自由)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
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/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### 示例 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 集成
|
||||
|
||||
### 光标 IDE
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### 克劳德·代码
|
||||
|
||||
编辑 `~/.claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### 开爪
|
||||
|
||||
编辑 `~/.openclaw/openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**或使用仪表板:** CLI 工具 → OpenClaw → 自动配置
|
||||
|
||||
### 克莱恩 / 继续 / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署
|
||||
|
||||
### VPS 部署
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### 码头工人
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
对于使用 CLI 二进制文件的主机集成模式,请参阅主文档中的 Docker 部分。
|
||||
|
||||
### 环境变量
|
||||
|
||||
| 变量 | 默认 | 描述 |
|
||||
| --------------------- | ------------------------------------ | -------------------------------------------------- |
|
||||
| `JWT_SECRET` | `omniroute-default-secret-change-me` | JWT 签名秘密(**生产变更**) |
|
||||
| `INITIAL_PASSWORD` | `123456` | 首次登录密码 |
|
||||
| `DATA_DIR` | `~/.omniroute` | 数据目录(数据库、使用情况、日志) |
|
||||
| `PORT` | 框架默认 | 服务端口(示例中为 `20128`) |
|
||||
| `HOSTNAME` | 框架默认 | 绑定主机(Docker 默认为 `0.0.0.0`) |
|
||||
| `NODE_ENV` | 运行时默认 | 设置 `production` 进行部署 |
|
||||
| `BASE_URL` | `http://localhost:20128` | 服务器端内部基本 URL |
|
||||
| `CLOUD_URL` | `https://omniroute.dev` | 云同步端点基本 URL |
|
||||
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | 生成的 API 密钥的 HMAC 秘密 |
|
||||
| `REQUIRE_API_KEY` | `false` | 在 `/v1/*` 上强制执行 Bearer API 密钥 |
|
||||
| `ENABLE_REQUEST_LOGS` | `false` | 启用请求/响应日志 |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | 强制 `Secure` auth cookie(在 HTTPS 反向代理后面) |
|
||||
|
||||
有关完整环境变量参考,请参阅 [README](../README.md)。
|
||||
|
||||
---
|
||||
|
||||
## 📊 可用型号
|
||||
|
||||
<details>
|
||||
<summary><b>查看所有可用型号</b></summary>
|
||||
|
||||
**克劳德代码 (`cc/`)** — Pro/Max:`cc/claude-opus-4-6`、`cc/claude-sonnet-4-5-20250929`、`cc/claude-haiku-4-5-20251001`
|
||||
|
||||
**法典 (`cx/`)** — 增强版/专业版:`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 美元/100 万美元:`glm/glm-4.7`
|
||||
|
||||
**MiniMax (`minimax/`)** — 0.2 美元/100 万美元:`minimax/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`
|
||||
|
||||
**DeepSeek (`ds/`)**:`ds/deepseek-chat`、`ds/deepseek-reasoner`
|
||||
|
||||
**Groq (`groq/`)**:`groq/llama-3.3-70b-versatile`、`groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI (`xai/`)**:`xai/grok-4`、`xai/grok-4-0709-fast-reasoning`、`xai/grok-code-mini`
|
||||
|
||||
**米斯特拉尔 (`mistral/`)**:`mistral/mistral-large-2501`、`mistral/codestral-2501`
|
||||
|
||||
**困惑 (`pplx/`)**:`pplx/sonar-pro`、`pplx/sonar`
|
||||
|
||||
**一起人工智能 (`together/`)**:`together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**烟花人工智能 (`fireworks/`)**:`fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**Cerebras (`cerebras/`)**:`cerebras/llama-3.3-70b`
|
||||
|
||||
**一致 (`cohere/`)**:`cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM (`nvidia/`)**:`nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 高级功能
|
||||
|
||||
### 定制模型
|
||||
|
||||
将任何模型 ID 添加到任何提供商,无需等待应用程序更新:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
或者使用仪表板:**提供商 → [提供商] → 自定义模型**。
|
||||
|
||||
### 专用提供商路线
|
||||
|
||||
通过模型验证将请求直接路由到特定提供者:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
如果缺少提供商前缀,则会自动添加。不匹配的模型返回 `400`。
|
||||
|
||||
### 网络代理配置
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**优先级:**特定于键→特定于组合→特定于提供者→全局→环境。
|
||||
|
||||
### 模型目录 API
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
返回按类型(`chat`、`embedding`、`image`)提供者分组的模型。
|
||||
|
||||
### 云同步
|
||||
|
||||
- 跨设备同步提供商、组合和设置
|
||||
- 自动后台同步,带超时+快速失败
|
||||
- 在生产中更喜欢服务器端 `BASE_URL`/`CLOUD_URL`
|
||||
|
||||
### LLM Gateway Intelligence(第 9 阶段)
|
||||
|
||||
- **语义缓存** — 自动缓存非流式传输、温度=0 响应(使用 `X-OmniRoute-No-Cache: true` 绕过)
|
||||
- **请求幂等性** — 通过 `Idempotency-Key` 或 `X-Request-Id` 标头在 5 秒内删除重复请求
|
||||
- **进度跟踪** — 通过 `X-OmniRoute-Progress: true` 标头选择加入 SSE `event: progress` 事件
|
||||
|
||||
---
|
||||
|
||||
### 翻译游乐场
|
||||
|
||||
通过**仪表板 → 翻译器**访问。调试并可视化 OmniRoute 如何在提供者之间转换 API 请求。
|
||||
|
||||
| 模式 | 目的 |
|
||||
| -------------- | ------------------------------------------------- |
|
||||
| **游乐场** | 选择源/目标格式,粘贴请求,然后立即查看翻译的输出 |
|
||||
| **聊天测试仪** | 通过代理发送实时聊天消息并检查完整的请求/响应周期 |
|
||||
| **测试台** | 跨多种格式组合运行批量测试以验证翻译的正确性 |
|
||||
| **实时监控** | 当请求流经代理时观看实时翻译 |
|
||||
|
||||
**使用案例:**
|
||||
|
||||
- 调试特定客户端/提供商组合失败的原因
|
||||
- 验证思维标签、工具调用和系统提示是否正确翻译
|
||||
- 比较 OpenAI、Claude、Gemini 和 Responses API 格式之间的格式差异
|
||||
|
||||
---
|
||||
|
||||
### 路由策略
|
||||
|
||||
通过**仪表板→设置→路由**进行配置。
|
||||
|
||||
| 战略 | 描述 |
|
||||
| ------------------------- | ------------------------------------------------------------------- |
|
||||
| **先填写** | 按优先级顺序使用帐户 — 主帐户处理所有请求,直到不可用为止 |
|
||||
| **循环赛** | 循环浏览所有帐户,并具有可配置的粘性限制(默认:每个帐户 3 次调用) |
|
||||
| **P2C(两种选择的力量)** | 随机选择 2 个账户并选择更健康的账户 — 平衡负荷与健康意识 |
|
||||
| **随机** | 使用 Fisher-Yates shuffle 为每个请求随机选择一个帐户 |
|
||||
| **最少使用** | 路由到具有最早 `lastUsedAt` 时间戳的帐户,均匀分配流量 |
|
||||
| **成本优化** | 路由至具有最低优先级值的帐户,针对成本最低的提供商进行优化 |
|
||||
|
||||
#### 通配符模型别名
|
||||
|
||||
创建通配符模式来重新映射模型名称:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
通配符支持 `*`(任何字符)和 `?`(单个字符)。
|
||||
|
||||
#### 后备链
|
||||
|
||||
定义适用于所有请求的全局后备链:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 弹性和断路器
|
||||
|
||||
通过**仪表板→设置→弹性**进行配置。
|
||||
|
||||
OmniRoute 通过四个组件实现提供商级弹性:
|
||||
|
||||
1. **提供商配置文件** — 每个提供商的配置:
|
||||
- 失败阈值(打开前有多少次失败)
|
||||
- 冷却时间
|
||||
- 速率限制检测灵敏度
|
||||
- 指数退避参数
|
||||
|
||||
2. **可编辑的速率限制** — 可在仪表板中配置的系统级默认值:
|
||||
- **每分钟请求数 (RPM)** — 每个帐户每分钟最大请求数
|
||||
- **请求之间的最小时间** — 请求之间的最小间隔(以毫秒为单位)
|
||||
- **最大并发请求** — 每个帐户的最大并发请求数
|
||||
- 点击**编辑**进行修改,然后点击**保存**或**取消**。价值通过弹性 API 得以保留。
|
||||
|
||||
3. **断路器** — 跟踪每个提供商的故障并在达到阈值时自动打开电路:
|
||||
- **CLOSED**(健康)— 请求正常流动
|
||||
- **OPEN** — 提供商在多次失败后被暂时阻止
|
||||
- **HALF_OPEN** — 测试提供商是否已恢复
|
||||
|
||||
4. **策略和锁定标识符** — 显示断路器状态和具有强制解锁功能的锁定标识符。
|
||||
|
||||
5. **速率限制自动检测** — 监控 `429` 和 `Retry-After` 标头,以主动避免达到提供商速率限制。
|
||||
|
||||
**专业提示:** 当提供商从中断中恢复时,使用 **全部重置** 按钮可以清除所有断路器和冷却时间。
|
||||
|
||||
---
|
||||
|
||||
### 数据库导出/导入
|
||||
|
||||
在**仪表板→设置→系统和存储**中管理数据库备份。
|
||||
|
||||
| 行动 | 描述 |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------- |
|
||||
| **导出数据库** | 将当前 SQLite 数据库下载为 `.sqlite` 文件 |
|
||||
| **全部导出 (.tar.gz)** | 下载完整的备份存档,包括:数据库、设置、组合、提供商连接(无凭据)、API 密钥元数据 |
|
||||
| **导入数据库** | 上传 `.sqlite` 文件以替换当前数据库。自动创建导入前备份 |
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**导入验证:** 验证导入文件的完整性(SQLite 编译指示检查)、所需表(`provider_connections`、`provider_nodes`、`combos`、`api_keys`)和大小(最大 100MB)。
|
||||
|
||||
**使用案例:**
|
||||
|
||||
- 在机器之间迁移 OmniRoute
|
||||
- 创建外部备份以进行灾难恢复
|
||||
- 在团队成员之间共享配置(导出全部→共享存档)
|
||||
|
||||
---
|
||||
|
||||
### 设置仪表板
|
||||
|
||||
设置页面分为 5 个选项卡,以便于导航:
|
||||
|
||||
| 选项卡 | 内容 |
|
||||
| ------------ | ----------------------------------------------------------------- |
|
||||
| **安全** | 登录/密码设置、IP 访问控制、`/models` 的 API 身份验证和提供商阻止 |
|
||||
| **路由** | 全局路由策略(6 个选项)、通配符模型别名、后备链、组合默认值 |
|
||||
| **弹性** | 提供商资料、可编辑的速率限制、断路器状态、策略和锁定标识符 |
|
||||
| **人工智能** | 思维预算配置、全局系统提示注入、提示缓存统计 |
|
||||
| **高级** | 全局代理配置(HTTP/SOCKS5) |
|
||||
|
||||
---
|
||||
|
||||
### 成本和预算管理
|
||||
|
||||
通过**仪表板 → 成本**访问。
|
||||
|
||||
| 选项卡 | 目的 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| **预算** | 通过每日/每周/每月预算和实时跟踪设置每个 API 密钥的支出限额 |
|
||||
| **定价** | 查看和编辑模型定价条目 - 每个提供商每 1K 输入/输出代币的成本 |
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**成本跟踪:** 每个请求都会记录令牌使用情况并使用定价表计算成本。按提供商、型号和 API 密钥查看 **仪表板 → 使用情况** 中的细分。
|
||||
|
||||
---
|
||||
|
||||
### 音频转录
|
||||
|
||||
OmniRoute 支持通过 OpenAI 兼容端点进行音频转录:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
可用提供程序:**Deepgram** (`deepgram/`)、**AssemblyAI** (`assemblyai/`)。
|
||||
|
||||
支持的音频格式:`mp3`、`wav`、`m4a`、`flac`、`ogg`、`webm`。
|
||||
|
||||
---
|
||||
|
||||
### 组合平衡策略
|
||||
|
||||
在**仪表板→组合→创建/编辑→策略**中配置每个组合的平衡。
|
||||
|
||||
| 战略 | 描述 |
|
||||
| ------------ | ---------------------------------------- |
|
||||
| **循环赛** | 按顺序轮换模型 |
|
||||
| **优先** | 总是尝试第一个模型;仅在错误时才回退 |
|
||||
| **随机** | 为每个请求从组合中选择一个随机模型 |
|
||||
| **加权** | 根据每个模型分配的权重按比例路由 |
|
||||
| **最少使用** | 路由到最近请求最少的模型(使用组合指标) |
|
||||
| **成本优化** | 通往最便宜可用型号的路线(使用定价表) |
|
||||
|
||||
全局组合默认值可以在**仪表板→设置→路由→组合默认值**中设置。
|
||||
|
||||
---
|
||||
|
||||
### 健康仪表板
|
||||
|
||||
通过**仪表板→健康**访问。 6张卡实时系统健康概览:
|
||||
|
||||
| 卡 | 它显示了什么 |
|
||||
| -------------- | ------------------------------------------ |
|
||||
| **系统状态** | 正常运行时间、版本、内存使用情况、数据目录 |
|
||||
| **提供者健康** | 每个提供商的断路器状态(闭合/打开/半开) |
|
||||
| **速率限制** | 每个帐户的活动速率限制冷却时间和剩余时间 |
|
||||
| **主动锁定** | 供应商因封锁政策而暂时被封锁 |
|
||||
| **签名缓存** | 重复数据删除缓存统计信息(活动键、命中率) |
|
||||
| **延迟遥测** | 每个提供商的 p50/p95/p99 延迟聚合 |
|
||||
|
||||
**专业提示:** 健康页面每 10 秒自动刷新一次。使用断路器卡来识别哪些提供商遇到问题。
|
||||
246
scripts/i18n/apply-priority-overrides.mjs
Normal file
246
scripts/i18n/apply-priority-overrides.mjs
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const MESSAGES_DIR = path.join(ROOT, "src", "i18n", "messages");
|
||||
|
||||
const MESSAGE_OVERRIDES = {
|
||||
es: {
|
||||
"common.disabled": "Deshabilitado",
|
||||
"common.model": "Modelo",
|
||||
"common.account": "Cuenta",
|
||||
"common.time": "Hora",
|
||||
"common.columns": "Columnas",
|
||||
"common.newest": "Más reciente",
|
||||
"common.oldest": "Más antiguo",
|
||||
"common.yes": "Sí",
|
||||
"sidebar.combos": "Combos",
|
||||
"sidebar.docs": "Documentación",
|
||||
"sidebar.apiManager": "Gestor de API",
|
||||
"header.providerDescription": "Gestiona tus conexiones de proveedores de IA",
|
||||
"header.combos": "Combos",
|
||||
"header.comboDescription": "Combinaciones de modelos con fallback",
|
||||
"header.analytics": "Analíticas",
|
||||
"header.anthropicCompatible": "Compatible con Anthropic",
|
||||
"home.quickStartDesc":
|
||||
"Empieza en 4 pasos. Conecta proveedores, enruta modelos y monitorea todo.",
|
||||
"home.fullDocs": "Documentación completa",
|
||||
"home.step1Desc":
|
||||
"Ve a <endpoint>Endpoint</endpoint> -> Claves registradas. Genera una clave por entorno.",
|
||||
"home.step2Desc":
|
||||
"Agrega cuentas en <providers>Proveedores</providers>. Soporta OAuth, API Key y planes gratuitos.",
|
||||
"home.step3Title": "3. Configura tu cliente",
|
||||
"home.step3Desc": "Configura la URL base como {url} en tu IDE o cliente API.",
|
||||
"home.step4Desc":
|
||||
"Supervisa tokens, costos y errores en <logs>Registros</logs> y <analytics>Analíticas</analytics>.",
|
||||
"home.requestsShort": "{count} reqs",
|
||||
"auth.unifiedProxy": "Proxy de API de IA unificado",
|
||||
"auth.unifiedAiApiProxy": "Proxy de API de IA unificado",
|
||||
"auth.runOnboardingWizard":
|
||||
"Ejecuta el asistente de onboarding para configurar tu contraseña y conectar tu primer proveedor de IA.",
|
||||
"auth.stopServer": "Detén el servidor OmniRoute",
|
||||
"auth.copyUrlManual": "Copia la URL de la barra de direcciones y pégala en la aplicación.",
|
||||
"auth.methodManualDescription":
|
||||
"Elimina la contraseña de la base de datos y configura una nueva al iniciar:",
|
||||
"auth.setPasswordInYour": "Define una nueva contraseña en tu",
|
||||
"auth.restartServerWithNewPassword": "Reinicia el servidor; usará la nueva contraseña",
|
||||
},
|
||||
fr: {
|
||||
"sidebar.docs": "Documentation",
|
||||
"header.providerDescription": "Gérez vos connexions aux fournisseurs d'IA",
|
||||
"header.comboDescription": "Combinaisons de modèles avec bascule de secours",
|
||||
"header.analytics": "Analytique",
|
||||
"header.anthropicCompatible": "Compatible Anthropic",
|
||||
"home.quickStartDesc":
|
||||
"Démarrez en 4 étapes. Connectez des fournisseurs, routez les modèles et surveillez tout.",
|
||||
"home.fullDocs": "Documentation complète",
|
||||
"home.step1Desc":
|
||||
"Allez dans <endpoint>Endpoint</endpoint> -> Clés enregistrées. Générez une clé par environnement.",
|
||||
"home.step2Title": "2. Connecter des fournisseurs",
|
||||
"home.step2Desc":
|
||||
"Ajoutez des comptes dans <providers>Providers</providers>. Prend en charge OAuth, API Key et paliers gratuits.",
|
||||
"home.step3Title": "3. Configurer votre client",
|
||||
"home.step3Desc": "Définissez l'URL de base sur {url} dans votre IDE ou client API.",
|
||||
"home.step4Desc":
|
||||
"Suivez les tokens, les coûts et les erreurs dans <logs>Journaux des requêtes</logs> et <analytics>Analytique</analytics>.",
|
||||
"home.requestsShort": "{count} reqs",
|
||||
"auth.runOnboardingWizard":
|
||||
"Exécutez l'assistant d'onboarding pour configurer votre mot de passe et connecter votre premier fournisseur d'IA.",
|
||||
"auth.copyUrlManual": "Copiez l'URL depuis la barre d'adresse et collez-la dans l'application.",
|
||||
"auth.newPasswordPlaceholder": "votre_nouveau_mot_de_passe",
|
||||
},
|
||||
de: {
|
||||
"sidebar.dashboard": "Dashboard",
|
||||
"header.analytics": "Analysen",
|
||||
"header.anthropicCompatible": "Anthropic-kompatibel",
|
||||
"home.step1Desc":
|
||||
"Gehen Sie zu <endpoint>Endpoint</endpoint> -> Registrierte Schlüssel. Erstellen Sie einen Schlüssel pro Umgebung.",
|
||||
"home.step2Desc":
|
||||
"Fügen Sie Konten unter <providers>Providers</providers> hinzu. Unterstützt OAuth, API Key und kostenlose Stufen.",
|
||||
"home.step3Title": "3. Client konfigurieren",
|
||||
"home.step4Desc":
|
||||
"Verfolgen Sie Tokens, Kosten und Fehler in <logs>Anfrageprotokollen</logs> und <analytics>Analysen</analytics>.",
|
||||
"home.requestsShort": "{count} Anfr.",
|
||||
"auth.unifiedProxy": "Einheitlicher KI-API-Proxy",
|
||||
"auth.unifiedAiApiProxy": "Einheitlicher KI-API-Proxy",
|
||||
"auth.setPasswordInYour": "Legen Sie ein neues Passwort in Ihrer",
|
||||
"auth.newPasswordPlaceholder": "ihr_neues_passwort",
|
||||
"auth.copyUrlManual":
|
||||
"Kopieren Sie die URL aus der Adressleiste und fügen Sie sie in die Anwendung ein.",
|
||||
},
|
||||
ja: {
|
||||
"common.disabled": "無効",
|
||||
"common.columns": "列",
|
||||
"common.newest": "新しい順",
|
||||
"common.oldest": "古い順",
|
||||
"header.providerDescription": "AI プロバイダー接続を管理します",
|
||||
"header.comboDescription": "フォールバック付きのモデルコンボ",
|
||||
"header.analytics": "アナリティクス",
|
||||
"header.settingsDescription": "設定を管理します",
|
||||
"header.anthropicCompatible": "Anthropic 互換",
|
||||
"home.quickStartDesc":
|
||||
"4 ステップでセットアップ完了。プロバイダー接続、モデルルーティング、監視まで一括で行えます。",
|
||||
"home.fullDocs": "完全版ドキュメント",
|
||||
"home.step1Desc":
|
||||
"<endpoint>Endpoint</endpoint> -> Registered Keys に移動し、環境ごとに 1 つのキーを作成します。",
|
||||
"home.step2Title": "2. プロバイダーを接続",
|
||||
"home.step2Desc":
|
||||
"<providers>Providers</providers> でアカウントを追加します。OAuth、API Key、無料枠に対応しています。",
|
||||
"home.step3Title": "3. クライアントを設定",
|
||||
"home.step3Desc": "IDE または API クライアントの Base URL を {url} に設定します。",
|
||||
"home.step4Desc":
|
||||
"<logs>リクエストログ</logs> と <analytics>アナリティクス</analytics> でトークン・コスト・エラーを追跡します。",
|
||||
"home.requestsShort": "{count} 件",
|
||||
"auth.runOnboardingWizard":
|
||||
"オンボーディングウィザードを実行して、パスワード設定と最初の AI プロバイダー接続を行います。",
|
||||
"auth.stopServer": "OmniRoute サーバーを停止",
|
||||
"auth.copyUrlManual": "アドレスバーの URL をコピーしてアプリケーションに貼り付けてください。",
|
||||
"auth.methodManualDescription":
|
||||
"データベースからパスワードを削除し、起動時に新しいパスワードを設定します:",
|
||||
"auth.setPasswordInYour": "次のファイルで新しいパスワードを設定してください",
|
||||
"auth.newPasswordPlaceholder": "あなたの新しいパスワード",
|
||||
"auth.restartServerWithNewPassword": "サーバーを再起動すると新しいパスワードが適用されます",
|
||||
},
|
||||
ar: {
|
||||
"common.disabled": "غير مفعّل",
|
||||
"sidebar.providers": "المزوّدون",
|
||||
"sidebar.apiManager": "مدير API",
|
||||
"header.providers": "المزوّدون",
|
||||
"header.providerDescription": "إدارة اتصالات مزوّدي الذكاء الاصطناعي",
|
||||
"header.comboDescription": "مجموعات نماذج مع التبديل الاحتياطي",
|
||||
"header.anthropicCompatible": "متوافق مع Anthropic",
|
||||
"home.quickStartDesc": "ابدأ خلال 4 خطوات: وصّل المزوّدين، فعّل توجيه النماذج، وراقب كل شيء.",
|
||||
"home.fullDocs": "التوثيق الكامل",
|
||||
"home.step1Desc":
|
||||
"انتقل إلى <endpoint>Endpoint</endpoint> -> Registered Keys. أنشئ مفتاحًا واحدًا لكل بيئة.",
|
||||
"home.step2Desc":
|
||||
"أضف الحسابات في <providers>Providers</providers>. يدعم OAuth وAPI Key والخطط المجانية.",
|
||||
"home.step3Title": "3. اضبط عميلك",
|
||||
"home.step3Desc": "عيّن عنوان Base URL إلى {url} في IDE أو عميل API لديك.",
|
||||
"home.step4Desc":
|
||||
"تتبّع الرموز والتكلفة والأخطاء في <logs>سجلات الطلبات</logs> و<analytics>التحليلات</analytics>.",
|
||||
"home.requestsShort": "{count} طلب",
|
||||
"auth.unifiedProxy": "وكيل API موحّد للذكاء الاصطناعي",
|
||||
"auth.unifiedAiApiProxy": "وكيل API موحّد للذكاء الاصطناعي",
|
||||
"auth.copyUrlManual": "انسخ عنوان URL من شريط العنوان والصقه داخل التطبيق.",
|
||||
"auth.setPasswordInYour": "عيّن كلمة مرور جديدة في ملف",
|
||||
"auth.newPasswordPlaceholder": "كلمة_مرور_جديدة",
|
||||
"auth.restartServerWithNewPassword": "أعد تشغيل الخادم وسيتم استخدام كلمة المرور الجديدة",
|
||||
},
|
||||
};
|
||||
|
||||
const README_PREFIX_OVERRIDES = {
|
||||
"README.es.md": "🌐 **Disponible en:**",
|
||||
"README.fr.md": "🌐 **Disponible en :**",
|
||||
"README.de.md": "🌐 **Verfügbar in:**",
|
||||
"README.ja.md": "🌐 **対応言語:**",
|
||||
"README.ar.md": "🌐 **متوفر باللغات:**",
|
||||
};
|
||||
|
||||
const README_NAV_OVERRIDES = {
|
||||
"README.ja.md":
|
||||
"[🌐 ウェブサイト](https://omniroute.online) • [🚀 クイックスタート](#-クイックスタート) • [💡 主な機能](#-主な機能) • [📖 ドキュメント](#-ドキュメント) • [💰 料金](#-価格の概要) • [💬 WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)",
|
||||
"README.ar.md":
|
||||
"[🌐 الموقع](https://omniroute.online) • [🚀 البداية السريعة](#-بداية-سريعة) • [💡 الميزات](#-الميزات-الرئيسية) • [📖 التوثيق](#-التوثيق) • [💰 الأسعار](#-لمحة-سريعة-عن-الأسعار) • [💬 WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)",
|
||||
};
|
||||
|
||||
function setByPath(target, pathStr, value) {
|
||||
const tokens = pathStr.split(".");
|
||||
let current = target;
|
||||
for (let i = 0; i < tokens.length - 1; i += 1) {
|
||||
current = current[tokens[i]];
|
||||
}
|
||||
current[tokens[tokens.length - 1]] = value;
|
||||
}
|
||||
|
||||
async function applyMessageOverrides() {
|
||||
for (const [locale, overrides] of Object.entries(MESSAGE_OVERRIDES)) {
|
||||
const file = path.join(MESSAGES_DIR, `${locale}.json`);
|
||||
const data = JSON.parse(await fs.readFile(file, "utf8"));
|
||||
|
||||
for (const [key, value] of Object.entries(overrides)) {
|
||||
setByPath(data, key, value);
|
||||
}
|
||||
|
||||
await fs.writeFile(file, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
function replaceLineByPrefix(content, prefix, replacement) {
|
||||
const lines = content.split("\n");
|
||||
const idx = lines.findIndex((line) => line.startsWith(prefix));
|
||||
if (idx >= 0) {
|
||||
lines[idx] = replacement;
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function removeEnglishPortugueseAnchorLine(content) {
|
||||
const lines = content.split("\n");
|
||||
const filtered = lines.filter(
|
||||
(line) =>
|
||||
!line.includes(
|
||||
"**[English](#-omniroute--the-free-ai-gateway)** | **[Português (BR)](#-omniroute--gateway-de-ia-gratuito)**"
|
||||
)
|
||||
);
|
||||
return filtered.join("\n");
|
||||
}
|
||||
|
||||
function removeBrazilianAppendixSection(content) {
|
||||
const marker = "\n## 🇧🇷 OmniRoute";
|
||||
const idx = content.indexOf(marker);
|
||||
if (idx === -1) {
|
||||
return content;
|
||||
}
|
||||
return content.slice(0, idx).trimEnd() + "\n";
|
||||
}
|
||||
|
||||
async function applyReadmeOverrides() {
|
||||
for (const [fileName, localizedPrefix] of Object.entries(README_PREFIX_OVERRIDES)) {
|
||||
const filePath = path.join(ROOT, fileName);
|
||||
let content = await fs.readFile(filePath, "utf8");
|
||||
|
||||
content = content.replace("🌐 **Available in:**", localizedPrefix);
|
||||
|
||||
if (README_NAV_OVERRIDES[fileName]) {
|
||||
content = replaceLineByPrefix(content, "[🌐 Website]", README_NAV_OVERRIDES[fileName]);
|
||||
content = removeEnglishPortugueseAnchorLine(content);
|
||||
content = removeBrazilianAppendixSection(content);
|
||||
}
|
||||
|
||||
await fs.writeFile(filePath, content, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await applyMessageOverrides();
|
||||
await applyReadmeOverrides();
|
||||
console.log("priority overrides applied");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
848
scripts/i18n/generate-multilang.mjs
Normal file
848
scripts/i18n/generate-multilang.mjs
Normal file
@@ -0,0 +1,848 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const MESSAGES_DIR = path.join(ROOT, "src", "i18n", "messages");
|
||||
const DOCS_DIR = path.join(ROOT, "docs");
|
||||
const DOCS_I18N_DIR = path.join(DOCS_DIR, "i18n");
|
||||
|
||||
const DOC_SOURCE_FILES = [
|
||||
"API_REFERENCE.md",
|
||||
"ARCHITECTURE.md",
|
||||
"CODEBASE_DOCUMENTATION.md",
|
||||
"FEATURES.md",
|
||||
"TROUBLESHOOTING.md",
|
||||
"USER_GUIDE.md",
|
||||
];
|
||||
|
||||
const LOCALE_SPECS = [
|
||||
{
|
||||
code: "en",
|
||||
googleTl: "en",
|
||||
label: "EN",
|
||||
flag: "🇺🇸",
|
||||
languageName: "English",
|
||||
readmeName: "English",
|
||||
docsName: "English",
|
||||
},
|
||||
{
|
||||
code: "pt-BR",
|
||||
googleTl: "pt",
|
||||
label: "PT-BR",
|
||||
flag: "🇧🇷",
|
||||
languageName: "Português (Brasil)",
|
||||
readmeName: "Português (Brasil)",
|
||||
docsName: "Português (Brasil)",
|
||||
},
|
||||
{
|
||||
code: "es",
|
||||
googleTl: "es",
|
||||
label: "ES",
|
||||
flag: "🇪🇸",
|
||||
languageName: "Español",
|
||||
readmeName: "Español",
|
||||
docsName: "Español",
|
||||
},
|
||||
{
|
||||
code: "fr",
|
||||
googleTl: "fr",
|
||||
label: "FR",
|
||||
flag: "🇫🇷",
|
||||
languageName: "Français",
|
||||
readmeName: "Français",
|
||||
docsName: "Français",
|
||||
},
|
||||
{
|
||||
code: "it",
|
||||
googleTl: "it",
|
||||
label: "IT",
|
||||
flag: "🇮🇹",
|
||||
languageName: "Italiano",
|
||||
readmeName: "Italiano",
|
||||
docsName: "Italiano",
|
||||
},
|
||||
{
|
||||
code: "ru",
|
||||
googleTl: "ru",
|
||||
label: "RU",
|
||||
flag: "🇷🇺",
|
||||
languageName: "Русский",
|
||||
readmeName: "Русский",
|
||||
docsName: "Русский",
|
||||
},
|
||||
{
|
||||
code: "zh-CN",
|
||||
googleTl: "zh-CN",
|
||||
label: "ZH-CN",
|
||||
flag: "🇨🇳",
|
||||
languageName: "中文 (简体)",
|
||||
readmeName: "中文 (简体)",
|
||||
docsName: "中文 (简体)",
|
||||
},
|
||||
{
|
||||
code: "de",
|
||||
googleTl: "de",
|
||||
label: "DE",
|
||||
flag: "🇩🇪",
|
||||
languageName: "Deutsch",
|
||||
readmeName: "Deutsch",
|
||||
docsName: "Deutsch",
|
||||
},
|
||||
{
|
||||
code: "in",
|
||||
googleTl: "hi",
|
||||
label: "IN",
|
||||
flag: "🇮🇳",
|
||||
languageName: "Hindi (India)",
|
||||
readmeName: "हिन्दी",
|
||||
docsName: "हिन्दी",
|
||||
},
|
||||
{
|
||||
code: "th",
|
||||
googleTl: "th",
|
||||
label: "TH",
|
||||
flag: "🇹🇭",
|
||||
languageName: "ไทย",
|
||||
readmeName: "ไทย",
|
||||
docsName: "ไทย",
|
||||
},
|
||||
{
|
||||
code: "uk-UA",
|
||||
googleTl: "uk",
|
||||
label: "UK-UA",
|
||||
flag: "🇺🇦",
|
||||
languageName: "Українська",
|
||||
readmeName: "Українська",
|
||||
docsName: "Українська",
|
||||
},
|
||||
{
|
||||
code: "ar",
|
||||
googleTl: "ar",
|
||||
label: "AR",
|
||||
flag: "🇸🇦",
|
||||
languageName: "العربية",
|
||||
readmeName: "العربية",
|
||||
docsName: "العربية",
|
||||
},
|
||||
{
|
||||
code: "ja",
|
||||
googleTl: "ja",
|
||||
label: "JA",
|
||||
flag: "🇯🇵",
|
||||
languageName: "日本語",
|
||||
readmeName: "日本語",
|
||||
docsName: "日本語",
|
||||
},
|
||||
{
|
||||
code: "vi",
|
||||
googleTl: "vi",
|
||||
label: "VI",
|
||||
flag: "🇻🇳",
|
||||
languageName: "Tiếng Việt",
|
||||
readmeName: "Tiếng Việt",
|
||||
docsName: "Tiếng Việt",
|
||||
},
|
||||
{
|
||||
code: "bg",
|
||||
googleTl: "bg",
|
||||
label: "BG",
|
||||
flag: "🇧🇬",
|
||||
languageName: "Български",
|
||||
readmeName: "Български",
|
||||
docsName: "Български",
|
||||
},
|
||||
{
|
||||
code: "da",
|
||||
googleTl: "da",
|
||||
label: "DA",
|
||||
flag: "🇩🇰",
|
||||
languageName: "Dansk",
|
||||
readmeName: "Dansk",
|
||||
docsName: "Dansk",
|
||||
},
|
||||
{
|
||||
code: "fi",
|
||||
googleTl: "fi",
|
||||
label: "FI",
|
||||
flag: "🇫🇮",
|
||||
languageName: "Suomi",
|
||||
readmeName: "Suomi",
|
||||
docsName: "Suomi",
|
||||
},
|
||||
{
|
||||
code: "he",
|
||||
googleTl: "iw",
|
||||
label: "HE",
|
||||
flag: "🇮🇱",
|
||||
languageName: "עברית",
|
||||
readmeName: "עברית",
|
||||
docsName: "עברית",
|
||||
},
|
||||
{
|
||||
code: "hu",
|
||||
googleTl: "hu",
|
||||
label: "HU",
|
||||
flag: "🇭🇺",
|
||||
languageName: "Magyar",
|
||||
readmeName: "Magyar",
|
||||
docsName: "Magyar",
|
||||
},
|
||||
{
|
||||
code: "id",
|
||||
googleTl: "id",
|
||||
label: "ID",
|
||||
flag: "🇮🇩",
|
||||
languageName: "Bahasa Indonesia",
|
||||
readmeName: "Bahasa Indonesia",
|
||||
docsName: "Bahasa Indonesia",
|
||||
},
|
||||
{
|
||||
code: "ko",
|
||||
googleTl: "ko",
|
||||
label: "KO",
|
||||
flag: "🇰🇷",
|
||||
languageName: "한국어",
|
||||
readmeName: "한국어",
|
||||
docsName: "한국어",
|
||||
},
|
||||
{
|
||||
code: "ms",
|
||||
googleTl: "ms",
|
||||
label: "MS",
|
||||
flag: "🇲🇾",
|
||||
languageName: "Bahasa Melayu",
|
||||
readmeName: "Bahasa Melayu",
|
||||
docsName: "Bahasa Melayu",
|
||||
},
|
||||
{
|
||||
code: "nl",
|
||||
googleTl: "nl",
|
||||
label: "NL",
|
||||
flag: "🇳🇱",
|
||||
languageName: "Nederlands",
|
||||
readmeName: "Nederlands",
|
||||
docsName: "Nederlands",
|
||||
},
|
||||
{
|
||||
code: "no",
|
||||
googleTl: "no",
|
||||
label: "NO",
|
||||
flag: "🇳🇴",
|
||||
languageName: "Norsk",
|
||||
readmeName: "Norsk",
|
||||
docsName: "Norsk",
|
||||
},
|
||||
{
|
||||
code: "pt",
|
||||
googleTl: "pt",
|
||||
label: "PT",
|
||||
flag: "🇵🇹",
|
||||
languageName: "Português (Portugal)",
|
||||
readmeName: "Português (Portugal)",
|
||||
docsName: "Português (Portugal)",
|
||||
},
|
||||
{
|
||||
code: "ro",
|
||||
googleTl: "ro",
|
||||
label: "RO",
|
||||
flag: "🇷🇴",
|
||||
languageName: "Română",
|
||||
readmeName: "Română",
|
||||
docsName: "Română",
|
||||
},
|
||||
{
|
||||
code: "pl",
|
||||
googleTl: "pl",
|
||||
label: "PL",
|
||||
flag: "🇵🇱",
|
||||
languageName: "Polski",
|
||||
readmeName: "Polski",
|
||||
docsName: "Polski",
|
||||
},
|
||||
{
|
||||
code: "sk",
|
||||
googleTl: "sk",
|
||||
label: "SK",
|
||||
flag: "🇸🇰",
|
||||
languageName: "Slovenčina",
|
||||
readmeName: "Slovenčina",
|
||||
docsName: "Slovenčina",
|
||||
},
|
||||
{
|
||||
code: "sv",
|
||||
googleTl: "sv",
|
||||
label: "SV",
|
||||
flag: "🇸🇪",
|
||||
languageName: "Svenska",
|
||||
readmeName: "Svenska",
|
||||
docsName: "Svenska",
|
||||
},
|
||||
{
|
||||
code: "phi",
|
||||
googleTl: "tl",
|
||||
label: "PHI",
|
||||
flag: "🇵🇭",
|
||||
languageName: "Filipino",
|
||||
readmeName: "Filipino",
|
||||
docsName: "Filipino",
|
||||
},
|
||||
];
|
||||
|
||||
const EXISTING_README_CODES = new Set(["pt-BR", "es", "fr", "it", "ru", "zh-CN", "de"]);
|
||||
const RTL_LOCALES = new Set(["ar", "he"]);
|
||||
|
||||
const URL_MAX_TEXT_LENGTH = 1800;
|
||||
const DELIMITER = "\n__OMNIROUTE_I18N_SEPARATOR__\n";
|
||||
const DELIMITER_REGEX = /\n\s*__OMNIROUTE_I18N_SEPARATOR__\s*\n/g;
|
||||
const TRANSLATION_CACHE = new Map();
|
||||
const REQUEST_TIMEOUT_MS = 20000;
|
||||
|
||||
function getReadmeFileName(code) {
|
||||
return code === "en" ? "README.md" : `README.${code}.md`;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function isProbablyTranslatable(text) {
|
||||
if (!text.trim()) {
|
||||
return false;
|
||||
}
|
||||
return /[A-Za-z]/.test(text);
|
||||
}
|
||||
|
||||
function maskBalancedCurlyBraces(input, stash) {
|
||||
let result = "";
|
||||
let i = 0;
|
||||
|
||||
while (i < input.length) {
|
||||
if (input[i] === "{") {
|
||||
let j = i;
|
||||
let depth = 0;
|
||||
|
||||
while (j < input.length) {
|
||||
const ch = input[j];
|
||||
if (ch === "{") {
|
||||
depth += 1;
|
||||
} else if (ch === "}") {
|
||||
depth -= 1;
|
||||
if (depth === 0) {
|
||||
j += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if (depth === 0 && j > i + 1) {
|
||||
result += stash(input.slice(i, j));
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result += input[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function protectText(input, options = {}) {
|
||||
const tokens = [];
|
||||
const stash = (value) => {
|
||||
const token = `__OMNI_TOKEN_${tokens.length}__`;
|
||||
tokens.push(value);
|
||||
return token;
|
||||
};
|
||||
|
||||
let output = input;
|
||||
|
||||
if (options.markdown) {
|
||||
output = output.replace(/```[\s\S]*?```/g, stash);
|
||||
output = output.replace(/<table[\s\S]*?<\/table>/gi, stash);
|
||||
output = output.replace(/`[^`\n]+`/g, stash);
|
||||
output = output.replace(/!?\[[^\]]*\]\([^\)]+\)/g, stash);
|
||||
output = output.replace(/<img[^>]*>/gi, stash);
|
||||
}
|
||||
|
||||
output = output.replace(/<\/?[a-zA-Z][^>]*>/g, stash);
|
||||
output = maskBalancedCurlyBraces(output, stash);
|
||||
|
||||
return { output, tokens };
|
||||
}
|
||||
|
||||
function restoreText(input, tokens) {
|
||||
let output = input;
|
||||
for (let i = 0; i < tokens.length; i += 1) {
|
||||
output = output.replaceAll(`__OMNI_TOKEN_${i}__`, tokens[i]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function parseTranslationPayload(payload) {
|
||||
if (!Array.isArray(payload) || !Array.isArray(payload[0])) {
|
||||
throw new Error("Invalid translation payload format");
|
||||
}
|
||||
|
||||
return payload[0].map((item) => item[0] || "").join("");
|
||||
}
|
||||
|
||||
async function translateTextRaw(text, targetLanguage, sourceLanguage = "en", attempt = 1) {
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
client: "gtx",
|
||||
sl: sourceLanguage,
|
||||
tl: targetLanguage,
|
||||
dt: "t",
|
||||
q: text,
|
||||
});
|
||||
|
||||
const url = `https://translate.googleapis.com/translate_a/single?${params.toString()}`;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 OmniRoute-I18N",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
clearTimeout(timeout);
|
||||
if (attempt < 5) {
|
||||
await sleep(300 * attempt);
|
||||
return translateTextRaw(text, targetLanguage, sourceLanguage, attempt + 1);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if ((response.status === 429 || response.status >= 500) && attempt < 5) {
|
||||
await sleep(300 * attempt);
|
||||
return translateTextRaw(text, targetLanguage, sourceLanguage, attempt + 1);
|
||||
}
|
||||
|
||||
const body = await response.text();
|
||||
throw new Error(`Translation request failed (${response.status}): ${body.slice(0, 200)}`);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
return parseTranslationPayload(payload);
|
||||
}
|
||||
|
||||
function getLocaleCache(targetLanguage) {
|
||||
if (!TRANSLATION_CACHE.has(targetLanguage)) {
|
||||
TRANSLATION_CACHE.set(targetLanguage, new Map());
|
||||
}
|
||||
return TRANSLATION_CACHE.get(targetLanguage);
|
||||
}
|
||||
|
||||
async function translateProtectedUnits(units, targetLanguage) {
|
||||
const translated = new Array(units.length);
|
||||
const cache = getLocaleCache(targetLanguage);
|
||||
|
||||
const pendingIndices = [];
|
||||
const uniqueTexts = [];
|
||||
const uniqueIndexMap = new Map();
|
||||
|
||||
for (let i = 0; i < units.length; i += 1) {
|
||||
const text = units[i];
|
||||
if (cache.has(text)) {
|
||||
translated[i] = cache.get(text);
|
||||
continue;
|
||||
}
|
||||
|
||||
pendingIndices.push(i);
|
||||
if (!uniqueIndexMap.has(text)) {
|
||||
uniqueIndexMap.set(text, uniqueTexts.length);
|
||||
uniqueTexts.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
if (uniqueTexts.length > 0) {
|
||||
let chunk = [];
|
||||
let chunkLen = 0;
|
||||
const translatedUnique = new Array(uniqueTexts.length);
|
||||
|
||||
const flushChunk = async () => {
|
||||
if (chunk.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const joined = chunk.join(DELIMITER);
|
||||
let translatedJoined;
|
||||
|
||||
try {
|
||||
translatedJoined = await translateTextRaw(joined, targetLanguage);
|
||||
} catch (error) {
|
||||
translatedJoined = null;
|
||||
}
|
||||
|
||||
if (translatedJoined) {
|
||||
const split = translatedJoined.split(DELIMITER_REGEX);
|
||||
if (split.length === chunk.length) {
|
||||
for (let i = 0; i < chunk.length; i += 1) {
|
||||
const originalText = chunk[i];
|
||||
const translatedText = split[i];
|
||||
const uniqueIdx = uniqueIndexMap.get(originalText);
|
||||
translatedUnique[uniqueIdx] = translatedText;
|
||||
cache.set(originalText, translatedText);
|
||||
}
|
||||
chunk = [];
|
||||
chunkLen = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const originalText of chunk) {
|
||||
const translatedText = await translateTextRaw(originalText, targetLanguage);
|
||||
const uniqueIdx = uniqueIndexMap.get(originalText);
|
||||
translatedUnique[uniqueIdx] = translatedText;
|
||||
cache.set(originalText, translatedText);
|
||||
}
|
||||
|
||||
chunk = [];
|
||||
chunkLen = 0;
|
||||
};
|
||||
|
||||
for (const text of uniqueTexts) {
|
||||
const projected = chunkLen + text.length + DELIMITER.length;
|
||||
if (projected > URL_MAX_TEXT_LENGTH && chunk.length > 0) {
|
||||
await flushChunk();
|
||||
}
|
||||
|
||||
chunk.push(text);
|
||||
chunkLen += text.length + DELIMITER.length;
|
||||
|
||||
if (chunkLen > URL_MAX_TEXT_LENGTH) {
|
||||
await flushChunk();
|
||||
}
|
||||
}
|
||||
|
||||
await flushChunk();
|
||||
|
||||
for (const index of pendingIndices) {
|
||||
const text = units[index];
|
||||
const uniqueIdx = uniqueIndexMap.get(text);
|
||||
translated[index] = translatedUnique[uniqueIdx] || text;
|
||||
}
|
||||
}
|
||||
|
||||
return translated;
|
||||
}
|
||||
|
||||
async function translateStrings(values, targetLanguage, options = {}) {
|
||||
if (targetLanguage === "en") {
|
||||
return values.slice();
|
||||
}
|
||||
|
||||
const protectedValues = values.map((value) => protectText(value, options));
|
||||
const maskedUnits = protectedValues.map((item) => item.output);
|
||||
|
||||
const needsTranslation = maskedUnits.map((unit) => isProbablyTranslatable(unit));
|
||||
const onlyForTranslation = [];
|
||||
const mapping = [];
|
||||
|
||||
for (let i = 0; i < maskedUnits.length; i += 1) {
|
||||
if (!needsTranslation[i]) {
|
||||
continue;
|
||||
}
|
||||
mapping.push(i);
|
||||
onlyForTranslation.push(maskedUnits[i]);
|
||||
}
|
||||
|
||||
const translatedUnits = await translateProtectedUnits(onlyForTranslation, targetLanguage);
|
||||
|
||||
const finalMasked = maskedUnits.slice();
|
||||
for (let i = 0; i < mapping.length; i += 1) {
|
||||
finalMasked[mapping[i]] = translatedUnits[i];
|
||||
}
|
||||
|
||||
return finalMasked.map((value, index) => restoreText(value, protectedValues[index].tokens));
|
||||
}
|
||||
|
||||
function collectStringLeaves(node, pathSoFar = [], output = []) {
|
||||
if (typeof node === "string") {
|
||||
output.push({ path: pathSoFar, value: node });
|
||||
return output;
|
||||
}
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
node.forEach((item, index) => {
|
||||
collectStringLeaves(item, [...pathSoFar, index], output);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
if (node && typeof node === "object") {
|
||||
for (const key of Object.keys(node)) {
|
||||
collectStringLeaves(node[key], [...pathSoFar, key], output);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function setByPath(target, pathTokens, value) {
|
||||
let current = target;
|
||||
for (let i = 0; i < pathTokens.length - 1; i += 1) {
|
||||
current = current[pathTokens[i]];
|
||||
}
|
||||
current[pathTokens[pathTokens.length - 1]] = value;
|
||||
}
|
||||
|
||||
async function ensureDir(dirPath) {
|
||||
await fs.mkdir(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
async function fileExists(filePath) {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function buildRootReadmeLanguageBar() {
|
||||
const entries = LOCALE_SPECS.map((spec) => {
|
||||
const file = getReadmeFileName(spec.code);
|
||||
return `${spec.flag} [${spec.readmeName}](${file})`;
|
||||
});
|
||||
return `🌐 **Available in:** ${entries.join(" | ")}`;
|
||||
}
|
||||
|
||||
function upsertRootReadmeLanguageBar(content, languageBar) {
|
||||
const existingBarRegex = /^🌐 \*\*.*README.*$/m;
|
||||
if (existingBarRegex.test(content)) {
|
||||
return content.replace(existingBarRegex, languageBar);
|
||||
}
|
||||
|
||||
const navLineRegex = /^\[🌐 .*$/m;
|
||||
const navLine = content.match(navLineRegex);
|
||||
if (navLine && typeof navLine.index === "number") {
|
||||
const insertAfter = navLine.index + navLine[0].length;
|
||||
return `${content.slice(0, insertAfter)}\n\n${languageBar}${content.slice(insertAfter)}`;
|
||||
}
|
||||
|
||||
return `${languageBar}\n\n${content}`;
|
||||
}
|
||||
|
||||
function buildDocsLanguageBar(docName, currentLocaleCode) {
|
||||
const entries = LOCALE_SPECS.map((spec) => {
|
||||
let targetPath;
|
||||
|
||||
if (spec.code === "en") {
|
||||
targetPath = currentLocaleCode ? `../../${docName}` : docName;
|
||||
} else if (currentLocaleCode) {
|
||||
targetPath = `../${spec.code}/${docName}`;
|
||||
} else {
|
||||
targetPath = `i18n/${spec.code}/${docName}`;
|
||||
}
|
||||
|
||||
return `${spec.flag} [${spec.docsName}](${targetPath})`;
|
||||
});
|
||||
|
||||
return `🌐 **Languages:** ${entries.join(" | ")}`;
|
||||
}
|
||||
|
||||
function upsertDocsLanguageBar(content, languageBar) {
|
||||
const existingBarRegex = /^🌐 \*\*Languages:\*\*.*$/m;
|
||||
if (existingBarRegex.test(content)) {
|
||||
return content.replace(existingBarRegex, languageBar);
|
||||
}
|
||||
|
||||
const firstHeadingRegex = /^(# .+\n?)/;
|
||||
if (firstHeadingRegex.test(content)) {
|
||||
return content.replace(firstHeadingRegex, `$1\n${languageBar}\n`);
|
||||
}
|
||||
|
||||
return `${languageBar}\n\n${content}`;
|
||||
}
|
||||
|
||||
function splitByParagraphs(markdown) {
|
||||
const parts = markdown.split(/(\n{2,})/g);
|
||||
return parts;
|
||||
}
|
||||
|
||||
async function translateMarkdownDocument(content, targetLanguage) {
|
||||
if (targetLanguage === "en") {
|
||||
return content;
|
||||
}
|
||||
|
||||
const protectedDoc = protectText(content, { markdown: true });
|
||||
const parts = splitByParagraphs(protectedDoc.output);
|
||||
|
||||
const translatableIndices = [];
|
||||
const translatableValues = [];
|
||||
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
if (i % 2 === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const part = parts[i];
|
||||
if (!isProbablyTranslatable(part)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
translatableIndices.push(i);
|
||||
translatableValues.push(part);
|
||||
}
|
||||
|
||||
const translated = await translateStrings(translatableValues, targetLanguage, { markdown: true });
|
||||
|
||||
for (let i = 0; i < translatableIndices.length; i += 1) {
|
||||
parts[translatableIndices[i]] = translated[i];
|
||||
}
|
||||
|
||||
const joined = parts.join("");
|
||||
return restoreText(joined, protectedDoc.tokens);
|
||||
}
|
||||
|
||||
async function generateMessageTranslations() {
|
||||
const enPath = path.join(MESSAGES_DIR, "en.json");
|
||||
const sourceRaw = await fs.readFile(enPath, "utf8");
|
||||
const sourceJson = JSON.parse(sourceRaw);
|
||||
|
||||
const leaves = collectStringLeaves(sourceJson);
|
||||
const sourceValues = leaves.map((entry) => entry.value);
|
||||
|
||||
for (const spec of LOCALE_SPECS) {
|
||||
if (spec.code === "en" || spec.code === "pt-BR") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetPath = path.join(MESSAGES_DIR, `${spec.code}.json`);
|
||||
if (await fileExists(targetPath)) {
|
||||
console.log(`[messages] Skipping ${spec.code} (already exists).`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[messages] Translating ${spec.code}...`);
|
||||
const translatedValues = await translateStrings(sourceValues, spec.googleTl);
|
||||
|
||||
const targetJson = structuredClone(sourceJson);
|
||||
translatedValues.forEach((value, index) => {
|
||||
setByPath(targetJson, leaves[index].path, value);
|
||||
});
|
||||
|
||||
await fs.writeFile(targetPath, `${JSON.stringify(targetJson, null, 2)}\n`, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
async function generateReadmeTranslations() {
|
||||
const sourceReadmePath = path.join(ROOT, "README.md");
|
||||
const sourceReadme = await fs.readFile(sourceReadmePath, "utf8");
|
||||
|
||||
for (const spec of LOCALE_SPECS) {
|
||||
if (spec.code === "en") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetFile = path.join(ROOT, getReadmeFileName(spec.code));
|
||||
|
||||
if (EXISTING_README_CODES.has(spec.code) || (await fileExists(targetFile))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[readme] Translating ${spec.code}...`);
|
||||
const translated = await translateMarkdownDocument(sourceReadme, spec.googleTl);
|
||||
await fs.writeFile(targetFile, translated, "utf8");
|
||||
}
|
||||
|
||||
const languageBar = buildRootReadmeLanguageBar();
|
||||
for (const spec of LOCALE_SPECS) {
|
||||
const readmePath = path.join(ROOT, getReadmeFileName(spec.code));
|
||||
const current = await fs.readFile(readmePath, "utf8");
|
||||
const updated = upsertRootReadmeLanguageBar(current, languageBar);
|
||||
await fs.writeFile(readmePath, updated, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
async function generateDocsTranslations() {
|
||||
await ensureDir(DOCS_I18N_DIR);
|
||||
|
||||
for (const docName of DOC_SOURCE_FILES) {
|
||||
const sourceDocPath = path.join(DOCS_DIR, docName);
|
||||
const sourceDocRaw = await fs.readFile(sourceDocPath, "utf8");
|
||||
|
||||
const sourceDocBar = buildDocsLanguageBar(docName, null);
|
||||
const sourceDocWithBar = upsertDocsLanguageBar(sourceDocRaw, sourceDocBar);
|
||||
await fs.writeFile(sourceDocPath, sourceDocWithBar, "utf8");
|
||||
|
||||
for (const spec of LOCALE_SPECS) {
|
||||
if (spec.code === "en") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetDir = path.join(DOCS_I18N_DIR, spec.code);
|
||||
await ensureDir(targetDir);
|
||||
const targetPath = path.join(targetDir, docName);
|
||||
|
||||
if (await fileExists(targetPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[docs] Translating ${docName} -> ${spec.code}...`);
|
||||
const translated = await translateMarkdownDocument(sourceDocRaw, spec.googleTl);
|
||||
const withBar = upsertDocsLanguageBar(translated, buildDocsLanguageBar(docName, spec.code));
|
||||
await fs.writeFile(targetPath, withBar, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
const indexLines = [
|
||||
"# Multilingual Documentation",
|
||||
"",
|
||||
"This directory contains machine-assisted translations based on the English docs.",
|
||||
"",
|
||||
...DOC_SOURCE_FILES.map((docName) => {
|
||||
const links = LOCALE_SPECS.map((spec) => {
|
||||
const link = spec.code === "en" ? `../${docName}` : `./${spec.code}/${docName}`;
|
||||
return `${spec.flag} [${spec.docsName}](${link})`;
|
||||
}).join(" | ");
|
||||
|
||||
return `- **${docName}**: ${links}`;
|
||||
}),
|
||||
"",
|
||||
`Generated on ${new Date().toISOString().slice(0, 10)}.`,
|
||||
];
|
||||
|
||||
await fs.writeFile(path.join(DOCS_I18N_DIR, "README.md"), `${indexLines.join("\n")}\n`, "utf8");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const runAll = args.size === 0 || args.has("all");
|
||||
|
||||
if (runAll || args.has("messages")) {
|
||||
await generateMessageTranslations();
|
||||
}
|
||||
|
||||
if (runAll || args.has("readme")) {
|
||||
await generateReadmeTranslations();
|
||||
}
|
||||
|
||||
if (runAll || args.has("docs")) {
|
||||
await generateDocsTranslations();
|
||||
}
|
||||
|
||||
console.log("Done.");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
338
scripts/i18n/generate-qa-checklist.mjs
Normal file
338
scripts/i18n/generate-qa-checklist.mjs
Normal file
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const APP_DIR = path.join(ROOT, "src", "app");
|
||||
const MESSAGES_DIR = path.join(ROOT, "src", "i18n", "messages");
|
||||
const REPORTS_DIR = path.join(ROOT, "docs", "reports");
|
||||
|
||||
const PRIORITY_LOCALES = ["es", "fr", "de", "ja", "ar"];
|
||||
|
||||
async function walk(dir) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
const out = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
out.push(...(await walk(full)));
|
||||
continue;
|
||||
}
|
||||
out.push(full);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function routeFromPage(pageFile) {
|
||||
const rel = path.relative(APP_DIR, pageFile).replace(/\\/g, "/");
|
||||
const noPage = rel.replace(/(^|\/)page\.tsx$/, "");
|
||||
if (!noPage) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
const segments = noPage
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.filter((segment) => !/^\(.+\)$/.test(segment));
|
||||
|
||||
return `/${segments.join("/")}`;
|
||||
}
|
||||
|
||||
function classifyPriority(route) {
|
||||
const highPatterns = [
|
||||
"/dashboard/usage",
|
||||
"/dashboard/providers",
|
||||
"/dashboard/settings",
|
||||
"/dashboard/endpoint",
|
||||
"/dashboard/logs",
|
||||
"/dashboard/audit-log",
|
||||
"/dashboard/health",
|
||||
"/dashboard/api-manager",
|
||||
"/dashboard/cli-tools",
|
||||
"/dashboard/combos",
|
||||
"/dashboard/translator",
|
||||
"/dashboard/analytics",
|
||||
"/dashboard/costs",
|
||||
"/dashboard/limits",
|
||||
];
|
||||
|
||||
if (highPatterns.some((pattern) => route.startsWith(pattern))) {
|
||||
return "Alta";
|
||||
}
|
||||
|
||||
if (route === "/") {
|
||||
return "Baixa";
|
||||
}
|
||||
|
||||
return "Media";
|
||||
}
|
||||
|
||||
function countRegex(content, regex) {
|
||||
const matches = content.match(regex);
|
||||
return matches ? matches.length : 0;
|
||||
}
|
||||
|
||||
async function collectRouteRiskMetrics(pageFile) {
|
||||
const routeDir = path.dirname(pageFile);
|
||||
if (routeDir === APP_DIR) {
|
||||
const raw = await fs.readFile(pageFile, "utf8");
|
||||
return {
|
||||
files: 1,
|
||||
fixedWidth: countRegex(raw, /\bw-(?:\d+|\[[^\]]+\]|\d+\/\d+)\b/g),
|
||||
directional: countRegex(
|
||||
raw,
|
||||
/\b(?:left|right|ml|mr|pl|pr)-[\w\[\]-]+\b|\btext-(?:left|right)\b/g
|
||||
),
|
||||
clipping: countRegex(raw, /\btruncate\b|\bline-clamp-\d+\b|\boverflow-hidden\b/g),
|
||||
};
|
||||
}
|
||||
|
||||
const files = await (async function walkRouteFiles(dir) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
const acc = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (full !== routeDir) {
|
||||
const nestedEntries = await fs.readdir(full);
|
||||
if (nestedEntries.includes("page.tsx")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
acc.push(...(await walkRouteFiles(full)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/\.(tsx|ts)$/.test(entry.name)) {
|
||||
acc.push(full);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
})(routeDir);
|
||||
|
||||
let fixedWidth = 0;
|
||||
let directional = 0;
|
||||
let clipping = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const raw = await fs.readFile(file, "utf8");
|
||||
fixedWidth += countRegex(raw, /\bw-(?:\d+|\[[^\]]+\]|\d+\/\d+)\b/g);
|
||||
directional += countRegex(
|
||||
raw,
|
||||
/\b(?:left|right|ml|mr|pl|pr)-[\w\[\]-]+\b|\btext-(?:left|right)\b/g
|
||||
);
|
||||
clipping += countRegex(raw, /\btruncate\b|\bline-clamp-\d+\b|\boverflow-hidden\b/g);
|
||||
}
|
||||
|
||||
return {
|
||||
files: files.length,
|
||||
fixedWidth,
|
||||
directional,
|
||||
clipping,
|
||||
};
|
||||
}
|
||||
|
||||
function getByPath(target, keyPath) {
|
||||
return keyPath.split(".").reduce((acc, token) => (acc ? acc[token] : undefined), target);
|
||||
}
|
||||
|
||||
function collectStringKeys(node, prefix = "", out = []) {
|
||||
if (typeof node === "string") {
|
||||
out.push(prefix);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
node.forEach((item, index) => collectStringKeys(item, `${prefix}[${index}]`, out));
|
||||
return out;
|
||||
}
|
||||
|
||||
if (node && typeof node === "object") {
|
||||
for (const key of Object.keys(node)) {
|
||||
collectStringKeys(node[key], prefix ? `${prefix}.${key}` : key, out);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
async function runAutomatedChecks() {
|
||||
const en = JSON.parse(await fs.readFile(path.join(MESSAGES_DIR, "en.json"), "utf8"));
|
||||
const enKeys = collectStringKeys(en).sort();
|
||||
|
||||
const localeFiles = (await fs.readdir(MESSAGES_DIR)).filter((file) => file.endsWith(".json"));
|
||||
const localeCodes = localeFiles.map((file) => file.replace(/\.json$/, "")).sort();
|
||||
|
||||
const parityIssues = [];
|
||||
for (const code of localeCodes) {
|
||||
if (code === "en") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const raw = await fs.readFile(path.join(MESSAGES_DIR, `${code}.json`), "utf8");
|
||||
const data = JSON.parse(raw);
|
||||
const keys = collectStringKeys(data).sort();
|
||||
|
||||
const missing = enKeys.filter((key) => !keys.includes(key));
|
||||
const extra = keys.filter((key) => !enKeys.includes(key));
|
||||
|
||||
if (missing.length || extra.length) {
|
||||
parityIssues.push({ code, missing: missing.length, extra: extra.length });
|
||||
}
|
||||
}
|
||||
|
||||
const readmeLabelChecks = [];
|
||||
const readmeExpectedPrefix = {
|
||||
"README.es.md": "🌐 **Disponible en:**",
|
||||
"README.fr.md": "🌐 **Disponible en :**",
|
||||
"README.de.md": "🌐 **Verfugbar in:**",
|
||||
"README.ja.md": "🌐 **対応言語:**",
|
||||
"README.ar.md": "🌐 **متوفر باللغات:**",
|
||||
};
|
||||
|
||||
for (const [file, expectedPrefix] of Object.entries(readmeExpectedPrefix)) {
|
||||
const content = await fs.readFile(path.join(ROOT, file), "utf8");
|
||||
const line = content.split("\n").find((entry) => entry.startsWith("🌐 **")) || "";
|
||||
|
||||
// Accept both ASCII-only and umlaut versions for DE prefix.
|
||||
const ok =
|
||||
file !== "README.de.md"
|
||||
? line.startsWith(expectedPrefix)
|
||||
: line.startsWith("🌐 **Verfügbar in:**") || line.startsWith(expectedPrefix);
|
||||
|
||||
readmeLabelChecks.push({ file, ok, line });
|
||||
}
|
||||
|
||||
const jaReadme = await fs.readFile(path.join(ROOT, "README.ja.md"), "utf8");
|
||||
const arReadme = await fs.readFile(path.join(ROOT, "README.ar.md"), "utf8");
|
||||
|
||||
const anchorLineRemoved =
|
||||
!jaReadme.includes("**[English](#-omniroute--the-free-ai-gateway)**") &&
|
||||
!arReadme.includes("**[English](#-omniroute--the-free-ai-gateway)**");
|
||||
|
||||
const brAppendixRemoved =
|
||||
!jaReadme.includes("## 🇧🇷 OmniRoute") && !arReadme.includes("## 🇧🇷 OmniRoute");
|
||||
|
||||
return {
|
||||
localeCodes,
|
||||
parityIssues,
|
||||
readmeLabelChecks,
|
||||
anchorLineRemoved,
|
||||
brAppendixRemoved,
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const allFiles = await walk(APP_DIR);
|
||||
const pageFiles = allFiles.filter((file) => file.endsWith("/page.tsx")).sort();
|
||||
|
||||
const automated = await runAutomatedChecks();
|
||||
|
||||
const routeRows = [];
|
||||
for (const pageFile of pageFiles) {
|
||||
const route = routeFromPage(pageFile);
|
||||
const metrics = await collectRouteRiskMetrics(pageFile);
|
||||
routeRows.push({
|
||||
route,
|
||||
priority: classifyPriority(route),
|
||||
...metrics,
|
||||
status: "Pendente validacao visual",
|
||||
});
|
||||
}
|
||||
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
const reportPath = path.join(REPORTS_DIR, `i18n-qa-checklist-${date}.md`);
|
||||
|
||||
const automatedChecksLines = [
|
||||
`- Locale files detectados: **${automated.localeCodes.length}**`,
|
||||
`- Locales priorizados nesta rodada: **${PRIORITY_LOCALES.join(", ")}**`,
|
||||
`- Paridade EN x demais idiomas: **${automated.parityIssues.length === 0 ? "OK" : "COM GAPS"}**`,
|
||||
];
|
||||
|
||||
if (automated.parityIssues.length > 0) {
|
||||
automatedChecksLines.push(
|
||||
...automated.parityIssues.map(
|
||||
(issue) => ` - ${issue.code}: missing=${issue.missing} extra=${issue.extra}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
automatedChecksLines.push(
|
||||
`- Prefixo local do seletor de idiomas em README (es/fr/de/ja/ar): **${automated.readmeLabelChecks.every((item) => item.ok) ? "OK" : "FALHAS"}**`,
|
||||
`- Linha legacy EN/PT removida em ja/ar: **${automated.anchorLineRemoved ? "OK" : "PENDENTE"}**`,
|
||||
`- Apêndice "## 🇧🇷 OmniRoute" removido em ja/ar: **${automated.brAppendixRemoved ? "OK" : "PENDENTE"}**`,
|
||||
"- RTL habilitado globalmente para `ar` e `he` via `dir=rtl` no layout."
|
||||
);
|
||||
|
||||
const routeTableHeader =
|
||||
"| Rota | Prioridade | Arquivos da rota | Risco truncamento (w-*) | Risco RTL direcional (left/right etc.) | Clipping (`truncate/line-clamp/overflow-hidden`) | Status |";
|
||||
const routeTableDivider = "|---|---|---:|---:|---:|---:|---|";
|
||||
const routeTableRows = routeRows.map(
|
||||
(row) =>
|
||||
`| \`${row.route}\` | ${row.priority} | ${row.files} | ${row.fixedWidth} | ${row.directional} | ${row.clipping} | ${row.status} |`
|
||||
);
|
||||
|
||||
const content = [
|
||||
"# Checklist QA i18n - Revisao Priorizada",
|
||||
"",
|
||||
`Data: ${date}`,
|
||||
"Escopo: validacao de UI e traducao para `es`, `fr`, `de`, `ja`, `ar`.",
|
||||
"",
|
||||
"## Resultado da revisao manual priorizada (item 1)",
|
||||
"",
|
||||
"- Ajustes manuais aplicados em `messages` para `es/fr/de/ja/ar` nos namespaces de alta visibilidade:",
|
||||
" - `common.*`",
|
||||
" - `sidebar.*`",
|
||||
" - `header.*`",
|
||||
" - `home.*`",
|
||||
" - `auth.*`",
|
||||
"- Ajustes de README aplicados em `README.es.md`, `README.fr.md`, `README.de.md`, `README.ja.md`, `README.ar.md`:",
|
||||
" - Prefixo local da linha de idiomas (bandeiras mantidas)",
|
||||
" - Navegacao superior localizada em `ja/ar`",
|
||||
" - Remocao da linha legada de anchors EN/PT em `ja/ar`",
|
||||
" - Remocao de apendice duplicado em `ja/ar`",
|
||||
"",
|
||||
"## Checagens automaticas",
|
||||
"",
|
||||
...automatedChecksLines,
|
||||
"",
|
||||
"## Checklist por pagina (item 2)",
|
||||
"",
|
||||
"Legenda:",
|
||||
"- Prioridade Alta: validar obrigatoriamente em desktop + mobile para `es/fr/de/ja/ar`, e em `ar` com fluxo RTL completo.",
|
||||
"- Risco truncamento: contagem heuristica de classes `w-*` em arquivos da rota.",
|
||||
"- Risco RTL direcional: contagem heuristica de classes com direcao fisica (`left/right/ml/mr/pl/pr/text-left/text-right`).",
|
||||
"",
|
||||
routeTableHeader,
|
||||
routeTableDivider,
|
||||
...routeTableRows,
|
||||
"",
|
||||
"## Passos de validacao visual recomendados",
|
||||
"",
|
||||
"1. Trocar idioma no seletor e recarregar rota atual (cookie + refresh).",
|
||||
"2. Validar titulos, subtitulos, CTAs e textos de estado vazios/erro/sucesso.",
|
||||
"3. Em `ar`: validar alinhamento RTL, ordem visual dos blocos e leitura dos componentes em tabela.",
|
||||
"4. Validar cards/tabelas com textos longos para evitar corte indevido e overflow horizontal.",
|
||||
"5. Validar labels com placeholders (`{count}`, `{provider}`, `{url}`) e tags (`<logs>`, `<analytics>`).",
|
||||
"",
|
||||
"## Observacoes",
|
||||
"",
|
||||
"- Este checklist combina validacao automatica + roteiro de validacao manual por pagina.",
|
||||
"- Nao foram executados testes E2E visuais nesta rodada (Playwright/screenshot).",
|
||||
].join("\n");
|
||||
|
||||
await fs.mkdir(REPORTS_DIR, { recursive: true });
|
||||
await fs.writeFile(reportPath, `${content}\n`, "utf8");
|
||||
|
||||
console.log(reportPath);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
477
scripts/i18n/run-visual-qa.mjs
Normal file
477
scripts/i18n/run-visual-qa.mjs
Normal file
@@ -0,0 +1,477 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { chromium, devices } from "@playwright/test";
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const REPORTS_DIR = path.join(ROOT, "docs", "reports");
|
||||
const DATE = new Date().toISOString().slice(0, 10);
|
||||
const BASE_URL = process.env.QA_BASE_URL || "http://localhost:20128";
|
||||
const REPORT_SUFFIX = process.env.QA_REPORT_SUFFIX ? `-${process.env.QA_REPORT_SUFFIX}` : "";
|
||||
|
||||
const DEFAULT_LOCALES = ["es", "fr", "de", "ja", "ar"];
|
||||
const RTL_LOCALES = new Set(["ar", "he"]);
|
||||
|
||||
const ROUTES = [
|
||||
"/dashboard/analytics",
|
||||
"/dashboard/api-manager",
|
||||
"/dashboard/audit-log",
|
||||
"/dashboard/cli-tools",
|
||||
"/dashboard/combos",
|
||||
"/dashboard/costs",
|
||||
"/dashboard/endpoint",
|
||||
"/dashboard/health",
|
||||
"/dashboard/limits",
|
||||
"/dashboard/logs",
|
||||
"/dashboard/providers",
|
||||
"/dashboard/settings",
|
||||
"/dashboard/settings/pricing",
|
||||
"/dashboard/translator",
|
||||
"/dashboard/usage",
|
||||
];
|
||||
|
||||
function parseRouteList(raw) {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const list = raw
|
||||
.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
.map((value) => (value.startsWith("/") ? value : `/${value}`));
|
||||
|
||||
return list.length > 0 ? list : null;
|
||||
}
|
||||
|
||||
const customRoutes = parseRouteList(process.env.QA_ROUTES);
|
||||
const ACTIVE_ROUTES = customRoutes || ROUTES;
|
||||
|
||||
function parseLocaleList(raw) {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const list = raw
|
||||
.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return list.length > 0 ? list : null;
|
||||
}
|
||||
|
||||
const customLocales = parseLocaleList(process.env.QA_LOCALES);
|
||||
const ACTIVE_LOCALES = customLocales || DEFAULT_LOCALES;
|
||||
|
||||
const VIEWPORTS = [
|
||||
{
|
||||
name: "desktop",
|
||||
viewport: { width: 1440, height: 900 },
|
||||
userAgent: devices["Desktop Chrome"].userAgent,
|
||||
},
|
||||
{
|
||||
name: "mobile",
|
||||
viewport: devices["iPhone 13"].viewport,
|
||||
userAgent: devices["iPhone 13"].userAgent,
|
||||
isMobile: true,
|
||||
hasTouch: true,
|
||||
deviceScaleFactor: devices["iPhone 13"].deviceScaleFactor,
|
||||
},
|
||||
];
|
||||
|
||||
function safeRoute(route) {
|
||||
return route === "/"
|
||||
? "root"
|
||||
: route.replace(/^\//, "").replace(/\//g, "__").replace(/\[|\]/g, "");
|
||||
}
|
||||
|
||||
function classifyResult(item) {
|
||||
if (item.error && !item.error.startsWith("screenshot-error:")) {
|
||||
return "Ajuste necessario";
|
||||
}
|
||||
|
||||
if (item.redirectedToLogin && item.route !== "/login") {
|
||||
return "Ajuste necessario";
|
||||
}
|
||||
|
||||
if (item.rtlMismatch) {
|
||||
return "Ajuste necessario";
|
||||
}
|
||||
|
||||
if (item.overflowCount > 8 || item.clippedCount > 6) {
|
||||
return "Revisar";
|
||||
}
|
||||
|
||||
if (item.error && item.error.startsWith("screenshot-error:")) {
|
||||
return "Revisar";
|
||||
}
|
||||
|
||||
return "OK";
|
||||
}
|
||||
|
||||
async function ensureLoggedIn(page) {
|
||||
await page.goto(`${BASE_URL}/dashboard`, { waitUntil: "domcontentloaded", timeout: 120000 });
|
||||
|
||||
if (!page.url().includes("/login")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const password = process.env.INITIAL_PASSWORD || "123456";
|
||||
const input = page.locator('input[type="password"]');
|
||||
if ((await input.count()) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await input.first().fill(password);
|
||||
const submit = page.locator('button[type="submit"]');
|
||||
if ((await submit.count()) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await submit.first().click();
|
||||
await page.waitForTimeout(700);
|
||||
|
||||
try {
|
||||
await page.waitForURL(/\/dashboard(\/.*)?/, { timeout: 30000 });
|
||||
} catch {
|
||||
// Keep going, final URL check below.
|
||||
}
|
||||
|
||||
return !page.url().includes("/login");
|
||||
}
|
||||
|
||||
async function evaluatePageHealth(page, locale) {
|
||||
return page.evaluate(
|
||||
({ locale, expectRtl }) => {
|
||||
const hasHorizontalScrollContext = (el) => {
|
||||
let current = el;
|
||||
while (current) {
|
||||
if (!(current instanceof HTMLElement)) {
|
||||
break;
|
||||
}
|
||||
const cls = typeof current.className === "string" ? current.className : "";
|
||||
if (
|
||||
cls.includes("overflow-x-auto") ||
|
||||
cls.includes("overflow-auto") ||
|
||||
cls.includes("overflow-scroll")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const style = window.getComputedStyle(current);
|
||||
if (style.overflowX === "auto" || style.overflowX === "scroll") {
|
||||
return true;
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isVisible = (el) => {
|
||||
const style = window.getComputedStyle(el);
|
||||
if (
|
||||
style.display === "none" ||
|
||||
style.visibility === "hidden" ||
|
||||
Number(style.opacity) === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const rect = el.getBoundingClientRect();
|
||||
return rect.width > 0 && rect.height > 0;
|
||||
};
|
||||
|
||||
const nodes = Array.from(document.querySelectorAll("*"));
|
||||
let overflowCount = 0;
|
||||
let clippedCount = 0;
|
||||
const samples = [];
|
||||
|
||||
for (const el of nodes) {
|
||||
if (!(el instanceof HTMLElement)) {
|
||||
continue;
|
||||
}
|
||||
if (!isVisible(el)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const text = (el.innerText || "").trim().replace(/\s+/g, " ");
|
||||
if (!text || text.length < 12) {
|
||||
continue;
|
||||
}
|
||||
if (text === "Skip to content") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const cls = el.className || "";
|
||||
const classString = typeof cls === "string" ? cls : "";
|
||||
if (
|
||||
classString.includes("monaco-") ||
|
||||
el.closest(".monaco-editor") ||
|
||||
el.closest(".monaco-scrollable-element")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (el.tagName === "HTML" || el.tagName === "BODY") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const overW = el.scrollWidth > el.clientWidth + 1;
|
||||
if (!overW) {
|
||||
continue;
|
||||
}
|
||||
if (hasHorizontalScrollContext(el)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decorative absolute layers often exceed bounds by design and should not
|
||||
// be treated as localization regressions.
|
||||
const style = window.getComputedStyle(el);
|
||||
if (style.position === "absolute" && el.getAttribute("aria-hidden") === "true") {
|
||||
continue;
|
||||
}
|
||||
|
||||
overflowCount += 1;
|
||||
|
||||
const looksClipped =
|
||||
classString.includes("truncate") ||
|
||||
classString.includes("line-clamp-") ||
|
||||
style.overflowX === "hidden" ||
|
||||
style.overflowY === "hidden" ||
|
||||
style.textOverflow === "ellipsis";
|
||||
|
||||
if (looksClipped) {
|
||||
clippedCount += 1;
|
||||
}
|
||||
|
||||
if (samples.length < 10 && looksClipped) {
|
||||
samples.push({
|
||||
tag: el.tagName.toLowerCase(),
|
||||
className: classString.slice(0, 120),
|
||||
text: text.slice(0, 140),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const dir = document.documentElement.getAttribute("dir") || "";
|
||||
const lang = document.documentElement.getAttribute("lang") || "";
|
||||
const rtlMismatch = expectRtl ? dir !== "rtl" : dir === "rtl";
|
||||
|
||||
return {
|
||||
locale,
|
||||
dir,
|
||||
lang,
|
||||
rtlMismatch,
|
||||
overflowCount,
|
||||
clippedCount,
|
||||
clippedSamples: samples,
|
||||
};
|
||||
},
|
||||
{ locale, expectRtl: RTL_LOCALES.has(locale) }
|
||||
);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await fs.mkdir(REPORTS_DIR, { recursive: true });
|
||||
|
||||
const screenshotRoot = path.join(REPORTS_DIR, `i18n-qa-screenshots-${DATE}`);
|
||||
await fs.mkdir(screenshotRoot, { recursive: true });
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
|
||||
const allResults = [];
|
||||
|
||||
for (const viewportSpec of VIEWPORTS) {
|
||||
const context = await browser.newContext({
|
||||
viewport: viewportSpec.viewport,
|
||||
userAgent: viewportSpec.userAgent,
|
||||
isMobile: viewportSpec.isMobile,
|
||||
hasTouch: viewportSpec.hasTouch,
|
||||
deviceScaleFactor: viewportSpec.deviceScaleFactor,
|
||||
});
|
||||
|
||||
const page = await context.newPage();
|
||||
|
||||
const logged = await ensureLoggedIn(page);
|
||||
console.log(`[qa] ${viewportSpec.name} login state: ${logged ? "ok" : "not-authenticated"}`);
|
||||
|
||||
for (const locale of ACTIVE_LOCALES) {
|
||||
await context.addCookies([
|
||||
{
|
||||
name: "NEXT_LOCALE",
|
||||
value: locale,
|
||||
domain: "localhost",
|
||||
path: "/",
|
||||
},
|
||||
]);
|
||||
|
||||
for (const route of ACTIVE_ROUTES) {
|
||||
const started = Date.now();
|
||||
const result = {
|
||||
route,
|
||||
locale,
|
||||
viewport: viewportSpec.name,
|
||||
finalUrl: "",
|
||||
durationMs: 0,
|
||||
status: "OK",
|
||||
redirectedToLogin: false,
|
||||
rtlMismatch: false,
|
||||
overflowCount: 0,
|
||||
clippedCount: 0,
|
||||
clippedSamples: [],
|
||||
dir: "",
|
||||
lang: "",
|
||||
error: "",
|
||||
screenshot: "",
|
||||
};
|
||||
|
||||
try {
|
||||
await page.goto(`${BASE_URL}${route}`, {
|
||||
waitUntil: "domcontentloaded",
|
||||
timeout: 120000,
|
||||
});
|
||||
await page.waitForTimeout(500);
|
||||
result.finalUrl = page.url();
|
||||
result.redirectedToLogin = result.finalUrl.includes("/login");
|
||||
|
||||
const metrics = await evaluatePageHealth(page, locale);
|
||||
result.rtlMismatch = metrics.rtlMismatch;
|
||||
result.overflowCount = metrics.overflowCount;
|
||||
result.clippedCount = metrics.clippedCount;
|
||||
result.clippedSamples = metrics.clippedSamples;
|
||||
result.dir = metrics.dir;
|
||||
result.lang = metrics.lang;
|
||||
} catch (error) {
|
||||
result.error = String(error?.message || error);
|
||||
}
|
||||
|
||||
result.durationMs = Date.now() - started;
|
||||
|
||||
const localeDir = path.join(screenshotRoot, viewportSpec.name, locale);
|
||||
await fs.mkdir(localeDir, { recursive: true });
|
||||
const screenshotPath = path.join(localeDir, `${safeRoute(route)}.png`);
|
||||
|
||||
try {
|
||||
await page.screenshot({ path: screenshotPath, fullPage: true, timeout: 120000 });
|
||||
result.screenshot = path.relative(ROOT, screenshotPath).replaceAll("\\", "/");
|
||||
} catch (error) {
|
||||
if (!result.error) {
|
||||
result.error = `screenshot-error: ${String(error?.message || error)}`;
|
||||
}
|
||||
}
|
||||
|
||||
result.status = classifyResult(result);
|
||||
allResults.push(result);
|
||||
|
||||
console.log(
|
||||
`[qa] ${viewportSpec.name} ${locale} ${route} -> ${result.status}` +
|
||||
`${result.redirectedToLogin ? " (redirected-login)" : ""}` +
|
||||
`${result.rtlMismatch ? " (rtl-mismatch)" : ""}` +
|
||||
`${result.clippedCount ? ` (clipped=${result.clippedCount})` : ""}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await context.close();
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
|
||||
const jsonPath = path.join(REPORTS_DIR, `i18n-visual-qa-${DATE}${REPORT_SUFFIX}.json`);
|
||||
await fs.writeFile(jsonPath, `${JSON.stringify(allResults, null, 2)}\n`, "utf8");
|
||||
|
||||
const aggregate = new Map();
|
||||
const aggregateByLocale = new Map();
|
||||
for (const item of allResults) {
|
||||
const key = item.route;
|
||||
if (!aggregate.has(key)) {
|
||||
aggregate.set(key, {
|
||||
route: key,
|
||||
ok: 0,
|
||||
review: 0,
|
||||
adjust: 0,
|
||||
clipped: 0,
|
||||
loginRedirects: 0,
|
||||
rtlMismatch: 0,
|
||||
});
|
||||
}
|
||||
|
||||
const slot = aggregate.get(key);
|
||||
if (item.status === "OK") slot.ok += 1;
|
||||
if (item.status === "Revisar") slot.review += 1;
|
||||
if (item.status === "Ajuste necessario") slot.adjust += 1;
|
||||
slot.clipped += item.clippedCount;
|
||||
if (item.redirectedToLogin) slot.loginRedirects += 1;
|
||||
if (item.rtlMismatch) slot.rtlMismatch += 1;
|
||||
|
||||
const localeKey = item.locale;
|
||||
if (!aggregateByLocale.has(localeKey)) {
|
||||
aggregateByLocale.set(localeKey, {
|
||||
locale: localeKey,
|
||||
ok: 0,
|
||||
review: 0,
|
||||
adjust: 0,
|
||||
clipped: 0,
|
||||
loginRedirects: 0,
|
||||
rtlMismatch: 0,
|
||||
});
|
||||
}
|
||||
|
||||
const localeSlot = aggregateByLocale.get(localeKey);
|
||||
if (item.status === "OK") localeSlot.ok += 1;
|
||||
if (item.status === "Revisar") localeSlot.review += 1;
|
||||
if (item.status === "Ajuste necessario") localeSlot.adjust += 1;
|
||||
localeSlot.clipped += item.clippedCount;
|
||||
if (item.redirectedToLogin) localeSlot.loginRedirects += 1;
|
||||
if (item.rtlMismatch) localeSlot.rtlMismatch += 1;
|
||||
}
|
||||
|
||||
const lines = [
|
||||
"# Relatorio QA Visual i18n",
|
||||
"",
|
||||
`Data: ${DATE}`,
|
||||
`Base URL: ${BASE_URL}`,
|
||||
`Locales: ${ACTIVE_LOCALES.join(", ")}`,
|
||||
`Viewports: ${VIEWPORTS.map((v) => v.name).join(", ")}`,
|
||||
"",
|
||||
"## Resumo por rota",
|
||||
"",
|
||||
"| Rota | OK | Revisar | Ajuste necessario | Clipped total | Redirect login | RTL mismatch |",
|
||||
"|---|---:|---:|---:|---:|---:|---:|",
|
||||
...Array.from(aggregate.values()).map(
|
||||
(row) =>
|
||||
`| \`${row.route}\` | ${row.ok} | ${row.review} | ${row.adjust} | ${row.clipped} | ${row.loginRedirects} | ${row.rtlMismatch} |`
|
||||
),
|
||||
"",
|
||||
"## Resumo por locale",
|
||||
"",
|
||||
"| Locale | OK | Revisar | Ajuste necessario | Clipped total | Redirect login | RTL mismatch |",
|
||||
"|---|---:|---:|---:|---:|---:|---:|",
|
||||
...Array.from(aggregateByLocale.values())
|
||||
.sort((a, b) => a.locale.localeCompare(b.locale))
|
||||
.map(
|
||||
(row) =>
|
||||
`| \`${row.locale}\` | ${row.ok} | ${row.review} | ${row.adjust} | ${row.clipped} | ${row.loginRedirects} | ${row.rtlMismatch} |`
|
||||
),
|
||||
"",
|
||||
"## Artefatos",
|
||||
"",
|
||||
`- JSON detalhado: \`${path.relative(ROOT, jsonPath)}\``,
|
||||
`- Screenshots: \`${path.relative(ROOT, screenshotRoot)}\``,
|
||||
"",
|
||||
"## Observacoes",
|
||||
"",
|
||||
"- Status `Revisar` e `Ajuste necessario` sao heuristicas automaticas (overflow/clipping/RTL/redirect).",
|
||||
"- A validacao final de UX deve ser confirmada manualmente nas rotas sinalizadas.",
|
||||
];
|
||||
|
||||
const mdPath = path.join(REPORTS_DIR, `i18n-visual-qa-${DATE}${REPORT_SUFFIX}.md`);
|
||||
await fs.writeFile(mdPath, `${lines.join("\n")}\n`, "utf8");
|
||||
|
||||
console.log(mdPath);
|
||||
console.log(jsonPath);
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user