omniroute 3.0.7 → 3.0.8
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.
- package/app/.next/BUILD_ID +1 -1
- package/app/.next/build-manifest.json +2 -2
- package/app/.next/prerender-manifest.json +3 -3
- package/app/.next/server/app/(dashboard)/dashboard/a2a/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/agents/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/analytics/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/api-manager/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/audit-log/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/auto-combo/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/costs/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/health/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/limits/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/logs/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/mcp/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/media/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/onboarding/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/playground/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/search-tools/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/settings/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/400/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/401/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/403/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/408/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/429/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/500/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/502/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/503/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/_global-error.html +2 -2
- package/app/.next/server/app/_global-error.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/forbidden/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/forgot-password/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/maintenance/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/status/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/terms/page_client-reference-manifest.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__051203a6._.js +2 -2
- package/app/.next/server/chunks/[root-of-the-server]__0891af92._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__1f2b0d89._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__46e00e59._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__6e52619e._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__70a3877b._.js +5 -5
- package/app/.next/server/chunks/[root-of-the-server]__7ace0fcd._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__7fa4d14e._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__b013abab._.js +6 -6
- package/app/.next/server/chunks/_05c48915._.js +1 -1
- package/app/.next/server/chunks/_06515a8a._.js +1 -1
- package/app/.next/server/chunks/_2115d8de._.js +1 -1
- package/app/.next/server/chunks/_3ac953eb._.js +1 -1
- package/app/.next/server/chunks/_4b8fd853._.js +1 -1
- package/app/.next/server/chunks/_68683848._.js +1 -1
- package/app/.next/server/chunks/_ee9b677b._.js +1 -1
- package/app/.next/server/chunks/_efd5ede2._.js +1 -1
- package/app/.next/server/chunks/open-sse_translator_index_ts_f5fd0821._.js +1 -1
- package/app/.next/server/chunks/ssr/[root-of-the-server]__9ef96d20._.js +1 -1
- package/app/.next/server/chunks/ssr/[root-of-the-server]__a6942102._.js +1 -1
- package/app/.next/server/pages/500.html +2 -2
- package/app/.next/server/server-reference-manifest.js +1 -1
- package/app/.next/server/server-reference-manifest.json +1 -1
- package/app/.next/static/chunks/{84065c05df1a3a1a.js → 6d9bfb06374cbab3.js} +1 -1
- package/app/CHANGELOG.md +13 -0
- package/app/docs/openapi.yaml +1 -1
- package/app/open-sse/handlers/responseSanitizer.ts +58 -0
- package/app/open-sse/translator/response/openai-to-claude.ts +12 -1
- package/app/open-sse/utils/usageTracking.ts +44 -5
- package/app/package-lock.json +20 -20
- package/app/package.json +1 -1
- package/app/src/lib/db/combos.ts +5 -5
- package/app/src/lib/db/models.ts +9 -9
- package/package.json +1 -1
- /package/app/.next/static/{kmbT_1tKg7aSbEcFkhAUg → VtLeHcThjUtEgx1Z2skPg}/_buildManifest.js +0 -0
- /package/app/.next/static/{kmbT_1tKg7aSbEcFkhAUg → VtLeHcThjUtEgx1Z2skPg}/_clientMiddlewareManifest.json +0 -0
- /package/app/.next/static/{kmbT_1tKg7aSbEcFkhAUg → VtLeHcThjUtEgx1Z2skPg}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,804730,e=>{e.v({name:"omniroute",version:"3.0.
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,804730,e=>{e.v({name:"omniroute",version:"3.0.8",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.",type:"module",bin:{omniroute:"bin/omniroute.mjs","omniroute-reset-password":"bin/reset-password.mjs"},files:["bin/","app/","open-sse/mcp-server/","src/shared/contracts/","scripts/postinstall.mjs","scripts/native-binary-compat.mjs","README.md","LICENSE"],workspaces:["open-sse"],engines:{node:">=18.0.0 <24.0.0"},keywords:["ai","router","proxy","openai","claude","anthropic","gemini","fallback","cursor","cline","codex","llm","auto-fallback"],license:"MIT",author:"diegosouzapw",repository:{type:"git",url:"https://github.com/diegosouzapw/OmniRoute"},homepage:"https://omniroute.online",scripts:{dev:"node scripts/run-next.mjs dev",build:"node scripts/build-next-isolated.mjs","build:cli":"node scripts/prepublish.mjs",start:"node scripts/run-next.mjs start",lint:"eslint .","electron:dev":'concurrently "npm run dev" "wait-on http://localhost:20128 && cd electron && npm run dev"',"electron:build":"npm run build && cd electron && npm run build","electron:build:win":"npm run build && cd electron && npm run build:win","electron:build:mac":"npm run build && cd electron && npm run build:mac","electron:build:linux":"npm run build && cd electron && npm run build:linux",test:"node --import tsx/esm --test tests/unit/*.test.mjs","test:unit":"node --import tsx/esm --test tests/unit/*.test.mjs","test:plan3":"node --import tsx/esm --test tests/unit/plan3-p0.test.mjs","test:fixes":"node --import tsx/esm --test tests/unit/fixes-p1.test.mjs","test:security":"node --import tsx/esm --test tests/unit/security-fase01.test.mjs","check:cycles":"node scripts/check-cycles.mjs","check:route-validation:t06":"node scripts/check-route-validation.mjs","check:any-budget:t11":"node scripts/check-t11-any-budget.mjs","check:docs-sync":"node scripts/check-docs-sync.mjs","typecheck:core":"tsc --pretty false -p tsconfig.typecheck-core.json","typecheck:noimplicit:core":"tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json","test:integration":"node --import tsx/esm --test tests/integration/*.test.mjs","test:e2e":"node scripts/run-playwright-tests.mjs test tests/e2e/*.spec.ts","test:protocols:e2e":"node scripts/run-protocol-clients-tests.mjs","test:vitest":"vitest run open-sse/mcp-server/__tests__/*.test.ts open-sse/services/autoCombo/__tests__/*.test.ts","test:ecosystem":"node scripts/run-ecosystem-tests.mjs","test:coverage":"npx c8 --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.mjs","test:all":"npm run test:unit && npm run test:vitest && npm run test:ecosystem && npm run test:e2e",check:"npm run lint && npm run test",prepublishOnly:"npm run build:cli",postinstall:"node scripts/postinstall.mjs",prepare:"husky","system-info":"node scripts/system-info.mjs"},dependencies:{"@lobehub/icons":"^5.0.1","@modelcontextprotocol/sdk":"^1.27.1","@monaco-editor/react":"^4.7.0","@swc/helpers":"0.5.19",bcryptjs:"^3.0.3","better-sqlite3":"^12.6.2",bottleneck:"^2.19.5",dompurify:"^3.3.2",express:"^5.2.1","fetch-socks":"^1.3.2","http-proxy-middleware":"^3.0.5","https-proxy-agent":"^8.0.0",jose:"^6.1.3",keytar:"^7.9.0",lowdb:"^7.0.1","monaco-editor":"^0.55.1",next:"^16.0.10","next-intl":"^4.8.3","node-machine-id":"^1.1.12",open:"^11.0.0",ora:"^9.1.0",pino:"^10.3.1","pino-pretty":"^13.1.3",react:"19.2.4","react-dom":"19.2.4",recharts:"^3.7.0",selfsigned:"^5.5.0",tsx:"^4.21.0",undici:"^7.19.2",uuid:"^13.0.0","wreq-js":"^2.0.1",zod:"^4.3.6",zustand:"^5.0.10"},devDependencies:{"@playwright/test":"^1.58.2","@tailwindcss/postcss":"^4.1.18","@types/bcryptjs":"^3.0.0","@types/better-sqlite3":"^7.6.13","@types/keytar":"^4.4.0","@types/node":"^25.2.3","@types/react":"^19.2.14","@types/react-dom":"^19.2.3",concurrently:"^9.2.1","cross-env":"^10.1.0",eslint:"^9.39.2","eslint-config-next":"^16.0.10",husky:"^9.1.7","lint-staged":"^16.2.7",prettier:"^3.8.1",tailwindcss:"^4",typescript:"^5.9.3","typescript-eslint":"^8.56.0",vitest:"^4.0.18","wait-on":"^9.0.4"},"lint-staged":{"*.{js,jsx,ts,tsx,mjs}":["prettier --write","eslint --fix --no-error-on-unmatched-pattern"],"*.{json,md,yml,yaml,css}":["prettier --write"]},pnpm:{onlyBuiltDependencies:["@parcel/watcher","@swc/core","better-sqlite3","esbuild","omniroute","sharp"]},overrides:{dompurify:"^3.3.2"}})},175696,e=>{"use strict";var t=e.i(861745),s=e.i(843476);function n({locale:e,...n}){if(!e)throw Error(void 0);return(0,s.jsx)(t.IntlProvider,{locale:e,...n})}e.s(["default",()=>n])}]);
|
package/app/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [3.0.8] — 2026-03-25
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **Translation Failures for OpenAI-format Providers in Claude CLI (#632):**
|
|
12
|
+
- Handle `reasoning_details[]` array format from StepFun/OpenRouter — converts to `reasoning_content`
|
|
13
|
+
- Handle `reasoning` field alias from some providers → normalized to `reasoning_content`
|
|
14
|
+
- Cross-map usage field names: `input_tokens`↔`prompt_tokens`, `output_tokens`↔`completion_tokens` in `filterUsageForFormat`
|
|
15
|
+
- Fix `extractUsage` to accept both `input_tokens`/`output_tokens` and `prompt_tokens`/`completion_tokens` as valid usage fields
|
|
16
|
+
- Applied to both streaming (`sanitizeStreamingChunk`, `openai-to-claude.ts` translator) and non-streaming (`sanitizeMessage`) paths
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
7
20
|
## [3.0.7] — 2026-03-25
|
|
8
21
|
|
|
9
22
|
### 🐛 Bug Fixes
|
package/app/docs/openapi.yaml
CHANGED
|
@@ -172,6 +172,44 @@ function sanitizeMessage(msg: unknown): unknown {
|
|
|
172
172
|
sanitized.reasoning_content = msgRecord.reasoning_content;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
// Handle 'reasoning' field alias (some providers use this instead of reasoning_content)
|
|
176
|
+
if (
|
|
177
|
+
msgRecord.reasoning &&
|
|
178
|
+
typeof msgRecord.reasoning === "string" &&
|
|
179
|
+
!sanitized.reasoning_content
|
|
180
|
+
) {
|
|
181
|
+
sanitized.reasoning_content = msgRecord.reasoning;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Handle reasoning_details[] array (StepFun/OpenRouter format)
|
|
185
|
+
// Structure: [{ type: "reasoning.text", text: "...", format: "unknown", index: 0 }]
|
|
186
|
+
if (Array.isArray(msgRecord.reasoning_details) && !sanitized.reasoning_content) {
|
|
187
|
+
const reasoningParts: string[] = [];
|
|
188
|
+
for (const detail of msgRecord.reasoning_details) {
|
|
189
|
+
const detailObj = detail && typeof detail === "object" ? (detail as JsonRecord) : null;
|
|
190
|
+
if (!detailObj) continue;
|
|
191
|
+
const detailType = typeof detailObj.type === "string" ? detailObj.type : "";
|
|
192
|
+
const detailText =
|
|
193
|
+
typeof detailObj.text === "string"
|
|
194
|
+
? detailObj.text
|
|
195
|
+
: typeof detailObj.content === "string"
|
|
196
|
+
? detailObj.content
|
|
197
|
+
: "";
|
|
198
|
+
if (
|
|
199
|
+
detailText &&
|
|
200
|
+
(detailType === "reasoning" ||
|
|
201
|
+
detailType === "reasoning.text" ||
|
|
202
|
+
detailType === "thinking" ||
|
|
203
|
+
detailType === "")
|
|
204
|
+
) {
|
|
205
|
+
reasoningParts.push(detailText);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (reasoningParts.length > 0) {
|
|
209
|
+
sanitized.reasoning_content = reasoningParts.join("");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
175
213
|
// Preserve tool_calls
|
|
176
214
|
if (msgRecord.tool_calls) {
|
|
177
215
|
sanitized.tool_calls = msgRecord.tool_calls;
|
|
@@ -258,6 +296,26 @@ export function sanitizeStreamingChunk(parsed: unknown): unknown {
|
|
|
258
296
|
if (deltaRecord.content !== undefined) delta.content = deltaRecord.content;
|
|
259
297
|
if (deltaRecord.reasoning_content !== undefined) {
|
|
260
298
|
delta.reasoning_content = deltaRecord.reasoning_content;
|
|
299
|
+
} else if (typeof deltaRecord.reasoning === "string" && deltaRecord.reasoning) {
|
|
300
|
+
// Alias: some providers use 'reasoning' instead of 'reasoning_content'
|
|
301
|
+
delta.reasoning_content = deltaRecord.reasoning;
|
|
302
|
+
} else if (Array.isArray(deltaRecord.reasoning_details)) {
|
|
303
|
+
// StepFun/OpenRouter: reasoning_details[{type:"reasoning.text", text:"..."}]
|
|
304
|
+
const parts: string[] = [];
|
|
305
|
+
for (const detail of deltaRecord.reasoning_details) {
|
|
306
|
+
const d = detail && typeof detail === "object" ? (detail as JsonRecord) : null;
|
|
307
|
+
if (!d) continue;
|
|
308
|
+
const text =
|
|
309
|
+
typeof d.text === "string"
|
|
310
|
+
? d.text
|
|
311
|
+
: typeof d.content === "string"
|
|
312
|
+
? d.content
|
|
313
|
+
: "";
|
|
314
|
+
if (text) parts.push(text);
|
|
315
|
+
}
|
|
316
|
+
if (parts.length > 0) {
|
|
317
|
+
delta.reasoning_content = parts.join("");
|
|
318
|
+
}
|
|
261
319
|
}
|
|
262
320
|
if (deltaRecord.tool_calls !== undefined) delta.tool_calls = deltaRecord.tool_calls;
|
|
263
321
|
if (deltaRecord.function_call !== undefined)
|
|
@@ -93,7 +93,18 @@ export function openaiToClaudeResponse(chunk, state) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// Handle reasoning_content (thinking) - GLM, DeepSeek, etc.
|
|
96
|
-
|
|
96
|
+
// Also supports 'reasoning' field alias and reasoning_details[] (StepFun/OpenRouter)
|
|
97
|
+
let reasoningContent = delta?.reasoning_content || delta?.reasoning;
|
|
98
|
+
if (!reasoningContent && Array.isArray(delta?.reasoning_details)) {
|
|
99
|
+
const parts: string[] = [];
|
|
100
|
+
for (const detail of delta.reasoning_details) {
|
|
101
|
+
if (detail && typeof detail === "object") {
|
|
102
|
+
const text = detail.text || detail.content;
|
|
103
|
+
if (typeof text === "string" && text) parts.push(text);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (parts.length > 0) reasoningContent = parts.join("");
|
|
107
|
+
}
|
|
97
108
|
if (reasoningContent) {
|
|
98
109
|
stopTextBlock(state, results);
|
|
99
110
|
|
|
@@ -66,12 +66,47 @@ export function addBufferToUsage(usage) {
|
|
|
66
66
|
export function filterUsageForFormat(usage, targetFormat) {
|
|
67
67
|
if (!usage || typeof usage !== "object") return usage;
|
|
68
68
|
|
|
69
|
+
// Cross-map between Claude-style and OpenAI-style field names before filtering.
|
|
70
|
+
// Some providers return input_tokens/output_tokens even when using OpenAI format.
|
|
71
|
+
const convertedUsage = { ...usage };
|
|
72
|
+
if (targetFormat === FORMATS.CLAUDE || targetFormat === FORMATS.OPENAI_RESPONSES) {
|
|
73
|
+
// OpenAI → Claude: prompt_tokens → input_tokens
|
|
74
|
+
if (convertedUsage.prompt_tokens !== undefined && convertedUsage.input_tokens === undefined) {
|
|
75
|
+
convertedUsage.input_tokens = convertedUsage.prompt_tokens;
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
convertedUsage.completion_tokens !== undefined &&
|
|
79
|
+
convertedUsage.output_tokens === undefined
|
|
80
|
+
) {
|
|
81
|
+
convertedUsage.output_tokens = convertedUsage.completion_tokens;
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
// Claude → OpenAI: input_tokens → prompt_tokens
|
|
85
|
+
if (convertedUsage.input_tokens !== undefined && convertedUsage.prompt_tokens === undefined) {
|
|
86
|
+
convertedUsage.prompt_tokens = convertedUsage.input_tokens;
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
convertedUsage.output_tokens !== undefined &&
|
|
90
|
+
convertedUsage.completion_tokens === undefined
|
|
91
|
+
) {
|
|
92
|
+
convertedUsage.completion_tokens = convertedUsage.output_tokens;
|
|
93
|
+
}
|
|
94
|
+
// Ensure total_tokens is set
|
|
95
|
+
if (
|
|
96
|
+
convertedUsage.total_tokens === undefined &&
|
|
97
|
+
convertedUsage.prompt_tokens !== undefined &&
|
|
98
|
+
convertedUsage.completion_tokens !== undefined
|
|
99
|
+
) {
|
|
100
|
+
convertedUsage.total_tokens = convertedUsage.prompt_tokens + convertedUsage.completion_tokens;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
69
104
|
// Helper to pick only defined fields from usage
|
|
70
105
|
const pickFields = (fields) => {
|
|
71
106
|
const filtered = {};
|
|
72
107
|
for (const field of fields) {
|
|
73
|
-
if (
|
|
74
|
-
filtered[field] =
|
|
108
|
+
if (convertedUsage[field] !== undefined) {
|
|
109
|
+
filtered[field] = convertedUsage[field];
|
|
75
110
|
}
|
|
76
111
|
}
|
|
77
112
|
return filtered;
|
|
@@ -230,10 +265,14 @@ export function extractUsage(chunk) {
|
|
|
230
265
|
}
|
|
231
266
|
|
|
232
267
|
// OpenAI format
|
|
233
|
-
if (
|
|
268
|
+
if (
|
|
269
|
+
chunk.usage &&
|
|
270
|
+
typeof chunk.usage === "object" &&
|
|
271
|
+
(chunk.usage.prompt_tokens !== undefined || chunk.usage.input_tokens !== undefined)
|
|
272
|
+
) {
|
|
234
273
|
return normalizeUsage({
|
|
235
|
-
prompt_tokens: chunk.usage.prompt_tokens,
|
|
236
|
-
completion_tokens: chunk.usage.completion_tokens
|
|
274
|
+
prompt_tokens: chunk.usage.prompt_tokens ?? chunk.usage.input_tokens ?? 0,
|
|
275
|
+
completion_tokens: chunk.usage.completion_tokens ?? chunk.usage.output_tokens ?? 0,
|
|
237
276
|
cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens,
|
|
238
277
|
reasoning_tokens: chunk.usage.completion_tokens_details?.reasoning_tokens,
|
|
239
278
|
});
|
package/app/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omniroute",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omniroute",
|
|
9
|
-
"version": "3.0.
|
|
9
|
+
"version": "3.0.8",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"workspaces": [
|
|
@@ -3096,9 +3096,9 @@
|
|
|
3096
3096
|
}
|
|
3097
3097
|
},
|
|
3098
3098
|
"node_modules/@parcel/watcher/node_modules/picomatch": {
|
|
3099
|
-
"version": "4.0.
|
|
3100
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.
|
|
3101
|
-
"integrity": "sha512-
|
|
3099
|
+
"version": "4.0.4",
|
|
3100
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
3101
|
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
|
3102
3102
|
"license": "MIT",
|
|
3103
3103
|
"engines": {
|
|
3104
3104
|
"node": ">=12"
|
|
@@ -12842,9 +12842,9 @@
|
|
|
12842
12842
|
}
|
|
12843
12843
|
},
|
|
12844
12844
|
"node_modules/lint-staged/node_modules/picomatch": {
|
|
12845
|
-
"version": "4.0.
|
|
12846
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.
|
|
12847
|
-
"integrity": "sha512-
|
|
12845
|
+
"version": "4.0.4",
|
|
12846
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
12847
|
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
|
12848
12848
|
"dev": true,
|
|
12849
12849
|
"license": "MIT",
|
|
12850
12850
|
"engines": {
|
|
@@ -15311,9 +15311,9 @@
|
|
|
15311
15311
|
"license": "ISC"
|
|
15312
15312
|
},
|
|
15313
15313
|
"node_modules/picomatch": {
|
|
15314
|
-
"version": "2.3.
|
|
15315
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.
|
|
15316
|
-
"integrity": "sha512-
|
|
15314
|
+
"version": "2.3.2",
|
|
15315
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
|
15316
|
+
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
|
15317
15317
|
"license": "MIT",
|
|
15318
15318
|
"engines": {
|
|
15319
15319
|
"node": ">=8.6"
|
|
@@ -18225,9 +18225,9 @@
|
|
|
18225
18225
|
}
|
|
18226
18226
|
},
|
|
18227
18227
|
"node_modules/tinyglobby/node_modules/picomatch": {
|
|
18228
|
-
"version": "4.0.
|
|
18229
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.
|
|
18230
|
-
"integrity": "sha512-
|
|
18228
|
+
"version": "4.0.4",
|
|
18229
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
18230
|
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
|
18231
18231
|
"dev": true,
|
|
18232
18232
|
"license": "MIT",
|
|
18233
18233
|
"engines": {
|
|
@@ -19373,9 +19373,9 @@
|
|
|
19373
19373
|
}
|
|
19374
19374
|
},
|
|
19375
19375
|
"node_modules/vite/node_modules/picomatch": {
|
|
19376
|
-
"version": "4.0.
|
|
19377
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.
|
|
19378
|
-
"integrity": "sha512-
|
|
19376
|
+
"version": "4.0.4",
|
|
19377
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
19378
|
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
|
19379
19379
|
"dev": true,
|
|
19380
19380
|
"license": "MIT",
|
|
19381
19381
|
"engines": {
|
|
@@ -19468,9 +19468,9 @@
|
|
|
19468
19468
|
}
|
|
19469
19469
|
},
|
|
19470
19470
|
"node_modules/vitest/node_modules/picomatch": {
|
|
19471
|
-
"version": "4.0.
|
|
19472
|
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.
|
|
19473
|
-
"integrity": "sha512-
|
|
19471
|
+
"version": "4.0.4",
|
|
19472
|
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
19473
|
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
|
19474
19474
|
"dev": true,
|
|
19475
19475
|
"license": "MIT",
|
|
19476
19476
|
"engines": {
|
package/app/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omniroute",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
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": {
|
package/app/src/lib/db/combos.ts
CHANGED
|
@@ -27,21 +27,21 @@ export async function getCombos() {
|
|
|
27
27
|
.map((row) => JSON.parse(row));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export async function getComboById(id) {
|
|
30
|
+
export async function getComboById(id: string) {
|
|
31
31
|
const db = getDbInstance();
|
|
32
32
|
const row = db.prepare("SELECT data FROM combos WHERE id = ?").get(id);
|
|
33
33
|
const payload = getSerializedData(row);
|
|
34
34
|
return payload ? JSON.parse(payload) : null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export async function getComboByName(name) {
|
|
37
|
+
export async function getComboByName(name: string) {
|
|
38
38
|
const db = getDbInstance();
|
|
39
39
|
const row = db.prepare("SELECT data FROM combos WHERE name = ?").get(name);
|
|
40
40
|
const payload = getSerializedData(row);
|
|
41
41
|
return payload ? JSON.parse(payload) : null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export async function createCombo(data) {
|
|
44
|
+
export async function createCombo(data: JsonRecord) {
|
|
45
45
|
const db = getDbInstance();
|
|
46
46
|
const now = new Date().toISOString();
|
|
47
47
|
|
|
@@ -63,7 +63,7 @@ export async function createCombo(data) {
|
|
|
63
63
|
return combo;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export async function updateCombo(id, data) {
|
|
66
|
+
export async function updateCombo(id: string, data: JsonRecord) {
|
|
67
67
|
const db = getDbInstance();
|
|
68
68
|
const existing = db.prepare("SELECT data FROM combos WHERE id = ?").get(id);
|
|
69
69
|
if (!existing) return null;
|
|
@@ -84,7 +84,7 @@ export async function updateCombo(id, data) {
|
|
|
84
84
|
return merged;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export async function deleteCombo(id) {
|
|
87
|
+
export async function deleteCombo(id: string) {
|
|
88
88
|
const db = getDbInstance();
|
|
89
89
|
const result = db.prepare("DELETE FROM combos WHERE id = ?").run(id);
|
|
90
90
|
if (result.changes === 0) return false;
|
package/app/src/lib/db/models.ts
CHANGED
|
@@ -247,7 +247,7 @@ export async function getModelAliases() {
|
|
|
247
247
|
return result;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
export async function setModelAlias(alias, model) {
|
|
250
|
+
export async function setModelAlias(alias: string, model: unknown) {
|
|
251
251
|
const db = getDbInstance();
|
|
252
252
|
db.prepare(
|
|
253
253
|
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('modelAliases', ?, ?)"
|
|
@@ -255,7 +255,7 @@ export async function setModelAlias(alias, model) {
|
|
|
255
255
|
backupDbFile("pre-write");
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
export async function deleteModelAlias(alias) {
|
|
258
|
+
export async function deleteModelAlias(alias: string) {
|
|
259
259
|
const db = getDbInstance();
|
|
260
260
|
db.prepare("DELETE FROM key_value WHERE namespace = 'modelAliases' AND key = ?").run(alias);
|
|
261
261
|
backupDbFile("pre-write");
|
|
@@ -263,7 +263,7 @@ export async function deleteModelAlias(alias) {
|
|
|
263
263
|
|
|
264
264
|
// ──────────────── MITM Alias ────────────────
|
|
265
265
|
|
|
266
|
-
export async function getMitmAlias(toolName) {
|
|
266
|
+
export async function getMitmAlias(toolName?: string) {
|
|
267
267
|
const db = getDbInstance();
|
|
268
268
|
if (toolName) {
|
|
269
269
|
const row = db
|
|
@@ -282,7 +282,7 @@ export async function getMitmAlias(toolName) {
|
|
|
282
282
|
return result;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
export async function setMitmAliasAll(toolName, mappings) {
|
|
285
|
+
export async function setMitmAliasAll(toolName: string, mappings: unknown) {
|
|
286
286
|
const db = getDbInstance();
|
|
287
287
|
db.prepare(
|
|
288
288
|
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('mitmAlias', ?, ?)"
|
|
@@ -292,7 +292,7 @@ export async function setMitmAliasAll(toolName, mappings) {
|
|
|
292
292
|
|
|
293
293
|
// ──────────────── Custom Models ────────────────
|
|
294
294
|
|
|
295
|
-
export async function getCustomModels(providerId) {
|
|
295
|
+
export async function getCustomModels(providerId?: string) {
|
|
296
296
|
const db = getDbInstance();
|
|
297
297
|
if (providerId) {
|
|
298
298
|
const row = db
|
|
@@ -342,7 +342,7 @@ export async function addCustomModel(
|
|
|
342
342
|
const value = getKeyValue(row).value;
|
|
343
343
|
const models = value ? JSON.parse(value) : [];
|
|
344
344
|
|
|
345
|
-
const exists = models.find((m) => m.id === modelId);
|
|
345
|
+
const exists = models.find((m: JsonRecord) => m.id === modelId);
|
|
346
346
|
if (exists) return exists;
|
|
347
347
|
|
|
348
348
|
const model = {
|
|
@@ -430,7 +430,7 @@ export async function replaceCustomModels(
|
|
|
430
430
|
return merged;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
-
export async function removeCustomModel(providerId, modelId) {
|
|
433
|
+
export async function removeCustomModel(providerId: string, modelId: string) {
|
|
434
434
|
const db = getDbInstance();
|
|
435
435
|
const row = db
|
|
436
436
|
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
|
|
@@ -441,7 +441,7 @@ export async function removeCustomModel(providerId, modelId) {
|
|
|
441
441
|
if (!value) return false;
|
|
442
442
|
const models = JSON.parse(value);
|
|
443
443
|
const before = models.length;
|
|
444
|
-
const filtered = models.filter((m) => m.id !== modelId);
|
|
444
|
+
const filtered = models.filter((m: JsonRecord) => m.id !== modelId);
|
|
445
445
|
|
|
446
446
|
if (filtered.length === before) return false;
|
|
447
447
|
|
|
@@ -476,7 +476,7 @@ export async function updateCustomModel(
|
|
|
476
476
|
if (!value) return null;
|
|
477
477
|
|
|
478
478
|
const models = JSON.parse(value);
|
|
479
|
-
const index = models.findIndex((m) => m.id === modelId);
|
|
479
|
+
const index = models.findIndex((m: JsonRecord) => m.id === modelId);
|
|
480
480
|
if (index === -1) return null;
|
|
481
481
|
|
|
482
482
|
const current = models[index];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omniroute",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
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": {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|