omniroute 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/app/.next/BUILD_ID +1 -1
  2. package/app/.next/build-manifest.json +2 -2
  3. package/app/.next/prerender-manifest.json +3 -3
  4. package/app/.next/server/app/(dashboard)/dashboard/a2a/page_client-reference-manifest.js +1 -1
  5. package/app/.next/server/app/(dashboard)/dashboard/agents/page_client-reference-manifest.js +1 -1
  6. package/app/.next/server/app/(dashboard)/dashboard/analytics/page_client-reference-manifest.js +1 -1
  7. package/app/.next/server/app/(dashboard)/dashboard/api-manager/page_client-reference-manifest.js +1 -1
  8. package/app/.next/server/app/(dashboard)/dashboard/audit-log/page_client-reference-manifest.js +1 -1
  9. package/app/.next/server/app/(dashboard)/dashboard/auto-combo/page_client-reference-manifest.js +1 -1
  10. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  11. package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  12. package/app/.next/server/app/(dashboard)/dashboard/costs/page_client-reference-manifest.js +1 -1
  13. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  14. package/app/.next/server/app/(dashboard)/dashboard/health/page_client-reference-manifest.js +1 -1
  15. package/app/.next/server/app/(dashboard)/dashboard/limits/page_client-reference-manifest.js +1 -1
  16. package/app/.next/server/app/(dashboard)/dashboard/logs/page_client-reference-manifest.js +1 -1
  17. package/app/.next/server/app/(dashboard)/dashboard/mcp/page_client-reference-manifest.js +1 -1
  18. package/app/.next/server/app/(dashboard)/dashboard/media/page_client-reference-manifest.js +1 -1
  19. package/app/.next/server/app/(dashboard)/dashboard/onboarding/page_client-reference-manifest.js +1 -1
  20. package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  21. package/app/.next/server/app/(dashboard)/dashboard/playground/page_client-reference-manifest.js +1 -1
  22. package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  23. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  24. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  25. package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  26. package/app/.next/server/app/(dashboard)/dashboard/search-tools/page_client-reference-manifest.js +1 -1
  27. package/app/.next/server/app/(dashboard)/dashboard/settings/page_client-reference-manifest.js +1 -1
  28. package/app/.next/server/app/(dashboard)/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  29. package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  30. package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  31. package/app/.next/server/app/400/page_client-reference-manifest.js +1 -1
  32. package/app/.next/server/app/401/page_client-reference-manifest.js +1 -1
  33. package/app/.next/server/app/403/page_client-reference-manifest.js +1 -1
  34. package/app/.next/server/app/408/page_client-reference-manifest.js +1 -1
  35. package/app/.next/server/app/429/page_client-reference-manifest.js +1 -1
  36. package/app/.next/server/app/500/page_client-reference-manifest.js +1 -1
  37. package/app/.next/server/app/502/page_client-reference-manifest.js +1 -1
  38. package/app/.next/server/app/503/page_client-reference-manifest.js +1 -1
  39. package/app/.next/server/app/_global-error.html +2 -2
  40. package/app/.next/server/app/_global-error.rsc +1 -1
  41. package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  42. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  43. package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  44. package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  45. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  46. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  47. package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
  48. package/app/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  49. package/app/.next/server/app/forbidden/page_client-reference-manifest.js +1 -1
  50. package/app/.next/server/app/forgot-password/page_client-reference-manifest.js +1 -1
  51. package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
  52. package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
  53. package/app/.next/server/app/maintenance/page_client-reference-manifest.js +1 -1
  54. package/app/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  55. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/app/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
  57. package/app/.next/server/app/status/page_client-reference-manifest.js +1 -1
  58. package/app/.next/server/app/terms/page_client-reference-manifest.js +1 -1
  59. package/app/.next/server/chunks/[root-of-the-server]__051203a6._.js +2 -2
  60. package/app/.next/server/chunks/[root-of-the-server]__0891af92._.js +1 -1
  61. package/app/.next/server/chunks/[root-of-the-server]__1f2b0d89._.js +1 -1
  62. package/app/.next/server/chunks/[root-of-the-server]__61d78f9d._.js +2 -2
  63. package/app/.next/server/chunks/[root-of-the-server]__6e52619e._.js +1 -1
  64. package/app/.next/server/chunks/[root-of-the-server]__afddb4ce._.js +1 -1
  65. package/app/.next/server/chunks/[root-of-the-server]__e27a89bd._.js +1 -1
  66. package/app/.next/server/chunks/[root-of-the-server]__fd2fbc93._.js +1 -1
  67. package/app/.next/server/chunks/_05c48915._.js +1 -1
  68. package/app/.next/server/chunks/_06515a8a._.js +1 -1
  69. package/app/.next/server/chunks/_2115d8de._.js +1 -1
  70. package/app/.next/server/chunks/_3ac953eb._.js +1 -1
  71. package/app/.next/server/chunks/_4b8fd853._.js +1 -1
  72. package/app/.next/server/chunks/_68683848._.js +1 -1
  73. package/app/.next/server/chunks/_6f1b3c3f._.js +1 -1
  74. package/app/.next/server/chunks/_ee9b677b._.js +1 -1
  75. package/app/.next/server/chunks/_efd5ede2._.js +2 -2
  76. package/app/.next/server/chunks/_ffda39da._.js +1 -1
  77. package/app/.next/server/chunks/open-sse_translator_index_ts_f5fd0821._.js +1 -1
  78. package/app/.next/server/chunks/src_6320c728._.js +1 -1
  79. package/app/.next/server/chunks/ssr/[root-of-the-server]__9ef96d20._.js +1 -1
  80. package/app/.next/server/chunks/ssr/[root-of-the-server]__a6942102._.js +1 -1
  81. package/app/.next/server/chunks/ssr/_f3674909._.js +1 -1
  82. package/app/.next/server/chunks/ssr/src_a82a42f9._.js +1 -1
  83. package/app/.next/server/chunks/ssr/src_app_(dashboard)_dashboard_936a9ee0._.js +1 -1
  84. package/app/.next/server/chunks/ssr/src_app_(dashboard)_dashboard_settings_9e20fb8d._.js +1 -1
  85. package/app/.next/server/pages/500.html +2 -2
  86. package/app/.next/server/server-reference-manifest.js +1 -1
  87. package/app/.next/server/server-reference-manifest.json +1 -1
  88. package/app/.next/static/chunks/26aeb12cf79339b1.js +1 -0
  89. package/app/.next/static/chunks/{80658e0c2b2c7004.js → 287f50749d5655ea.js} +1 -1
  90. package/app/.next/static/chunks/7df0b3c357097db5.js +1 -0
  91. package/app/.next/static/chunks/a051245b46459ad2.css +1 -0
  92. package/app/.next/static/chunks/d188e358e1ec0a7d.js +1 -0
  93. package/app/.next/static/chunks/{4e3fe685e3218d24.js → fa9e2a946d2cdbc7.js} +1 -1
  94. package/app/CHANGELOG.md +148 -68
  95. package/app/docs/i18n/ru/README.md +3 -3
  96. package/app/docs/openapi.yaml +1 -1
  97. package/app/open-sse/handlers/chatCore.ts +9 -2
  98. package/app/open-sse/handlers/responseTranslator.ts +13 -3
  99. package/app/open-sse/services/usage.ts +22 -9
  100. package/app/open-sse/translator/request/openai-to-claude.ts +10 -2
  101. package/app/package-lock.json +2 -2
  102. package/app/package.json +1 -1
  103. package/app/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +198 -63
  104. package/app/src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx +123 -19
  105. package/app/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx +22 -24
  106. package/app/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx +5 -0
  107. package/app/src/lib/providers/validation.ts +21 -0
  108. package/app/src/shared/components/ProxyConfigModal.tsx +42 -13
  109. package/package.json +1 -1
  110. package/app/.next/static/chunks/594bdf69c19b5310.js +0 -1
  111. package/app/.next/static/chunks/643428fcd3f9bb51.js +0 -1
  112. package/app/.next/static/chunks/8efe14530c87d9f6.css +0 -1
  113. package/app/.next/static/chunks/d0cff7a6c3a66c7d.js +0 -1
  114. /package/app/.next/static/{cQZnxkFFvb8O7T4x7OSsG → m-GCTx-KZwpGhw6wBIOzp}/_buildManifest.js +0 -0
  115. /package/app/.next/static/{cQZnxkFFvb8O7T4x7OSsG → m-GCTx-KZwpGhw6wBIOzp}/_clientMiddlewareManifest.json +0 -0
  116. /package/app/.next/static/{cQZnxkFFvb8O7T4x7OSsG → m-GCTx-KZwpGhw6wBIOzp}/_ssgManifest.js +0 -0
