Add Magyar (hu) translation of the architecture documentation to support Hungarian-speaking contributors and users.
43 KiB
ð Languages: ðºðž English | ð§ð· Português (Brasil) | ðªðž Español | ð«ð· Français | ð®ð¹ Italiano | ð·ðº Ð ÑÑÑкОй | ðšð³ äžæ (ç®äœ) | ð©ðª Deutsch | ð®ð³ à€¹à€¿à€šà¥à€Šà¥ | ð¹ð à¹àžàž¢ | ðºðŠ Ð£ÐºÑаÑМÑÑка | ðžðŠ Ø§ÙØ¹Ø±ØšÙØ© | ð¯ðµ æ¥æ¬èª | ð»ð³ Tiếng Viá»t | ð§ð¬ ÐÑлгаÑÑкО | ð©ð° Dansk | ð«ð® Suomi | ð®ð± ×¢×ך×ת | ððº Magyar | ð®ð© Bahasa Indonesia | ð°ð· íêµìŽ | ð²ðŸ Bahasa Melayu | ð³ð± Nederlands | ð³ðŽ Norsk | ðµð¹ Português (Portugal) | ð·ðŽ RomânÄ | ðµð± Polski | ðžð° SlovenÄina | ðžðª Svenska | ðµð Filipino
#omniroute â ã³ãŒãããŒã¹ã®ããã¥ã¡ã³ã
omniroute ãã«ããããã€ã㌠AI ãããã· ã«ãŒã¿ãŒã«é¢ããåå¿è åãã®å æ¬çãªã¬ã€ãã
1. ãªã ãã«ãŒããšã¯äœã§ãã?
ãªã ãã«ãŒãã¯ãAI ã¯ã©ã€ã¢ã³ã (Claude CLIãCodexãCursor IDE ãªã©) ãš AI ãããã€ã㌠(AnthropicãGoogleãOpenAIãAWSãGitHub ãªã©) ã®éã«äœçœ®ãã ãããã· ã«ãŒã¿ãŒ ã§ããããã«ããã1 ã€ã®å€§ããªåé¡ã解決ãããŸãã
ç°ãªã AI ã¯ã©ã€ã¢ã³ãã¯ç°ãªããèšèªã(API 圢åŒ) ã話ããç°ãªã AI ãããã€ããŒãç°ãªããèšèªããæåŸ ããŸãã ãªã ãã«ãŒãã¯ãããã®éã§èªåçã«ç¿»èš³ããŸãã
ãããåœé£ã®äžèœç¿»èš³è ã®ãããªãã®ã ãšèããŠãã ãããã©ã®ä»£è¡šè ãããããèšèªã話ãããšãã§ãã翻蚳è ã¯ä»ã®ä»£è¡šè ã®ããã«ããã倿ããŸãã
2. ã¢ãŒããã¯ãã£ã®æŠèŠ
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 ãã©ã³ã¹ã¬ãŒã¿ãŒ (ãã©ãŒãããããšã« 1 人) ã ããå¿ èŠã§ããããšãæå³ããŸãã
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 |
äžå€®ã¢ãã« ã¬ãžã¹ããª: ãããã€ããŒã®ãšã€ãªã¢ã¹ â ã¢ãã« ID ããããããŸãã getModels()ãgetProviderByAlias() ã®ãããªé¢æ°ã |
codexInstructions.ts |
Codex ãªã¯ãšã¹ãã«æ¿å ¥ãããã·ã¹ãã åœä»€ (ç·šéå¶çŽããµã³ãããã¯ã¹ ã«ãŒã«ãæ¿èªããªã·ãŒ)ã |
defaultThinkingSignature.ts |
Claude ã¢ãã«ãš Gemini ã¢ãã«ã®ããã©ã«ãã®ãæèãã·ã°ããã£ã |
ollamaModels.ts |
ããŒã«ã« Ollama ã¢ãã«ã®ã¹ããŒãå®çŸ© (ååããµã€ãºããã¡ããªãŒãéåå)ã |
èªèšŒæ å ±ã®èªã¿èŸŒã¿ãããŒ
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/)
ãšã°ãŒãã¥ãŒã¿ã¯ãæŠç¥ãã¿ãŒã³ã䜿çšããŠãããã€ãåºæã®ããžãã¯ãã«ãã»ã«åããŸããåãšã°ãŒãã¥ãŒã¿ã¯ãå¿ èŠã«å¿ããŠåºæ¬ã¡ãœããããªãŒããŒã©ã€ãããŸãã
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 |
ã¯ããŒãããžã§ãããOpenAIãGLMããããMiniMax | æšæºãããã€ããŒã®æ±çš OAuth ããŒã¯ã³ã®æŽæ° |
antigravity.ts |
Googleã¯ã©ãŠãã³ãŒã | ãããžã§ã¯ã/ã»ãã·ã§ã³ ID ã®çæããã«ã URL ãã©ãŒã«ããã¯ããšã©ãŒ ã¡ãã»ãŒãžããã®ã«ã¹ã¿ã å詊è¡è§£æ (ã2 æé 7 å 23 ç§åŸã«ãªã»ããã) |
cursor.ts |
ã«ãŒãœã«IDE | æãè€é: SHA-256 ãã§ãã¯ãµã èªèšŒãProtobuf ãªã¯ãšã¹ã ãšã³ã³ãŒãããã€ã㪠EventStream â SSE ã¬ã¹ãã³ã¹è§£æ |
codex.ts |
OpenAI ã³ãŒããã¯ã¹ | ã·ã¹ãã åœä»€ã®æ¿å ¥ãæèã¬ãã«ã®ç®¡çããµããŒããããŠããªããã©ã¡ãŒã¿ã®åé€ |
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 |
OpenAI ã®å¿ç API çšã¢ããã¿ãŒ: å¿ç圢åŒã倿 â ãã£ããå®äº â chatCore ã«éä¿¡ â SSE ãå¿ç圢åŒã«å€æããŸãã |
embeddings.ts |
åã蟌ã¿çæãã³ãã©ãŒ: åã蟌ã¿ã¢ãã«âãããã€ããŒã解決ãããããã€ã㌠API ã«ãã£ã¹ãããããOpenAI äºæã®åã蟌ã¿å¿çãè¿ããŸãã 6 ã€ä»¥äžã®ãããã€ããŒããµããŒãããŸãã |
imageGeneration.ts |
ã€ã¡ãŒãžçæãã³ãã©ãŒ: ã€ã¡ãŒãž ã¢ãã« â ãããã€ããŒã解決ããOpenAI äºæãGemini ã€ã¡ãŒãž (Antigravity)ãããã³ãã©ãŒã«ãã㯠(Nebius) ã¢ãŒãããµããŒãããŸãã Base64 ãŸã㯠URL ã€ã¡ãŒãžãè¿ããŸãã |
ãªã¯ãšã¹ãã®ã©ã€ããµã€ã¯ã« (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 ãµãŒãã¹ (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"})ãè¡çªæ€åºã«ãããšã€ãªã¢ã¹è§£æ±ºãå
¥åãµãã¿ã€ãº (ãã¹ ãã©ããŒãµã«/å¶åŸ¡æåã®æåŠ)ãããã³éåæãšã€ãªã¢ã¹ ã²ãã¿ãŒ ãµããŒãã«ããã¢ãã«æ
å ±è§£æ±ºã |
accountFallback.ts |
ã¬ãŒãå¶éã®åŠç: ææ°é¢æ°çããã¯ãªã (1 ç§ â 2 ç§ â 4 ç§ â æå€§ 2 å)ãã¢ã«ãŠã³ãã®ã¯ãŒã«ããŠã³ç®¡çããšã©ãŒåé¡ (ã©ã®ãšã©ãŒããã©ãŒã«ããã¯ãããªã¬ãŒããã®ããããªã¬ãŒããªãã®ã)ã |
tokenRefresh.ts |
ãã¹ãŠã®ãããã€ãã® OAuth ããŒã¯ã³æŽæ°: Google (GeminiãAntigravity)ãClaudeãCodexãQwenãiFlowãGitHub (OAuth + Copilot ãã¥ã¢ã« ããŒã¯ã³)ãKiro (AWS SSO OIDC + Social Auth)ãå®è¡äžã® Promise éè€æé€ãã£ãã·ã¥ãšææ°ããã¯ãªãã«ããå詊è¡ãå«ãŸããŸãã |
combo.ts |
ã³ã³ã ã¢ãã«: ãã©ãŒã«ãã㯠ã¢ãã«ã®ãã§ãŒã³ãã¢ãã« A ããã©ãŒã«ããã¯å¯Ÿè±¡ãšã©ãŒã§å€±æããå Žåã¯ãã¢ãã« Bãæ¬¡ã«ã¢ãã« C ãªã©ã詊ããŸããå®éã®ã¢ããã¹ããªãŒã ã¹ããŒã¿ã¹ ã³ãŒããè¿ããŸãã |
usage.ts |
ãããã€ã㌠API ããã¯ã©ãŒã¿/䜿çšéããŒã¿ãååŸããŸã (GitHub Copilot ã¯ã©ãŒã¿ãåéåã¢ãã« ã¯ã©ãŒã¿ãCodex ã¬ãŒãå¶éãKiro 䜿çšéã®å èš³ãClaude èšå®)ã |
accountSelector.ts |
ã¹ã³ã¢ãªã³ã° ã¢ã«ãŽãªãºã ã䜿çšããã¹ããŒããªã¢ã«ãŠã³ãéžæ: åªå 床ãå¥å šæ§ã¹ããŒã¿ã¹ãã©ãŠã³ãããã³ ããžã·ã§ã³ãã¯ãŒã«ããŠã³ç¶æ ãèæ ®ããŠãåãªã¯ãšã¹ãã«æé©ãªã¢ã«ãŠã³ããéžæããŸãã |
contextManager.ts |
ãªã¯ãšã¹ã ã³ã³ããã¹ãã®ã©ã€ããµã€ã¯ã«ç®¡ç: ãããã°ãšãã®ã³ã°ã®ããã«ãã¡ã¿ããŒã¿ (ãªã¯ãšã¹ã IDãã¿ã€ã ã¹ã¿ã³ãããããã€ããŒæ å ±) ãå«ããªã¯ãšã¹ãããšã®ã³ã³ããã¹ã ãªããžã§ã¯ããäœæããã³è¿œè·¡ããŸãã |
ipFilter.ts |
IP ããŒã¹ã®ã¢ã¯ã»ã¹å¶åŸ¡: ãã¯ã€ããªã¹ã ã¢ãŒããšãããã¯ãªã¹ã ã¢ãŒãããµããŒãããŸãã API ãªã¯ãšã¹ããåŠçããåã«ãèšå®ãããã«ãŒã«ã«ç §ãããŠã¯ã©ã€ã¢ã³ã IP ãæ€èšŒããŸãã |
sessionManager.ts |
ã¯ã©ã€ã¢ã³ã ãã£ã³ã¬ãŒããªã³ãã«ããã»ãã·ã§ã³è¿œè·¡: ããã·ã¥ãããã¯ã©ã€ã¢ã³ã ID ã䜿çšããŠã¢ã¯ãã£ããªã»ãã·ã§ã³ã远跡ãããªã¯ãšã¹ãæ°ãç£èŠããã»ãã·ã§ã³ ã¡ããªãã¯ãæäŸããŸãã |
signatureCache.ts |
ãªã¯ãšã¹ã眲åããŒã¹ã®éè€æé€ãã£ãã·ã¥: æè¿ã®ãªã¯ãšã¹ã眲åããã£ãã·ã¥ããæéæ å ã®åäžãªã¯ãšã¹ãã«å¯ŸããŠãã£ãã·ã¥ãããå¿çãè¿ãããšã§ããªã¯ãšã¹ãã®éè€ãé²ããŸãã |
systemPrompt.ts |
ã°ããŒãã« ã·ã¹ãã ããã³ãã ã€ã³ãžã§ã¯ã·ã§ã³: ãããã€ããŒããšã®äºææ§åŠçã䜿çšããŠãæ§æå¯èœãªã·ã¹ãã ããã³ããããã¹ãŠã®ãªã¯ãšã¹ãã®å é ãŸãã¯æ«å°Ÿã«è¿œå ããŸãã |
thinkingBudget.ts |
æšè«ããŒã¯ã³ã®äºç®ç®¡ç: æè/æšè«ããŒã¯ã³ãå¶åŸ¡ããããã®ãã¹ã¹ã«ãŒãèªå (ã¹ããªããæèæ§æ)ãã«ã¹ã¿ã (åºå®äºç®)ãããã³é©å¿å (è€éãã¹ã±ãŒã«) ã¢ãŒãããµããŒãããŸãã |
wildcardRouter.ts |
ã¯ã€ã«ãã«ãŒã ã¢ãã« ãã¿ãŒã³ ã«ãŒãã£ã³ã°: å¯çšæ§ãšåªå
床ã«åºã¥ããŠãã¯ã€ã«ãã«ãŒã ãã¿ãŒã³ (*/claude-* ãªã©) ãå
·äœçãªãããã€ããŒ/ã¢ãã«ã®ãã¢ã«è§£æ±ºããŸãã |
ããŒã¯ã³ã®ãªãã¬ãã·ã¥ã®éè€æé€
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
ã¢ã«ãŠã³ã ãã©ãŒã«ãã㯠ã¹ããŒã ãã·ã³
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
}
ã³ã³ã ã¢ãã« ãã§ãŒã³
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/)
èªå·±ç»é²ãã©ã°ã€ã³ ã·ã¹ãã ã䜿çšãã ãã©ãŒããã倿ãšã³ãžã³ã
ã¢ãŒããã¯ãã£
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ã |
äž»ãªèšèš: èªå·±ç»é²ãã©ã°ã€ã³
// 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 â ã³ã¢ ã¹ããªãŒãã³ã° ãã€ãã©ã€ã³ã 2 ã€ã®ã¢ãŒã: 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 ã¹ããªãŒãã³ã° ãã€ãã©ã€ã³
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 |
GET/PUT | IP ãã¯ã€ããªã¹ã/ãããã¯ãªã¹ã管ç |
/api/settings/thinking-budget |
GET/PUT | æšè«ããŒã¯ã³ã®äºç®æ§æ (ãã¹ã¹ã«ãŒ/èªå/ã«ã¹ã¿ã /ã¢ãããã£ã) |
/api/settings/system-prompt |
GET/PUT | ãã¹ãŠã®ãªã¯ãšã¹ãã«å¯Ÿããã°ããŒãã« ã·ã¹ãã ããã³ãã ã€ã³ãžã§ã¯ã·ã§ã³ |
/api/sessions |
å ¥æ | ã¢ã¯ãã£ããªã»ãã·ã§ã³ã®è¿œè·¡ãšã¡ããªã¯ã¹ |
/api/rate-limits |
å ¥æ | ã¢ã«ãŠã³ãããšã®ã¬ãŒãå¶éã¹ããŒã¿ã¹ |
5. äž»èŠãªèšèšãã¿ãŒã³
5.1 ããã¢ã³ãã¹ããŒã¯å€æ
ãã¹ãŠã®åœ¢åŒã¯ OpenAI 圢åŒããããšããŠå€æããŸããæ°ãããããã€ããŒã远å ããã«ã¯ãN ãã¢ã§ã¯ãªãã1 ã㢠ã®ãã©ã³ã¹ã¬ãŒã¿ãŒ (OpenAI ãšã®é) ãäœæããã ãã§æžã¿ãŸãã
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 |
| Google ãžã§ãã | ãœãŒã¹ + ã¿ãŒã²ãã | gemini |
| Google Gemini CLI | ã¿ãŒã²ããã®ã¿ | gemini-cli |
| åéå | ãœãŒã¹ + ã¿ãŒã²ãã | antigravity |
| AWS ãã | ã¿ãŒã²ããã®ã¿ | kiro |
| ã«ãŒãœã« | ã¿ãŒã²ããã®ã¿ | cursor |
7. ãµããŒããããŠãããããã€ããŒ
| ãããã€ã㌠| èªèšŒæ¹æ³ | å·è¡è | éèŠãªã¡ã¢ |
|---|---|---|---|
| 人éã®ã¯ããŒã | API ããŒãŸã㯠OAuth | ããã©ã«ã | x-api-key ããããŒã䜿çšããŸãã |
| Google ãžã§ãã | API ããŒãŸã㯠OAuth | ããã©ã«ã | x-goog-api-key ããããŒã䜿çšããŸãã |
| Google Gemini CLI | OAuth | ãžã§ããCLI | streamGenerateContent ãšã³ããã€ã³ãã䜿çšããŸãã |
| åéå | OAuth | åéå | ãã«ã URL ãã©ãŒã«ããã¯ãã«ã¹ã¿ã å詊è¡è§£æ |
| ãªãŒãã³AI | APIã㌠| ããã©ã«ã | æšæºãã¢ã©ãŒèªèšŒ |
| ã³ãŒããã¯ã¹ | OAuth | ã³ãŒããã¯ã¹ | ã·ã¹ãã åœä»€ãæ³šå ¥ããæèã管çããŸã |
| GitHub ã³ãã€ããã | OAuth + ã³ãã€ããã ããŒã¯ã³ | ã®ãããã | ãã¥ã¢ã« ããŒã¯ã³ãVSCode ããããŒã®æš¡å£ |
| ãã (AWS) | AWS SSO OIDC ãŸãã¯ãœãŒã·ã£ã« | ãã | ãã€ã㪠EventStream è§£æ |
| ã«ãŒãœã«IDE | ãã§ãã¯ãµã èªèšŒ | ã«ãŒãœã« | Protobuf ãšã³ã³ãŒãã£ã³ã°ãSHA-256 ãã§ãã¯ãµã |
| ã¯ãŠã§ã³ | OAuth | ããã©ã«ã | æšæºèªèšŒ |
| iFlow | OAuth (ããŒã·ã㯠+ ãã¢ã©ãŒ) | ããã©ã«ã | ãã¥ã¢ã«èªèšŒããã㌠|
| ãªãŒãã³ã«ãŒã¿ãŒ | APIã㌠| ããã©ã«ã | æšæºãã¢ã©ãŒèªèšŒ |
| GLMããããããããã¯ã¹ | APIã㌠| ããã©ã«ã | Claude ãšäºææ§ããããããx-api-key ã䜿çšããŠãã ããã |
openai-compatible-* |
APIã㌠| ããã©ã«ã | åç: ä»»æã® OpenAI äºæãšã³ããã€ã³ã |
anthropic-compatible-* |
APIã㌠| ããã©ã«ã | åç: ã¯ããŒããšäºææ§ã®ããä»»æã®ãšã³ããã€ã³ã |
8. ããŒã¿ãããŒã®æŠèŠ
ã¹ããªãŒãã³ã°ãªã¯ãšã¹ã
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()"]
éã¹ããªãŒãã³ã°ãªã¯ãšã¹ã
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)
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"]