Files
OmniRoute/docs/i18n/id/docs/architecture/CODEBASE_DOCUMENTATION.md
Diego Rodrigues de Sa e Souza 7db430a352 Release v3.8.14 (#3340)
* chore(release): open v3.8.14 development cycle

Version bump 3.8.13 -> 3.8.14 (root + electron + open-sse + openapi + lockfiles).
Seed the v3.8.14 changelog with the four post-tag hotfixes that shipped to
Docker/Electron in v3.8.13 but missed the immutable npm 3.8.13 (#3336 SSRF /
CodeQL #323, #3334/#3335/#3339 Electron packaging). i18n CHANGELOG mirrors get
the in-progress placeholder section.

* feat: add per-provider custom headers support for OpenAI/Anthropic-compatible nodes (#3338)

Integrated into release/v3.8.14

* fix: Kiro Builder ID token import fails with Bad credentials (#3333)

Integrated into release/v3.8.14 — adds Builder ID cached-creds + OIDC refresh path for Kiro token import, with regression tests (#3333).

* Improve code quality: auto-pr/docstrings-1780792063 (#3337)

Integrated into release/v3.8.14 — docstring for context analytics route re-export.

* fix(catalog): remove minimaxai/minimax-m3 from NVIDIA NIM tier (404 upstream) (#3329) (#3341)

NVIDIA NIM does not host minimaxai/minimax-m3 — every request returns
404 page not found, while sibling minimaxai/minimax-m2.7 on the same provider
works. Advertising a model that 404s is a catalog bug; remove it from the nvidia
tier (it remains on the tiers that actually serve MiniMax M3). Re-add only once
NVIDIA serves it.

Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com>

* fix(cli): write OpenCode config to ~/.config on all platforms incl. Windows (#3330) (#3343)

resolveOpencodeConfigDir used %APPDATA% on Windows, but OpenCode reads its
config from XDG ~/.config/opencode/ on every platform (on Windows:
%USERPROFILE%\.config\opencode\, NOT %APPDATA%). So a Windows user who
configured OpenCode via the dashboard had the file written where OpenCode never
looks — it silently had no effect.

Use the XDG path (XDG_CONFIG_HOME || ~/.config) unconditionally. Update the UI
note + route JSDoc, and flip the three tests that encoded the old %APPDATA%
behavior (t40 per-platform + card-note, cli-runtime-extended getCliConfigPaths).

Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com>

* fix(proxy): make auto-selection fallback opt-in (#3332) (#3344)

selectWorkingProxyFallback (Step 11 of resolveProxyForConnection) listed ALL
registry proxies, ignoring assignments and per-connection proxy_enabled, and
returned the first working one with level:'autoSelect'. So a single proxy added
to the registry silently became a global fallback for every connection's traffic.

Gate it behind a new PROXY_AUTO_SELECT_ENABLED feature flag (default off): the
fallback now no-ops unless the operator opts in. No registry proxy becomes a
silent global default anymore.

Co-authored-by: hertznsk <hertznsk@users.noreply.github.com>

* fix(sse): treat MiniMax M3 as multimodal so vision isn't stripped (#3328) (#3342)

MiniMax M3 via the opencode provider (oc/minimax-m3-free) appeared blind:
image inputs didn't reach the model, while the same model in Cline could
see them. Verified empirically that MiniMax M3 on the opencode upstream IS
multimodal -- a base64 image is described correctly (it returns 403 only
for remote image URLs, which it doesn't accept).

Root cause: OmniRoute treated MiniMax M3 as a non-vision model in two
places, so when compression was active the image was replaced with a text
placeholder before dispatch:
- compression's modelSupportsVision() heuristic (lite.ts) only matched
  gpt-4/4o/claude-3/gemini/vision -- minimax was absent -> replaceImageUrls
  stripped the image.
- the opencode minimax-m3-free catalog entry lacked supportsVision, so the
  combo vision-capability gate could also exclude/mishandle it.

Add 'minimax-m3' to the vision heuristic and supportsVision: true to the
opencode minimax-m3-free entry. TDD: a failing-then-passing test in
compression/lite.test.ts proves replaceImageUrls now keeps images for
minimax-m3 ids, plus a registry assertion mirroring the #2822 qwen test.

Reported-by: @mikmaneggahommie

* docs(i18n): translate 25 core documentation files to Indonesian (#3348)

Integrated into release/v3.8.14 — Indonesian i18n docs.

* fix(review): resolve /review-reviews battery findings (LEDGER-1..11) on v3.8.14 (#3350)

Integrated into release/v3.8.14 — /review-reviews battery hardening (LEDGER-1..11) for #3338 custom-headers + #3333 kiro, plus cycle-test drift fixes (#3329/#3330/#3332).

* fix(provider-proxy): honor per-account proxy toggles (#3349)

Integrated into release/v3.8.14 — honor per-account proxy toggles + auto-fallback opt-in via PROXY_AUTO_SELECT_ENABLED.

* fix(dashboard): remove duplicate Distribute Proxies button on provider page (#3352)

* fix(providers): reduce proxy label noise (#3346)

Integrated into release/v3.8.14 — reduce proxy label noise + a11y (aria-label/sr-only).

* fix(duckduckgo): restore bare Response contract and rebase onto release/v3.8.14 (#3323)

Integrated into release/v3.8.14 — browser-backed cookie providers (duckduckgo/claude-web) with restored executor contract + unit tests.

* fix(noauth): expose only usable model aliases (#3345)

Integrated into release/v3.8.14 — noauth usable-alias filtering + registry alias plumbing (veo-free).

* fix(dashboard): stop infinite config-load loop on Hermes Agent detail page (#3353)

* fix(electron): tree-kill the server on exit/update to release the omniroute.exe lock (#3347) (#3354)

* chore(release): finalize v3.8.14 changelog + clear release-gate drift

- CHANGELOG: finalize the v3.8.14 section (date, full New Features/Bug Fixes/
  Maintenance coverage of all 16 cycle commits, Contributors hall of 12).
- docs: document OMNIROUTE_BROWSER_POOL + WEB_COOKIE_USE_BROWSER (#3323) in
  .env.example + ENVIRONMENT.md; regenerate the id/llm.txt strict mirror (#3348
  had translated it; llm.txt mirrors must match root).
- test(proxy-fetch): #3323 made tlsClient.available a computed getter — stub it
  via Object.defineProperty instead of assignment (5 tests were red on the base).

* fix(translator): coerce Gemini functionDeclaration parameters to an OBJECT schema (#3357) (#3360)

* fix(gemini): resolve truncation/suppression of false positive textual tool call markers in backticks (#3358)

Integrated into release/v3.8.14 — Gemini/Antigravity textual tool-call marker normalization (no false-positive suppression + split-chunk buffering).

* docs(changelog): add #3358 Gemini textual tool-call normalization to v3.8.14

* fix(dashboard): surface real analytics error instead of generic placeholder (#3356) (#3361)

The Analytics page discarded the server's error body on a non-OK response and
rendered a generic "An error occurred", so users (and maintainers) could not see
why /api/usage/analytics 500'd after an upgrade. Now the route returns the real
reason via buildErrorBody (sanitized, Hard Rule #12) and the page surfaces it via
a new readFetchErrorMessage helper that handles both the OpenAI-style and legacy
error shapes.

Reported-by: @superti4r

---------

Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Someres <168349709+quanturbo@users.noreply.github.com>
Co-authored-by: Dong Mengzhe <154944819+Lang-Qiu@users.noreply.github.com>
Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com>
Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com>
Co-authored-by: hertznsk <hertznsk@users.noreply.github.com>
Co-authored-by: Krisna Santosa <54174372+KrisnaSantosa15@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
2026-06-07 07:20:02 -03:00

38 KiB

omniroute — Codebase Documentation (Bahasa Indonesia)

🌐 Languages: 🇺🇸 English · 🇸🇦 ar · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇪🇸 es · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇳 gu · 🇮🇱 he · 🇮🇳 hi · 🇭🇺 hu · 🇮🇩 id · 🇮🇹 it · 🇯🇵 ja · 🇰🇷 ko · 🇮🇳 mr · 🇲🇾 ms · 🇳🇱 nl · 🇳🇴 no · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇸🇰 sk · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇻🇳 vi · 🇨🇳 zh-CN


Panduan lengkap dan ramah-pemula untuk router proxy AI multi-penyedia omniroute.


1. Apa Itu omniroute?

omniroute adalah sebuah router proxy yang berada di antara klien AI (Claude CLI, Codex, Cursor IDE, dll.) dan penyedia AI (Anthropic, Google, OpenAI, AWS, GitHub, dll.). Ia memecahkan satu masalah besar:

Klien AI yang berbeda berbicara "bahasa" yang berbeda (format API), dan penyedia AI yang berbeda pun mengharapkan "bahasa" yang berbeda pula. omniroute menerjemahkan di antara mereka secara otomatis.

Bayangkan seperti penerjemah universal di Perserikatan Bangsa-Bangsa — delegasi mana pun dapat berbicara dalam bahasa apa pun, dan penerjemah mengubahnya untuk delegasi lainnya.


2. Ikhtisar Arsitektur

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

Prinsip Inti: Terjemahan Hub-and-Spoke

Semua terjemahan format melewati format OpenAI sebagai hub:

Client Format → [OpenAI Hub] → Provider Format    (request)
Provider Format → [OpenAI Hub] → Client Format    (response)

Artinya Anda hanya membutuhkan N penerjemah (satu per format), bukan (setiap pasangan).


3. Struktur Proyek

omniroute/
├── open-sse/                  ← Library proxy inti (portabel, framework-agnostic)
│   ├── index.js               ← Titik masuk utama, mengekspor segalanya
│   ├── config/                ← Konfigurasi & konstanta
│   ├── executors/             ← Eksekusi permintaan khusus penyedia
│   ├── handlers/              ← Orkestrasi penanganan permintaan
│   ├── services/              ← Logika bisnis (auth, model, fallback, penggunaan)
│   ├── translator/            ← Mesin terjemahan format
│   │   ├── request/           ← Penerjemah permintaan (8 file)
│   │   ├── response/          ← Penerjemah respons (7 file)
│   │   └── helpers/           ← Utilitas terjemahan bersama (6 file)
│   └── utils/                 ← Fungsi utilitas
├── src/                       ← Lapisan aplikasi (runtime Express/Worker)
│   ├── app/                   ← Antarmuka web, rute API, middleware
│   ├── lib/                   ← Database, auth, dan kode library bersama
│   ├── mitm/                  ← Utilitas proxy man-in-the-middle
│   ├── models/                ← Model database
│   ├── shared/                ← Utilitas bersama (wrapper open-sse)
│   ├── sse/                   ← Handler endpoint SSE
│   └── store/                 ← Manajemen state
├── data/                      ← Data runtime (kredensial, log)
│   └── provider-credentials.json   (override kredensial eksternal, diabaikan git)
└── tester/                    ← Utilitas pengujian

4. Rincian Modul per Modul

4.1 Config (open-sse/config/)

Satu-satunya sumber kebenaran untuk semua konfigurasi penyedia.

File Tujuan
constants.ts Objek PROVIDERS dengan URL dasar, kredensial OAuth (default), header, dan system prompt default untuk setiap penyedia. Juga mendefinisikan HTTP_STATUS, ERROR_TYPES, COOLDOWN_MS, BACKOFF_CONFIG, dan SKIP_PATTERNS.
credentialLoader.ts Memuat kredensial eksternal dari data/provider-credentials.json dan menggabungkannya ke atas nilai default yang ter-hardcode di PROVIDERS. Menjaga rahasia di luar source control sambil mempertahankan kompatibilitas mundur.
providerModels.ts Registry model terpusat: memetakan alias penyedia → ID model. Fungsi-fungsi seperti getModels(), getProviderByAlias().
codexInstructions.ts Instruksi sistem yang disuntikkan ke dalam permintaan Codex (batasan pengeditan, aturan sandbox, kebijakan persetujuan).
defaultThinkingSignature.ts Tanda tangan "berpikir" default untuk model Claude dan Gemini.
ollamaModels.ts Definisi skema untuk model Ollama lokal (nama, ukuran, keluarga, kuantisasi).

Alur Pemuatan Kredensial

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["Gunakan default hardcode"]
    D --> F{"For each provider in JSON"}
    F --> G{"Provider exists\nin PROVIDERS?"}
    G -->|No| H["Log warning, skip"]
    G -->|Yes| I{"Nilai adalah objek?"}
    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 siap dengan\nkredensial yang digabungkan"]
    E --> L

4.2 Executors (open-sse/executors/)

Executor merangkum logika khusus penyedia menggunakan Strategy Pattern. Setiap executor mengganti metode dasar sesuai kebutuhan.

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
Executor Penyedia Spesialisasi Utama
base.ts Basis abstrak: pembangunan URL, header, logika percobaan ulang, pembaruan kredensial
default.ts Claude, Gemini, OpenAI, GLM, Kimi, MiniMax Pembaruan token OAuth generik untuk penyedia standar
antigravity.ts Google Cloud Code Pembuatan ID proyek/sesi, fallback multi-URL, parsing percobaan ulang kustom dari pesan error ("reset after 2h7m23s")
cursor.ts Cursor IDE Paling kompleks: autentikasi checksum SHA-256, encoding permintaan Protobuf, parsing binary EventStream → respons SSE
codex.ts OpenAI Codex Menyuntikkan instruksi sistem, mengelola tingkat berpikir, menghapus parameter yang tidak didukung
gemini-cli.ts Google Gemini CLI Pembangunan URL kustom (streamGenerateContent), pembaruan token OAuth Google
github.ts GitHub Copilot Sistem token ganda (GitHub OAuth + token Copilot), peniruan header VSCode
kiro.ts AWS CodeWhisperer Parsing binary AWS EventStream, frame event AMZN, estimasi token
index.ts Factory: memetakan nama penyedia → kelas executor, dengan fallback default

4.3 Handlers (open-sse/handlers/)

Lapisan orkestrasi — mengoordinasikan terjemahan, eksekusi, streaming, dan penanganan error.

File Tujuan
chatCore.ts Orkestrator pusat (~600 baris). Menangani siklus hidup permintaan secara lengkap: deteksi format → terjemahan → dispatch executor → respons streaming/non-streaming → pembaruan token → penanganan error → pencatatan penggunaan.
responsesHandler.ts Adaptor untuk Responses API OpenAI: mengonversi format Responses → Penyelesaian Obrolan → mengirim ke chatCore → mengonversi SSE kembali ke format Responses.
embeddings.ts Handler pembuatan embedding: me-resolve model embedding → penyedia, mengirim ke API penyedia, mengembalikan respons embedding yang kompatibel dengan OpenAI. Mendukung 6+ penyedia.
imageGeneration.ts Handler pembuatan gambar: me-resolve model gambar → penyedia, mendukung mode kompatibel-OpenAI, Gemini-image (Antigravity), dan fallback (Nebius). Mengembalikan gambar base64 atau URL.

Siklus Hidup Permintaan (chatCore.ts)

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 Services (open-sse/services/)

Logika bisnis yang mendukung handler dan executor.

File Tujuan
provider.ts Deteksi format (detectFormat): menganalisis struktur body permintaan untuk mengidentifikasi format Claude/OpenAI/Gemini/Antigravity/Responses (mencakup heuristik max_tokens untuk Claude). Juga: pembangunan URL, pembangunan header, normalisasi konfigurasi berpikir. Mendukung penyedia dinamis openai-compatible-* dan anthropic-compatible-*.
model.ts Parsing string model (claude/model-name{provider: "claude", model: "model-name"}), resolusi alias dengan deteksi tabrakan, sanitasi input (menolak path traversal/karakter kontrol), dan resolusi info model dengan dukungan getter alias asinkron.
accountFallback.ts Penanganan rate-limit: backoff eksponensial (1d → 2d → 4d → maks 2min), manajemen cooldown akun, klasifikasi error (error mana yang memicu fallback vs. tidak).
tokenRefresh.ts Pembaruan token OAuth untuk setiap penyedia: Google (Gemini, Antigravity), Claude, Codex, Qwen, Qoder, GitHub (OAuth + token ganda Copilot), Kiro (AWS SSO OIDC + Social Auth). Mencakup cache deduplikasi promise in-flight dan percobaan ulang dengan backoff eksponensial.
combo.ts Model combo: rantai model fallback. Jika model A gagal dengan error yang memenuhi syarat fallback, coba model B, lalu C, dst. Mengembalikan kode status upstream yang sebenarnya.
usage.ts Mengambil data kuota/penggunaan dari API penyedia (kuota GitHub Copilot, kuota model Antigravity, batas laju Codex, rincian penggunaan Kiro, pengaturan Claude).
accountSelector.ts Pemilihan akun cerdas dengan algoritma penilaian: mempertimbangkan prioritas, status kesehatan, posisi round-robin, dan kondisi cooldown untuk memilih akun optimal setiap permintaan.
contextManager.ts Manajemen siklus hidup konteks permintaan: membuat dan melacak objek konteks per-permintaan dengan metadata (ID permintaan, stempel waktu, info penyedia) untuk debugging dan pencatatan.
ipFilter.ts Kontrol akses berbasis IP: mendukung mode allowlist dan blocklist. Memvalidasi IP klien terhadap aturan yang dikonfigurasi sebelum memproses permintaan API.
sessionManager.ts Pelacakan sesi dengan fingerprinting klien: melacak sesi aktif menggunakan identifier klien yang di-hash, memantau jumlah permintaan, dan menyediakan metrik sesi.
signatureCache.ts Cache deduplikasi berbasis tanda tangan permintaan: mencegah permintaan duplikat dengan menyimpan cache tanda tangan permintaan terbaru dan mengembalikan respons tersimpan untuk permintaan identik dalam jendela waktu tertentu.
systemPrompt.ts Injeksi system prompt global: menambahkan di depan atau di belakang system prompt yang dapat dikonfigurasi ke semua permintaan, dengan penanganan kompatibilitas per-penyedia.
thinkingBudget.ts Manajemen anggaran token penalaran: mendukung mode passthrough, auto (hapus konfigurasi berpikir), kustom (anggaran tetap), dan adaptif (skala kompleksitas) untuk mengendalikan token berpikir/penalaran.
wildcardRouter.ts Routing pola model wildcard: me-resolve pola wildcard (mis., */claude-*) ke pasangan penyedia/model konkret berdasarkan ketersediaan dan prioritas.

Deduplikasi Pembaruan Token

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

Mesin Status Fallback Akun

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
    }

Model Rantai Kombo

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 Translator (open-sse/translator/)

Mesin terjemahan format yang menggunakan sistem plugin pendaftaran-diri.

Arsitektur

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
Direktori File Deskripsi
request/ 8 penerjemah Mengonversi body permintaan antar format. Setiap file mendaftar sendiri melalui register(from, to, fn) saat diimpor.
response/ 7 penerjemah Mengonversi potongan respon streaming antar format. berpartisipasi tipe acara SSE, blok berpikir, panggilan alat.
helpers/ 6 pembantu Utilitas bersama: claudeHelper (ekstraksi system prompt, konfigurasi berpikir), geminiHelper (pemetaan parts/contents), openaiHelper (pemfilteran format), toolCallHelper (pembuatan ID, injeksi respons yang hilang), maxTokensHelper, responsesApiHelper.
index.ts Mesin terjemahan: translateRequest(), translateResponse(), manajemen state, registry.
formats.ts Konstanta format: OPENAI, CLAUDE, GEMINI, ANTIGRAVITY, KIRO, CURSOR, OPENAI_RESPONSES.

Desain Utama: Plugin Pendaftaran-Diri

// Setiap file penerjemah memanggil register() saat diimpor:
import { register } from "../index.js";
register("claude", "openai", translateClaudeToOpenAI);

// index.js mengimpor semua file penerjemah, memicu pendaftaran:
import "./request/claude-to-openai.js"; // ← mendaftar sendiri

4.6 Utils (open-sse/utils/)

File Tujuan
error.ts Pembangunan respons error (format kompatibel-OpenAI), parsing error upstream, ekstraksi waktu percobaan ulang Antigravity dari pesan error, streaming error SSE.
stream.ts SSE Transform Stream — inti streaming saluran pipa. Mode dua: TRANSLATE (terjemahan format penuh) dan PASSTHROUGH (normalisasi + penggunaan ekstraksi). menyertakan buffering potongan, estimasi penggunaan, pelacakan panjang konten. Encoder/decoder instance per-stream menghindari status bersama.
streamHelpers.ts Utilitas SSE tingkat rendah: parseSSELine (toleran terhadap spasi), hasValuableContent (menyaring potongan kosong untuk OpenAI/Claude/Gemini), fixInvalidId, formatSSE (serialisasi SSE yang peka format dengan pembersihan perf_metrics).
usageTracking.ts Ekstraksi penggunaan token dari format apa pun (Claude/OpenAI/Gemini/Responses), estimasi dengan rasio karakter-per-token terpisah untuk tool/pesan, penambahan buffer (margin keamanan 2000 token), pemfilteran field spesifik-format, pencatatan konsol dengan warna ANSI.
requestLogger.ts Pembantu pencatatan permintaan berbasis file lawas yang dipertahankan untuk kompatibilitas. Deployment saat ini sebaiknya menggunakan APP_LOG_TO_FILE untuk log aplikasi dan pipeline log panggilan untuk artefak permintaan yang dipersistensikan.
bypassHandler.ts Mengintersep pola tertentu dari Claude CLI (ekstraksi judul, pemanasan, penghitungan) dan mengembalikan respons palsu tanpa memanggil penyedia apa pun. Mendukung streaming maupun non-streaming. Sengaja dibatasi hanya untuk cakupan Claude CLI.
networkProxy.ts Me-resolve URL proxy keluar untuk penyedia tertentu dengan urutan prioritas: konfigurasi spesifik-penyedia → konfigurasi global → variabel lingkungan (HTTPS_PROXY/HTTP_PROXY/ALL_PROXY). Mendukung pengecualian NO_PROXY. Menyimpan cache konfigurasi selama 30d.

SSE Streaming Saluran Pipa

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

Struktur Sesi Permintaan Logger

logs/
└── claude_gemini_claude-sonnet_20260208_143045/
    ├── 1_req_client.json      ← Permintaan klien mentah
    ├── 2_req_source.json      ← Setelah konversi awal
    ├── 3_req_openai.json      ← Format perantara OpenAI
    ├── 4_req_target.json      ← Format target akhir
    ├── 5_res_provider.txt     ← Potongan SSE penyedia (streaming)
    ├── 5_res_provider.json    ← Respons penyedia (non-streaming)
    ├── 6_res_openai.txt       ← Potongan perantara OpenAI
    ├── 7_res_client.txt       ← Potongan SSE yang menghadap klien
    └── 6_error.json           ← Detail error (jika ada)

4.7 Lapisan Aplikasi (src/)

Direktori Tujuan
src/app/ Antarmuka web, rute API, middleware Express, handler callback OAuth
src/lib/ Akses database (localDb.ts, usageDb.ts), autentikasi, kode bersama
src/mitm/ Utilitas proxy man-in-the-middle untuk mengintersep lalu lintas penyedia
src/models/ Definisi model basis data
src/shared/ Wrapper fungsi open-sse (penyedia, stream, error, dll.)
src/sse/ Handler endpoint SSE yang menghubungkan library open-sse ke rute Express
src/store/ Manajemen state aplikasi

Rute API Penting

Rute Metode Tujuan
/api/provider-models GET/POST/DELETE CRUD untuk model kustom per penyedia
/api/models/catalog GET Katalog gabungan semua model (chat, embedding, gambar, kustom) yang dikelompokkan per penyedia
/api/settings/proxy GET/PUT/DELETE Konfigurasi proxy keluar hierarkis (global/providers/combos/keys)
/api/settings/proxy/test POST Memvalidasi konektivitas proxy dan mengembalikan IP publik/latensi
/v1/providers/[provider]/chat/completions POST Chat completions khusus per-penyedia dengan validasi model
/v1/providers/[provider]/embeddings POST Embedding khusus per-penyedia dengan validasi model
/v1/providers/[provider]/images/generations POST Pembuatan gambar khusus per-penyedia dengan validasi model
/api/settings/ip-filter GET/PUT Manajemen allowlist/blocklist IP
/api/settings/thinking-budget GET/PUT Konfigurasi anggaran token penalaran (passthrough/auto/custom/adaptive)
/api/settings/system-prompt GET/PUT Injeksi system prompt global untuk semua permintaan
/api/sessions GET Pelacakan sesi aktif dan metrik
/api/rate-limits GET Status batas laju per-akun

5. Pola Desain Utama

5.1 Terjemahan Hub-and-Spoke

Semua format diterjemahkan melalui format OpenAI sebagai hub. Menambahkan penyedia baru hanya membutuhkan penulisan satu pasang penerjemah (ke/dari OpenAI), bukan N pasangan.

5.2 Strategy Pattern pada Executor

Setiap penyedia memiliki kelas executor khusus yang mewarisi dari BaseExecutor. Factory di executors/index.ts memilih yang tepat saat runtime.

5.3 Sistem Plugin Pendaftaran-Diri

Modul penerjemah mendaftarkan diri saat diimpor melalui register(). Menambahkan penerjemah baru cukup dengan membuat file dan mengimpornya.

5.4 Fallback Akun dengan Backoff Eksponensial

Ketika penyedia mengembalikan 429/401/500, sistem dapat beralih ke akun berikutnya, menerapkan cooldown eksponensial (1d → 2d → 4d → maks 2min).

5.5 Model Rantai Kombo

Sebuah "combo" mengelompokkan beberapa string provider/model. Jika yang pertama gagal, otomatis beralih ke berikutnya.

5.6 Terjemahan Streaming dengan State

Terjemahan respons mempertahankan state di seluruh potongan SSE (pelacakan blok berpikir, akumulasi tool call, pengindeksan blok konten) melalui mekanisme initState().

5.7 Buffer Keamanan Penggunaan

Buffer 2000 token ditambahkan ke penggunaan yang dilaporkan untuk mencegah klien mencapai batas jendela konteks akibat overhead dari system prompt dan terjemahan format.


6. Format yang Didukung

Format Arah Identifier
OpenAI Chat Completions sumber + target openai
API Respons OpenAI sumber + target openai-responses
Anthropic Claude sumber + target claude
Google Gemini sumber + target gemini
Google Gemini CLI target saja gemini-cli
Antigravity sumber + target antigravity
AWS Kiro target saja kiro
Cursor target saja cursor

7. Penyedia yang Didukung

Penyedia Metode Autentikasi Executor Catatan Utama
Anthropic Claude Kunci API atau OAuth Default Menggunakan header x-api-key
Google Gemini Kunci API atau OAuth Default Menggunakan header x-goog-api-key
Google Gemini CLI OAuth GeminiCLI Menggunakan endpoint streamGenerateContent
Antigravity OAuth Antigravity Penggantian multi-URL, penguraian percobaan ulang kustom
OpenAI API key Default Autentikasi Bearer standar
Codex OAuth Codex Menyuntikkan instruksi sistem, mengelola berpikir
GitHub Copilot OAuth + token Copilot Github Token ganda, peniruan header VSCode
Kiro (AWS) AWS SSO OIDC atau Social Kiro Parsing binary EventStream
Cursor IDE Autentikasi checksum Cursor Encoding Protobuf, checksum SHA-256
Qwen OAuth Default Autentikasi standar
Qoder OAuth (Basic + Bearer) Default Autentikasi header ganda
OpenRouter API key Default Autentikasi Bearer standar
GLM, Kimi, MiniMax API key Default Kompatibel-Claude, menggunakan x-api-key
openai-compatible-* API key Default Dinamis: endpoint kompatibel-OpenAI apa pun
anthropic-compatible-* API key Default Dinamis: endpoint kompatibel-Claude apa pun

8. Ringkasan Alur Data

Permintaan Streaming

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()"]

Permintaan Non-Streaming

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"]

Alur Bypass (Claude CLI)

flowchart LR
    A["Claude CLI request"] --> B{"Match bypass\npattern?"}
    B -->|"Title/Warmup/Count"| C["Buat respons palsu\nOpenAI"]
    B -->|"No match"| D["Normal flow"]
    C --> E["Translate to\nsource format"]
    E --> F["Return without\ncalling provider"]