package/app/CHANGELOG.md CHANGED
@@ -4,6 +4,79 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## [3.0.2] — 2026-03-25
8
+
9
+ ### 🚀 Enhancements & Features
10
+
11
+ #### feat(ui): Connection Tag Grouping
12
+
13
+ - Added a Tag/Group field to `EditConnectionModal` (stored in `providerSpecificData.tag`) without requiring DB schema migrations.
14
+ - Connections in the provider view now dynamically group by tag with visual dividers.
15
+ - Untagged connections appear first without a header, followed by tagged groups in alphabetical order.
16
+ - The tag grouping automatically applies to the Codex/Copilot/Antigravity Limits section since toggles exist inside connection rows.
17
+
18
+ ### 🐛 Bug Fixes
19
+
20
+ #### fix(ui): Proxy Management UI Stabilization
21
+
22
+ - **Missing badges on connection cards:** Fixed by using `resolveProxyForConnection()` rather than static mapping.
23
+ - **Test Connection disabled in saved mode:** Enabled the Test button by resolving proxy config from the saved list.
24
+ - **Config Modal freezing:** Added `onClose()` calls after save/clear to prevent the UI from freezing.
25
+ - **Double usage counting:** `ProxyRegistryManager` now loads usage eagerly on mount with deduplication by `scope` + `scopeId`. Usage counts were replaced with a Test button displaying IP/latency inline.
26
+
27
+ #### fix(translator): `function_call` prefix stripping
28
+
29
+ - Repaired an incomplete fix from PR #607 where only `tool_use` blocks stripped Claude's `proxy_` tool prefix. Now, clients using the OpenAI Responses API format will also correctly receive tool tools without the `proxy_` prefix.
30
+
31
+ ---
32
+
33
+ ## [3.0.1] — 2026-03-25
34
+
35
+ ### 🔧 Hotfix Patch — Critical Bug Fixes
36
+
37
+ Three critical regressions reported by users after the v3.0.0 launch have been resolved.
38
+
39
+ #### fix(translator): strip `proxy_` prefix in non-streaming Claude responses (#605)
40
+
41
+ The `proxy_` prefix added by Claude OAuth was only stripped from **streaming** responses. In **non-streaming** mode, `translateNonStreamingResponse` had no access to the `toolNameMap`, causing clients to receive mangled tool names like `proxy_read_file` instead of `read_file`.
42
+
43
+ **Fix:** Added optional `toolNameMap` parameter to `translateNonStreamingResponse` and applied prefix stripping in the Claude `tool_use` block handler. `chatCore.ts` now passes the map through.
44
+
45
+ #### fix(validation): add LongCat specialty validator to skip /models probe (#592)
46
+
47
+ LongCat AI does not expose `GET /v1/models`. The generic `validateOpenAICompatibleProvider` validator fell through to a chat-completions fallback only if `validationModelId` was set, which LongCat doesn't configure. This caused provider validation to fail with a misleading error on add/save.
48
+
49
+ **Fix:** Added `longcat` to the specialty validators map, probing `/chat/completions` directly and treating any non-auth response as a pass.
50
+
51
+ #### fix(translator): normalize object tool schemas for Anthropic (#595)
52
+
53
+ MCP tools (e.g. `pencil`, `computer_use`) forward tool definitions with `{type:"object"}` but without a `properties` field. Anthropic's API rejects these with: `object schema missing properties`.
54
+
55
+ **Fix:** In `openai-to-claude.ts`, inject `properties: {}` as a safe default when `type` is `"object"` and `properties` is absent.
56
+
57
+ ---
58
+
59
+ ### 🔀 Community PRs Merged (2)
60
+
61
+ | PR | Author | Summary |
62
+ | -------- | ------- | -------------------------------------------------------------------------- |
63
+ | **#589** | @flobo3 | docs(i18n): fix Russian translation for Playground and Testbed |
64
+ | **#591** | @rdself | fix(ui): improve Provider Limits light mode contrast and plan tier display |
65
+
66
+ ---
67
+
68
+ ### ✅ Issues Resolved
69
+
70
+ `#592` `#595` `#605`
71
+
72
+ ---
73
+
74
+ ### 🧪 Tests
75
+
76
+ - **926 tests, 0 failures** (unchanged from v3.0.0)
77
+
78
+ ---
79
+
7
80
  ## [3.0.0] — 2026-03-24
8
81
 
9
82
  ### 🎉 OmniRoute v3.0.0 — The Free AI Gateway, Now with 67+ Providers
@@ -16,39 +89,39 @@
16
89
 
17
90
  ### 🆕 New Providers (+31 since v2.9.5)
18
91
 
19
- | Provider | Alias | Tier | Notes |
20
- |----------|-------|------|-------|
21
- | **OpenCode Zen** | `opencode-zen` | Free | 3 models via `opencode.ai/zen/v1` (PR #530 by @kang-heewon) |
22
- | **OpenCode Go** | `opencode-go` | Paid | 4 models via `opencode.ai/zen/go/v1` (PR #530 by @kang-heewon) |
23
- | **LongCat AI** | `lc` | Free | 50M tokens/day (Flash-Lite) + 500K/day (Chat/Thinking) during public beta |
24
- | **Pollinations AI** | `pol` | Free | No API key needed — GPT-5, Claude, Gemini, DeepSeek V3, Llama 4 (1 req/15s) |
25
- | **Cloudflare Workers AI** | `cf` | Free | 10K Neurons/day — ~150 LLM responses or 500s Whisper audio, edge inference |
26
- | **Scaleway AI** | `scw` | Free | 1M free tokens for new accounts — EU/GDPR compliant (Paris) |
27
- | **AI/ML API** | `aiml` | Free | $0.025/day free credits — 200+ models via single endpoint |
28
- | **Puter AI** | `pu` | Free | 500+ models (GPT-5, Claude Opus 4, Gemini 3 Pro, Grok 4, DeepSeek V3) |
29
- | **Alibaba Cloud (DashScope)** | `ali` | Paid | International + China endpoints via `alicode`/`alicode-intl` |
30
- | **Alibaba Coding Plan** | `bcp` | Paid | Alibaba Model Studio with Anthropic-compatible API |
31
- | **Kimi Coding (API Key)** | `kmca` | Paid | Dedicated API-key-based Kimi access (separate from OAuth) |
32
- | **MiniMax Coding** | `minimax` | Paid | International endpoint |
33
- | **MiniMax (China)** | `minimax-cn` | Paid | China-specific endpoint |
34
- | **Z.AI (GLM-5)** | `zai` | Paid | Zhipu AI next-gen GLM models |
35
- | **Vertex AI** | `vertex` | Paid | Google Cloud — Service Account JSON or OAuth access_token |
36
- | **Ollama Cloud** | `ollamacloud` | Paid | Ollama's hosted API service |
37
- | **Synthetic** | `synthetic` | Paid | Passthrough models gateway |
38
- | **Kilo Gateway** | `kg` | Paid | Passthrough models gateway |
39
- | **Perplexity Search** | `pplx-search` | Paid | Dedicated search-grounded endpoint |
40
- | **Serper Search** | `serper-search` | Paid | Web search API integration |
41
- | **Brave Search** | `brave-search` | Paid | Brave Search API integration |
42
- | **Exa Search** | `exa-search` | Paid | Neural search API integration |
43
- | **Tavily Search** | `tavily-search` | Paid | AI search API integration |
44
- | **NanoBanana** | `nb` | Paid | Image generation API |
45
- | **ElevenLabs** | `el` | Paid | Text-to-speech voice synthesis |
46
- | **Cartesia** | `cartesia` | Paid | Ultra-fast TTS voice synthesis |
47
- | **PlayHT** | `playht` | Paid | Voice cloning and TTS |
48
- | **Inworld** | `inworld` | Paid | AI character voice chat |
49
- | **SD WebUI** | `sdwebui` | Self-hosted | Stable Diffusion local image generation |
50
- | **ComfyUI** | `comfyui` | Self-hosted | ComfyUI local workflow node-based generation |
51
- | **GLM Coding** | `glm` | Paid | BigModel/Zhipu coding-specific endpoint |
92
+ | Provider | Alias | Tier | Notes |
93
+ | ----------------------------- | --------------- | ----------- | --------------------------------------------------------------------------- |
94
+ | **OpenCode Zen** | `opencode-zen` | Free | 3 models via `opencode.ai/zen/v1` (PR #530 by @kang-heewon) |
95
+ | **OpenCode Go** | `opencode-go` | Paid | 4 models via `opencode.ai/zen/go/v1` (PR #530 by @kang-heewon) |
96
+ | **LongCat AI** | `lc` | Free | 50M tokens/day (Flash-Lite) + 500K/day (Chat/Thinking) during public beta |
97
+ | **Pollinations AI** | `pol` | Free | No API key needed — GPT-5, Claude, Gemini, DeepSeek V3, Llama 4 (1 req/15s) |
98
+ | **Cloudflare Workers AI** | `cf` | Free | 10K Neurons/day — ~150 LLM responses or 500s Whisper audio, edge inference |
99
+ | **Scaleway AI** | `scw` | Free | 1M free tokens for new accounts — EU/GDPR compliant (Paris) |
100
+ | **AI/ML API** | `aiml` | Free | $0.025/day free credits — 200+ models via single endpoint |
101
+ | **Puter AI** | `pu` | Free | 500+ models (GPT-5, Claude Opus 4, Gemini 3 Pro, Grok 4, DeepSeek V3) |
102
+ | **Alibaba Cloud (DashScope)** | `ali` | Paid | International + China endpoints via `alicode`/`alicode-intl` |
103
+ | **Alibaba Coding Plan** | `bcp` | Paid | Alibaba Model Studio with Anthropic-compatible API |
104
+ | **Kimi Coding (API Key)** | `kmca` | Paid | Dedicated API-key-based Kimi access (separate from OAuth) |
105
+ | **MiniMax Coding** | `minimax` | Paid | International endpoint |
106
+ | **MiniMax (China)** | `minimax-cn` | Paid | China-specific endpoint |
107
+ | **Z.AI (GLM-5)** | `zai` | Paid | Zhipu AI next-gen GLM models |
108
+ | **Vertex AI** | `vertex` | Paid | Google Cloud — Service Account JSON or OAuth access_token |
109
+ | **Ollama Cloud** | `ollamacloud` | Paid | Ollama's hosted API service |
110
+ | **Synthetic** | `synthetic` | Paid | Passthrough models gateway |
111
+ | **Kilo Gateway** | `kg` | Paid | Passthrough models gateway |
112
+ | **Perplexity Search** | `pplx-search` | Paid | Dedicated search-grounded endpoint |
113
+ | **Serper Search** | `serper-search` | Paid | Web search API integration |
114
+ | **Brave Search** | `brave-search` | Paid | Brave Search API integration |
115
+ | **Exa Search** | `exa-search` | Paid | Neural search API integration |
116
+ | **Tavily Search** | `tavily-search` | Paid | AI search API integration |
117
+ | **NanoBanana** | `nb` | Paid | Image generation API |
118
+ | **ElevenLabs** | `el` | Paid | Text-to-speech voice synthesis |
119
+ | **Cartesia** | `cartesia` | Paid | Ultra-fast TTS voice synthesis |
120
+ | **PlayHT** | `playht` | Paid | Voice cloning and TTS |
121
+ | **Inworld** | `inworld` | Paid | AI character voice chat |
122
+ | **SD WebUI** | `sdwebui` | Self-hosted | Stable Diffusion local image generation |
123
+ | **ComfyUI** | `comfyui` | Self-hosted | ComfyUI local workflow node-based generation |
124
+ | **GLM Coding** | `glm` | Paid | BigModel/Zhipu coding-specific endpoint |
52
125
 
53
126
  **Total: 67+ providers** (4 Free, 8 OAuth, 55 API Key) + unlimited OpenAI/Anthropic-Compatible custom providers.
54
127
 
@@ -60,15 +133,15 @@
60
133
 
61
134
  Auto-generate and issue OmniRoute API keys programmatically with per-provider and per-account quota enforcement.
62
135
 
63
- | Endpoint | Method | Description |
64
- |----------|--------|-------------|
65
- | `/api/v1/registered-keys` | `POST` | Issue a new key — raw key returned **once only** |
66
- | `/api/v1/registered-keys` | `GET` | List registered keys (masked) |
67
- | `/api/v1/registered-keys/{id}` | `GET/DELETE` | Get metadata / Revoke |
68
- | `/api/v1/quotas/check` | `GET` | Pre-validate quota before issuing |
69
- | `/api/v1/providers/{id}/limits` | `GET/PUT` | Configure per-provider issuance limits |
70
- | `/api/v1/accounts/{id}/limits` | `GET/PUT` | Configure per-account issuance limits |
71
- | `/api/v1/issues/report` | `POST` | Report quota events to GitHub Issues |
136
+ | Endpoint | Method | Description |
137
+ | ------------------------------- | ------------ | ------------------------------------------------ |
138
+ | `/api/v1/registered-keys` | `POST` | Issue a new key — raw key returned **once only** |
139
+ | `/api/v1/registered-keys` | `GET` | List registered keys (masked) |
140
+ | `/api/v1/registered-keys/{id}` | `GET/DELETE` | Get metadata / Revoke |
141
+ | `/api/v1/quotas/check` | `GET` | Pre-validate quota before issuing |
142
+ | `/api/v1/providers/{id}/limits` | `GET/PUT` | Configure per-provider issuance limits |
143
+ | `/api/v1/accounts/{id}/limits` | `GET/PUT` | Configure per-account issuance limits |
144
+ | `/api/v1/issues/report` | `POST` | Report quota events to GitHub Issues |
72
145
 
73
146
  **Security:** Keys stored as SHA-256 hashes. Raw key shown once on creation, never retrievable again.
74
147
 
@@ -83,6 +156,7 @@ Auto-refreshes model lists for connected providers every **24 hours**. Runs on s
83
156
  #### 🔀 Per-Model Combo Routing (#563)
84
157
 
85
158
  Map model name patterns (glob) to specific combos for automatic routing:
159
+
86
160
  - `claude-sonnet*` → code-combo, `gpt-4o*` → openai-combo, `gemini-*` → google-combo
87
161
  - New `model_combo_mappings` table with glob-to-regex matching
88
162
  - Dashboard UI section: "Model Routing Rules" with inline add/edit/toggle/delete
@@ -122,12 +196,14 @@ Full media generation playground at `/dashboard/media`: Image Generation, Video,
122
196
  ### 🐛 Bug Fixes (40+)
123
197
 
124
198
  #### OAuth & Auth
199
+
125
200
  - **#537** — Gemini CLI OAuth: clear actionable error when `GEMINI_OAUTH_CLIENT_SECRET` missing in Docker
126
201
  - **#549** — CLI settings routes now resolve real API key from `keyId` (not masked strings)
127
202
  - **#574** — Login no longer freezes after skipping wizard password setup
128
203
  - **#506** — Cross-platform `machineId` rewritten (Windows REG.exe → macOS ioreg → Linux → hostname fallback)
129
204
 
130
205
  #### Providers & Routing
206
+
131
207
  - **#536** — LongCat AI: fixed `baseUrl` and `authHeader`
132
208
  - **#535** — Pinned model override: `body.model` correctly set to `pinnedModel`
133
209
  - **#570** — Unprefixed Claude models now resolve to Anthropic provider
@@ -137,6 +213,7 @@ Full media generation playground at `/dashboard/media`: Image Generation, Video,
137
213
  - **#511** — `<omniModel>` tag injected into first content chunk (not after `[DONE]`)
138
214
 
139
215
  #### CLI & Tools
216
+
140
217
  - **#527** — Claude Code + Codex loop: `tool_result` blocks now converted to text
141
218
  - **#524** — OpenCode config saved correctly (XDG_CONFIG_HOME, TOML format)
142
219
  - **#522** — API Manager: removed misleading "Copy masked key" button
@@ -146,12 +223,14 @@ Full media generation playground at `/dashboard/media`: Image Generation, Video,
146
223
  - **#492** — CLI detects `mise`/`nvm`-managed Node when `app/server.js` missing
147
224
 
148
225
  #### Streaming & SSE
226
+
149
227
  - **PR #587** — Revert `resolveDataDir` import in responsesTransformer for Cloudflare Workers compat (@k0valik)
150
228
  - **PR #495** — Bottleneck 429 infinite wait: drop waiting jobs on rate limit (@xandr0s)
151
229
  - **#483** — Stop trailing `data: null` after `[DONE]` signal
152
230
  - **#473** — Zombie SSE streams: timeout reduced 300s → 120s for faster fallback
153
231
 
154
232
  #### Media & Transcription
233
+
155
234
  - **Transcription** — Deepgram `video/mp4` → `audio/mp4` MIME mapping, auto language detection, punctuation
156
235
  - **TTS** — `[object Object]` error display fixed for ElevenLabs-style nested errors
157
236
  - **Upload limits** — Media transcription increased to 2GB (nginx `client_max_body_size 2g` + `maxDuration=300`)
@@ -214,27 +293,27 @@ Full media generation playground at `/dashboard/media`: Image Generation, Video,
214
293
 
215
294
  ### 🔀 Community PRs Merged (10)
216
295
 
217
- | PR | Author | Summary |
218
- |----|--------|---------|
219
- | **#587** | @k0valik | fix(sse): revert resolveDataDir import for Cloudflare Workers compat |
220
- | **#582** | @jay77721 | feat(proxy): model name prefix stripping option |
221
- | **#581** | @jay77721 | fix(npm): link electron-release to npm-publish workflow |
222
- | **#578** | @hijak | feat: configurable context length in model metadata |
223
- | **#575** | @zhangqiang8vip | feat: per-model upstream headers, compat PATCH, chat alignment |
224
- | **#562** | @coobabm | fix: MCP session management, Claude passthrough, detectFormat |
225
- | **#561** | @zen0bit | fix(i18n): Czech translation corrections |
226
- | **#555** | @k0valik | fix(sse): centralized `resolveDataDir()` for path resolution |
227
- | **#546** | @k0valik | fix(cli): `--version` returning `unknown` on Windows |
228
- | **#544** | @k0valik | fix(cli): secure CLI tool detection via installation paths |
229
- | **#542** | @rdself | fix(ui): light mode contrast CSS theme variables |
230
- | **#530** | @kang-heewon | feat: OpenCode Zen + Go providers with `OpencodeExecutor` |
231
- | **#512** | @zhangqiang8vip | feat: per-protocol model compatibility (`compatByProtocol`) |
232
- | **#497** | @zhangqiang8vip | fix: dev-mode HMR resource leaks (ZWS v5) |
233
- | **#495** | @xandr0s | fix: Bottleneck 429 infinite wait (drop waiting jobs) |
234
- | **#494** | @zhangqiang8vip | feat: MiniMax developer→system role fix |
235
- | **#480** | @prakersh | fix: stream flush usage extraction |
236
- | **#479** | @prakersh | feat: Codex 5.3/5.4 and Anthropic pricing entries |
237
- | **#475** | @only4copilot | feat(i18n): improved Chinese translation |
296
+ | PR | Author | Summary |
297
+ | -------- | --------------- | -------------------------------------------------------------------- |
298
+ | **#587** | @k0valik | fix(sse): revert resolveDataDir import for Cloudflare Workers compat |
299
+ | **#582** | @jay77721 | feat(proxy): model name prefix stripping option |
300
+ | **#581** | @jay77721 | fix(npm): link electron-release to npm-publish workflow |
301
+ | **#578** | @hijak | feat: configurable context length in model metadata |
302
+ | **#575** | @zhangqiang8vip | feat: per-model upstream headers, compat PATCH, chat alignment |
303
+ | **#562** | @coobabm | fix: MCP session management, Claude passthrough, detectFormat |
304
+ | **#561** | @zen0bit | fix(i18n): Czech translation corrections |
305
+ | **#555** | @k0valik | fix(sse): centralized `resolveDataDir()` for path resolution |
306
+ | **#546** | @k0valik | fix(cli): `--version` returning `unknown` on Windows |
307
+ | **#544** | @k0valik | fix(cli): secure CLI tool detection via installation paths |
308
+ | **#542** | @rdself | fix(ui): light mode contrast CSS theme variables |
309
+ | **#530** | @kang-heewon | feat: OpenCode Zen + Go providers with `OpencodeExecutor` |
310
+ | **#512** | @zhangqiang8vip | feat: per-protocol model compatibility (`compatByProtocol`) |
311
+ | **#497** | @zhangqiang8vip | fix: dev-mode HMR resource leaks (ZWS v5) |
312
+ | **#495** | @xandr0s | fix: Bottleneck 429 infinite wait (drop waiting jobs) |
313
+ | **#494** | @zhangqiang8vip | feat: MiniMax developer→system role fix |
314
+ | **#480** | @prakersh | fix: stream flush usage extraction |
315
+ | **#479** | @prakersh | feat: Codex 5.3/5.4 and Anthropic pricing entries |
316
+ | **#475** | @only4copilot | feat(i18n): improved Chinese translation |
238
317
 
239
318
  **Thank you to all contributors!** 🙏
240
319
 
@@ -255,11 +334,11 @@ Full media generation playground at `/dashboard/media`: Image Generation, Video,
255
334
 
256
335
  ### 📦 Database Migrations
257
336
 
258
- | Migration | Description |
259
- |-----------|-------------|
260
- | **008** | `registered_keys`, `provider_key_limits`, `account_key_limits` tables |
261
- | **009** | `requested_model` column in `call_logs` |
262
- | **010** | `model_combo_mappings` table for per-model combo routing |
337
+ | Migration | Description |
338
+ | --------- | --------------------------------------------------------------------- |
339
+ | **008** | `registered_keys`, `provider_key_limits`, `account_key_limits` tables |
340
+ | **009** | `requested_model` column in `call_logs` |
341
+ | **010** | `model_combo_mappings` table for per-model combo routing |
263
342
 
264
343
  ---
265
344
 
@@ -310,6 +389,7 @@ docker pull diegosouzapw/omniroute:3.0.0
310
389
  ## [3.0.0-rc.16] — 2026-03-24
311
390
 
312
391
  ### ✨ New Features
392
+
313
393
  - Increased media transcription limits
314
394
  - Added Model Context Length to registry metadata
315
395
  - Added per-model upstream custom headers via configuration UI
@@ -386,7 +386,7 @@ Claude Code, Codex, Gemini CLI, Copilot — все используют OAuth 2.
386
386
  - **Панель управления унифицированными журналами** — 4 вкладки: журналы запросов, журналы прокси, журналы аудита, консоль.
387
387
  - **Консольный просмотр журнала** — просмотрщик в режиме терминала в режиме реального времени с уровнями с цветовой кодировкой, автоматической прокруткой, поиском и фильтрацией.
388
388
  - **Журналы прокси-сервера SQLite** — постоянные журналы, сохраняющиеся после перезапуска сервера.
389
- - **Площадка переводчика** — 4 режима отладки: Площадка (перевод формата), Тестер чата (туда и обратно), Тестовый стенд (пакетный), Мониторинг в реальном времени (в режиме реального времени).
389
+ - **Площадка транслятора (Translator Playground)** — 4 режима отладки: Площадка (перевод формата), Тестер чата (туда и обратно), Тестовый стенд (пакетный), Мониторинг в реальном времени (в режиме реального времени).
390
390
  - **Запрос телеметрии** — задержка p50/p95/p99 + отслеживание X-Request-Id
391
391
  - **Журналирование на основе файлов с ротацией** — перехватчик консоли записывает все в журнал JSON с ротацией на основе размера.
392
392
 
@@ -451,7 +451,7 @@ Claude Code, Codex, Gemini CLI, Copilot — все используют OAuth 2.
451
451
 
452
452
  - **Оценки LLM** — тестирование золотого набора с 10 предварительно загруженными вариантами, охватывающими приветствия, математику, географию, генерацию кода, соответствие JSON, перевод, уценку, отказ от безопасности.
453
453
  - **4 стратегии сопоставления** — `exact`, `contains`, `regex`, `custom` (функция JS)
454
- - **Тестовый стенд Translator Playground** — пакетное тестирование с несколькими входными данными и ожидаемыми результатами, сравнение между поставщиками.
454
+ - **Тестовый стенд (Testbed)** — пакетное тестирование с несколькими входными данными и ожидаемыми результатами, сравнение между поставщиками.
455
455
  - **Тестер чата** — полный цикл с визуальным отображением ответов.
456
456
  - **Живой монитор** — поток всех запросов, проходящих через прокси, в реальном времени.
457
457
 
@@ -1016,7 +1016,7 @@ OmniRoute включает встроенный фреймворк оценки
1016
1016
 
1017
1017
  - Приветствия, математика, география, генерация кода
1018
1018
  - Соответствие формату JSON, перевод, markdown
1019
- - Отказ от небезопасного контента, подсчёт, булева логика
1019
+ - Отказ от небезопасного контента (Safety refusal), подсчёт, булева логика
1020
1020
 
1021
1021
  ### Стратегии оценки
1022
1022
 
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OmniRoute API
4
- version: 3.0.0
4
+ version: 3.0.2
5
5
  description: |
6
6
  OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
7
7
  endpoint that routes requests to multiple AI providers with load balancing,
@@ -223,7 +223,8 @@ export async function handleChatCore({
223
223
 
224
224
  const endpointPath = String(clientRawRequest?.endpoint || "");
225
225
  const sourceFormat = detectFormatFromEndpoint(body, endpointPath);
226
- const isResponsesEndpoint = /\/responses(?=\/|$)/i.test(endpointPath) || /^responses(?=\/|$)/i.test(endpointPath);
226
+ const isResponsesEndpoint =
227
+ /\/responses(?=\/|$)/i.test(endpointPath) || /^responses(?=\/|$)/i.test(endpointPath);
227
228
  const nativeCodexPassthrough = shouldUseNativeCodexPassthrough({
228
229
  provider,
229
230
  sourceFormat,
@@ -1067,8 +1068,14 @@ export async function handleChatCore({
1067
1068
  }
1068
1069
 
1069
1070
  // Translate response to client's expected format (usually OpenAI)
1071
+ // Pass toolNameMap so Claude OAuth proxy_ prefix is stripped in tool_use blocks (#605)
1070
1072
  let translatedResponse = needsTranslation(targetFormat, sourceFormat)
1071
- ? translateNonStreamingResponse(responseBody, targetFormat, sourceFormat)
1073
+ ? translateNonStreamingResponse(
1074
+ responseBody,
1075
+ targetFormat,
1076
+ sourceFormat,
1077
+ toolNameMap as Map<string, string> | null
1078
+ )
1072
1079
  : responseBody;
1073
1080
 
1074
1081
  // T26: Strip markdown code blocks if provider format is Claude
@@ -68,11 +68,14 @@ function findBestMessageText(output: unknown[]): {
68
68
  /**
69
69
  * Translate non-streaming response to OpenAI format
70
70
  * Handles different provider response formats (Gemini, Claude, etc.)
71
+ *
72
+ * @param toolNameMap - Optional Map<prefixedName, originalName> for Claude OAuth tool name stripping
71
73
  */
72
74
  export function translateNonStreamingResponse(
73
75
  responseBody: unknown,
74
76
  targetFormat: string,
75
- sourceFormat: string
77
+ sourceFormat: string,
78
+ toolNameMap?: Map<string, string> | null
76
79
  ): unknown {
77
80
  // If already in source format (usually OpenAI), return as-is
78
81
  if (targetFormat === sourceFormat || targetFormat === FORMATS.OPENAI) {
@@ -122,11 +125,14 @@ export function translateNonStreamingResponse(
122
125
  typeof itemObj.arguments === "string"
123
126
  ? itemObj.arguments
124
127
  : JSON.stringify(itemObj.arguments || {});
128
+ const rawName = toString(itemObj.name);
129
+ // Strip Claude OAuth proxy_ prefix using toolNameMap (mirrors tool_use fix for #605)
130
+ const resolvedName = toolNameMap?.get(rawName) ?? rawName;
125
131
  toolCalls.push({
126
132
  id: callId,
127
133
  type: "function",
128
134
  function: {
129
- name: toString(itemObj.name),
135
+ name: resolvedName,
130
136
  arguments: fnArgs,
131
137
  },
132
138
  });
@@ -334,11 +340,15 @@ export function translateNonStreamingResponse(
334
340
  } else if (blockObj.type === "thinking") {
335
341
  thinkingContent += toString(blockObj.thinking);
336
342
  } else if (blockObj.type === "tool_use") {
343
+ // Strip Claude OAuth tool name prefix (proxy_) using the map from request translation.
344
+ // Fallback to raw name if block wasn't prefixed (disableToolPrefix path).
345
+ const rawName = toString(blockObj.name);
346
+ const strippedName = toolNameMap?.get(rawName) ?? rawName;
337
347
  toolCalls.push({
338
348
  id: toString(blockObj.id, `call_${Date.now()}_${toolCalls.length}`),
339
349
  type: "function",
340
350
  function: {
341
- name: toString(blockObj.name),
351
+ name: strippedName,
342
352
  arguments: JSON.stringify(blockObj.input || {}),
343
353
  },
344
354
  });
@@ -86,7 +86,8 @@ function toDisplayLabel(value: string): string {
86
86
  .filter(Boolean)
87
87
  .map((part) => {
88
88
  if (/^pro\+$/i.test(part)) return "Pro+";
89
- if (/^[a-z]{2,}$/.test(part)) return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
89
+ if (/^[a-z]{2,}$/.test(part))
90
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
90
91
  return part;
91
92
  })
92
93
  .join(" ")
@@ -200,7 +201,9 @@ async function getGitHubUsage(accessToken, providerSpecificData) {
200
201
  if (dataRecord.quota_snapshots) {
201
202
  // Paid plan format
202
203
  const snapshots = toRecord(dataRecord.quota_snapshots);
203
- const resetAt = parseResetTime(getFieldValue(dataRecord, "quota_reset_date", "quotaResetDate"));
204
+ const resetAt = parseResetTime(
205
+ getFieldValue(dataRecord, "quota_reset_date", "quotaResetDate")
206
+ );
204
207
  const premiumQuota = formatGitHubQuotaSnapshot(snapshots.premium_interactions, resetAt);
205
208
  const chatQuota = formatGitHubQuotaSnapshot(snapshots.chat, resetAt);
206
209
  const completionsQuota = formatGitHubQuotaSnapshot(snapshots.completions, resetAt);
@@ -225,7 +228,11 @@ async function getGitHubUsage(accessToken, providerSpecificData) {
225
228
  // Free/limited plan format
226
229
  const monthlyQuotas = toRecord(dataRecord.monthly_quotas);
227
230
  const usedQuotas = toRecord(dataRecord.limited_user_quotas);
228
- const resetDate = getFieldValue(dataRecord, "limited_user_reset_date", "limitedUserResetDate");
231
+ const resetDate = getFieldValue(
232
+ dataRecord,
233
+ "limited_user_reset_date",
234
+ "limitedUserResetDate"
235
+ );
229
236
  const resetAt = parseResetTime(resetDate);
230
237
  const quotas: Record<string, UsageQuota> = {};
231
238
 
@@ -327,11 +334,7 @@ function inferGitHubPlanName(data: JsonRecord, premiumQuota: UsageQuota | null):
327
334
  toNumber(getFieldValue(monthlyQuotas, "premium_interactions", "premiumInteractions"), 0);
328
335
  const chatTotal = toNumber(getFieldValue(monthlyQuotas, "chat", "chat"), 0);
329
336
 
330
- if (
331
- combined.includes("PRO+") ||
332
- combined.includes("PRO_PLUS") ||
333
- combined.includes("PROPLUS")
334
- ) {
337
+ if (combined.includes("PRO+") || combined.includes("PRO_PLUS") || combined.includes("PROPLUS")) {
335
338
  return "Copilot Pro+";
336
339
  }
337
340
  if (combined.includes("ENTERPRISE")) return "Copilot Enterprise";
@@ -655,8 +658,18 @@ async function getClaudeUsage(accessToken) {
655
658
  }
656
659
  }
657
660
 
661
+ // Try to extract plan tier from the OAuth response
662
+ const planRaw =
663
+ typeof data.tier === "string"
664
+ ? data.tier
665
+ : typeof data.plan === "string"
666
+ ? data.plan
667
+ : typeof data.subscription_type === "string"
668
+ ? data.subscription_type
669
+ : null;
670
+
658
671
  return {
659
- plan: "Claude Code",
672
+ plan: planRaw || "Claude Code",
660
673
  quotas,
661
674
  extraUsage: data.extra_usage ?? null,
662
675
  };
@@ -259,11 +259,19 @@ export function openaiToClaudeRequest(model, body, stream) {
259
259
  toolNameMap.set(toolName, originalName);
260
260
  }
261
261
 
262
+ // Normalize input_schema: Anthropic requires `properties` when type is "object" (#595).
263
+ // MCP tools (e.g. pencil, computer_use) may omit properties on object-type schemas.
264
+ const rawSchema: Record<string, unknown> =
265
+ toolData.parameters || toolData.input_schema || { type: "object", properties: {}, required: [] };
266
+ const normalizedSchema =
267
+ rawSchema.type === "object" && !rawSchema.properties
268
+ ? { ...rawSchema, properties: {} }
269
+ : rawSchema;
270
+
262
271
  return {
263
272
  name: toolName,
264
273
  description: toolData.description || "",
265
- input_schema: toolData.parameters ||
266
- toolData.input_schema || { type: "object", properties: {}, required: [] },
274
+ input_schema: normalizedSchema,
267
275
  };
268
276
  });
269
277
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omniroute",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omniroute",
9
- "version": "3.0.0",
9
+ "version": "3.0.2",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "workspaces": [
package/app/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omniroute",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
5
5
  "type": "module",
6
6
  "bin": {