omniroute 2.8.0 → 2.8.1
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]__09c944b3._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__167585da._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__3972de72._.js +5 -5
- package/app/.next/server/chunks/[root-of-the-server]__64bd5d97._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__784fb7c5._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__7d9b23e7._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__80e3bfc3._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__84e445b2._.js +2 -2
- package/app/.next/server/chunks/[root-of-the-server]__92cb0def._.js +2 -2
- package/app/.next/server/chunks/[root-of-the-server]__a630d6ef._.js +5 -5
- package/app/.next/server/chunks/[root-of-the-server]__cb8a67d1._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__db2f9fe0._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__dc47ee64._.js +1 -1
- package/app/.next/server/chunks/[root-of-the-server]__f0f9eb3f._.js +2 -2
- package/app/.next/server/chunks/_05c48915._.js +1 -1
- package/app/.next/server/chunks/_1244636c._.js +5 -5
- 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/open-sse_translator_index_ts_f5fd0821._.js +9 -9
- package/app/.next/server/chunks/src_lib_a886ee1f._.js +1 -1
- package/app/.next/server/chunks/src_shared_validation_schemas_ts_4e63863a._.js +1 -1
- package/app/.next/server/chunks/ssr/[root-of-the-server]__9affb65e._.js +1 -1
- package/app/.next/server/chunks/ssr/[root-of-the-server]__a6942102._.js +1 -1
- package/app/.next/server/chunks/ssr/_5a9cd299._.js +1 -1
- package/app/.next/server/chunks/ssr/src_d3225e36._.js +1 -1
- package/app/.next/server/chunks/ssr/src_i18n_messages_zh-CN_json_f4112d90._.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/02afac1b585d8219.css +1 -0
- package/app/.next/static/chunks/{d3cb88d181c61235.js → 4bed5b394f9dece9.js} +1 -1
- package/app/.next/static/chunks/{4f099d0f5b0f00d6.js → b5cc103e16794392.js} +1 -1
- package/app/.next/static/chunks/d19ab4efcaddd1db.js +1 -0
- package/app/CHANGELOG.md +20 -0
- package/app/docs/openapi.yaml +1 -1
- package/app/open-sse/executors/kiro.ts +5 -2
- package/app/open-sse/handlers/chatCore.ts +11 -3
- package/app/open-sse/translator/helpers/toolCallHelper.ts +58 -15
- package/app/open-sse/translator/index.ts +10 -3
- package/app/open-sse/utils/stream.ts +129 -19
- package/app/open-sse/utils/usageTracking.ts +3 -1
- package/app/package-lock.json +2 -2
- package/app/package.json +1 -1
- package/app/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +22 -0
- package/app/src/app/api/keys/[id]/route.ts +26 -4
- package/app/src/app/api/provider-models/route.ts +3 -1
- package/app/src/i18n/messages/zh-CN.json +862 -787
- package/app/src/lib/db/models.ts +25 -0
- package/app/src/lib/usage/callLogs.ts +6 -2
- package/app/src/shared/components/RequestLoggerDetail.tsx +10 -10
- package/app/src/shared/validation/schemas.ts +1 -0
- package/app/src/sse/handlers/chat.ts +8 -3
- package/package.json +1 -1
- package/app/.next/static/chunks/194dbd564ba7150b.css +0 -1
- package/app/.next/static/chunks/3f3d822837a5ff5f.js +0 -1
- /package/app/.next/static/{Y1ARupD8_nbYoMt6WYrBO → JoLoKHY_9DVANe7Qs4D0K}/_buildManifest.js +0 -0
- /package/app/.next/static/{Y1ARupD8_nbYoMt6WYrBO → JoLoKHY_9DVANe7Qs4D0K}/_clientMiddlewareManifest.json +0 -0
- /package/app/.next/static/{Y1ARupD8_nbYoMt6WYrBO → JoLoKHY_9DVANe7Qs4D0K}/_ssgManifest.js +0 -0
|
@@ -30,6 +30,8 @@ type StreamLogger = {
|
|
|
30
30
|
type StreamCompletePayload = {
|
|
31
31
|
status: number;
|
|
32
32
|
usage: unknown;
|
|
33
|
+
/** Minimal response body for call log (streaming: usage + note; non-streaming not used) */
|
|
34
|
+
responseBody?: unknown;
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
type StreamOptions = {
|
|
@@ -51,6 +53,8 @@ type TranslateState = ReturnType<typeof initState> & {
|
|
|
51
53
|
toolNameMap?: unknown;
|
|
52
54
|
usage?: unknown;
|
|
53
55
|
finishReason?: unknown;
|
|
56
|
+
/** Accumulated message content for call log response body */
|
|
57
|
+
accumulatedContent?: string;
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
function getOpenAIIntermediateChunks(value: unknown): unknown[] {
|
|
@@ -106,14 +110,21 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
106
110
|
let buffer = "";
|
|
107
111
|
let usage = null;
|
|
108
112
|
|
|
109
|
-
// State for translate mode
|
|
113
|
+
// State for translate mode (accumulatedContent for call log response body)
|
|
110
114
|
const state: TranslateState | null =
|
|
111
115
|
mode === STREAM_MODE.TRANSLATE
|
|
112
|
-
? {
|
|
116
|
+
? {
|
|
117
|
+
...(initState(sourceFormat) as TranslateState),
|
|
118
|
+
provider,
|
|
119
|
+
toolNameMap,
|
|
120
|
+
accumulatedContent: "",
|
|
121
|
+
}
|
|
113
122
|
: null;
|
|
114
123
|
|
|
115
124
|
// Track content length for usage estimation (both modes)
|
|
116
125
|
let totalContentLength = 0;
|
|
126
|
+
// Passthrough: accumulate content for call log response body
|
|
127
|
+
let passthroughAccumulatedContent = "";
|
|
117
128
|
|
|
118
129
|
// Guard against duplicate [DONE] events — ensures exactly one per stream
|
|
119
130
|
let doneSent = false;
|
|
@@ -201,9 +212,10 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
201
212
|
if (extracted) {
|
|
202
213
|
usage = extracted;
|
|
203
214
|
}
|
|
204
|
-
// Track content length
|
|
215
|
+
// Track content length and accumulate for call log
|
|
205
216
|
if (parsed.delta && typeof parsed.delta === "string") {
|
|
206
217
|
totalContentLength += parsed.delta.length;
|
|
218
|
+
passthroughAccumulatedContent += parsed.delta;
|
|
207
219
|
}
|
|
208
220
|
} else if (isClaudeSSE) {
|
|
209
221
|
// Claude SSE: extract usage, track content, forward as-is
|
|
@@ -213,14 +225,23 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
213
225
|
// message_start carries input_tokens, message_delta carries output_tokens
|
|
214
226
|
if (!usage) usage = {};
|
|
215
227
|
if (extracted.prompt_tokens > 0) usage.prompt_tokens = extracted.prompt_tokens;
|
|
216
|
-
if (extracted.completion_tokens > 0)
|
|
228
|
+
if (extracted.completion_tokens > 0)
|
|
229
|
+
usage.completion_tokens = extracted.completion_tokens;
|
|
217
230
|
if (extracted.total_tokens > 0) usage.total_tokens = extracted.total_tokens;
|
|
218
|
-
if (extracted.cache_read_input_tokens)
|
|
219
|
-
|
|
231
|
+
if (extracted.cache_read_input_tokens)
|
|
232
|
+
usage.cache_read_input_tokens = extracted.cache_read_input_tokens;
|
|
233
|
+
if (extracted.cache_creation_input_tokens)
|
|
234
|
+
usage.cache_creation_input_tokens = extracted.cache_creation_input_tokens;
|
|
235
|
+
}
|
|
236
|
+
// Track content length and accumulate from Claude format
|
|
237
|
+
if (parsed.delta?.text) {
|
|
238
|
+
totalContentLength += parsed.delta.text.length;
|
|
239
|
+
passthroughAccumulatedContent += parsed.delta.text;
|
|
240
|
+
}
|
|
241
|
+
if (parsed.delta?.thinking) {
|
|
242
|
+
totalContentLength += parsed.delta.thinking.length;
|
|
243
|
+
passthroughAccumulatedContent += parsed.delta.thinking;
|
|
220
244
|
}
|
|
221
|
-
// Track content length from Claude format
|
|
222
|
-
if (parsed.delta?.text) totalContentLength += parsed.delta.text.length;
|
|
223
|
-
if (parsed.delta?.thinking) totalContentLength += parsed.delta.thinking.length;
|
|
224
245
|
} else {
|
|
225
246
|
// Chat Completions: full sanitization pipeline
|
|
226
247
|
parsed = sanitizeStreamingChunk(parsed);
|
|
@@ -246,6 +267,10 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
246
267
|
if (content && typeof content === "string") {
|
|
247
268
|
totalContentLength += content.length;
|
|
248
269
|
}
|
|
270
|
+
if (typeof delta?.content === "string")
|
|
271
|
+
passthroughAccumulatedContent += delta.content;
|
|
272
|
+
if (typeof delta?.reasoning_content === "string")
|
|
273
|
+
passthroughAccumulatedContent += delta.reasoning_content;
|
|
249
274
|
|
|
250
275
|
const extracted = extractUsage(parsed);
|
|
251
276
|
if (extracted) {
|
|
@@ -301,23 +326,45 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
301
326
|
continue;
|
|
302
327
|
}
|
|
303
328
|
|
|
304
|
-
// Track content length for
|
|
305
|
-
//
|
|
329
|
+
// Track content length and accumulate for call log (from raw provider chunk, so content is never missed)
|
|
330
|
+
// Do this before translation so we capture content regardless of translator output shape
|
|
306
331
|
|
|
307
332
|
// Claude format
|
|
308
333
|
if (parsed.delta?.text) {
|
|
309
|
-
|
|
334
|
+
const t = parsed.delta.text;
|
|
335
|
+
totalContentLength += t.length;
|
|
336
|
+
if (state?.accumulatedContent !== undefined && typeof t === "string")
|
|
337
|
+
state.accumulatedContent += t;
|
|
310
338
|
}
|
|
311
339
|
if (parsed.delta?.thinking) {
|
|
312
|
-
|
|
340
|
+
const t = parsed.delta.thinking;
|
|
341
|
+
totalContentLength += t.length;
|
|
342
|
+
if (state?.accumulatedContent !== undefined && typeof t === "string")
|
|
343
|
+
state.accumulatedContent += t;
|
|
313
344
|
}
|
|
314
345
|
|
|
315
346
|
// OpenAI format
|
|
316
347
|
if (parsed.choices?.[0]?.delta?.content) {
|
|
317
|
-
|
|
348
|
+
const c = parsed.choices[0].delta.content;
|
|
349
|
+
if (typeof c === "string") {
|
|
350
|
+
totalContentLength += c.length;
|
|
351
|
+
if (state?.accumulatedContent !== undefined) state.accumulatedContent += c;
|
|
352
|
+
} else if (Array.isArray(c)) {
|
|
353
|
+
for (const part of c) {
|
|
354
|
+
if (part?.text && typeof part.text === "string") {
|
|
355
|
+
totalContentLength += part.text.length;
|
|
356
|
+
if (state?.accumulatedContent !== undefined)
|
|
357
|
+
state.accumulatedContent += part.text;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
318
361
|
}
|
|
319
362
|
if (parsed.choices?.[0]?.delta?.reasoning_content) {
|
|
320
|
-
|
|
363
|
+
const r = parsed.choices[0].delta.reasoning_content;
|
|
364
|
+
if (typeof r === "string") {
|
|
365
|
+
totalContentLength += r.length;
|
|
366
|
+
if (state?.accumulatedContent !== undefined) state.accumulatedContent += r;
|
|
367
|
+
}
|
|
321
368
|
}
|
|
322
369
|
|
|
323
370
|
// Gemini format - may have multiple parts
|
|
@@ -325,10 +372,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
325
372
|
for (const part of parsed.candidates[0].content.parts) {
|
|
326
373
|
if (part.text && typeof part.text === "string") {
|
|
327
374
|
totalContentLength += part.text.length;
|
|
375
|
+
if (state?.accumulatedContent !== undefined) state.accumulatedContent += part.text;
|
|
328
376
|
}
|
|
329
377
|
}
|
|
330
378
|
}
|
|
331
379
|
|
|
380
|
+
// Generic fallback: delta string, top-level content/text (e.g. some SSE payloads)
|
|
381
|
+
if (state?.accumulatedContent !== undefined) {
|
|
382
|
+
if (typeof (parsed as JsonRecord).delta === "string") {
|
|
383
|
+
const d = (parsed as JsonRecord).delta as string;
|
|
384
|
+
state.accumulatedContent += d;
|
|
385
|
+
totalContentLength += d.length;
|
|
386
|
+
}
|
|
387
|
+
if (typeof (parsed as JsonRecord).content === "string") {
|
|
388
|
+
const c = (parsed as JsonRecord).content as string;
|
|
389
|
+
state.accumulatedContent += c;
|
|
390
|
+
totalContentLength += c.length;
|
|
391
|
+
}
|
|
392
|
+
if (typeof (parsed as JsonRecord).text === "string") {
|
|
393
|
+
const t = (parsed as JsonRecord).text as string;
|
|
394
|
+
state.accumulatedContent += t;
|
|
395
|
+
totalContentLength += t.length;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
332
399
|
// Extract usage
|
|
333
400
|
const extracted = extractUsage(parsed);
|
|
334
401
|
if (extracted) state.usage = extracted; // Keep original usage for logging
|
|
@@ -344,6 +411,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
344
411
|
|
|
345
412
|
if (translated?.length > 0) {
|
|
346
413
|
for (const item of translated) {
|
|
414
|
+
// Content for call log is accumulated only from parsed (above) to avoid double-counting;
|
|
415
|
+
// do not add again from item here.
|
|
416
|
+
|
|
347
417
|
// Filter empty chunks
|
|
348
418
|
if (!hasValuableContent(item, sourceFormat)) {
|
|
349
419
|
continue; // Skip this empty chunk
|
|
@@ -415,10 +485,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
415
485
|
status: "200 OK",
|
|
416
486
|
}).catch(() => {});
|
|
417
487
|
}
|
|
418
|
-
// Notify caller for call log persistence
|
|
488
|
+
// Notify caller for call log persistence (include full response body with accumulated content)
|
|
419
489
|
if (onComplete) {
|
|
420
490
|
try {
|
|
421
|
-
|
|
491
|
+
const u = usage as Record<string, unknown> | null;
|
|
492
|
+
const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0);
|
|
493
|
+
const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0);
|
|
494
|
+
const content = passthroughAccumulatedContent.trim() || "";
|
|
495
|
+
const responseBody = {
|
|
496
|
+
choices: [
|
|
497
|
+
{
|
|
498
|
+
message: {
|
|
499
|
+
role: "assistant",
|
|
500
|
+
content,
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
usage: {
|
|
505
|
+
prompt_tokens: prompt,
|
|
506
|
+
completion_tokens: completion,
|
|
507
|
+
total_tokens: prompt + completion,
|
|
508
|
+
},
|
|
509
|
+
_streamed: true,
|
|
510
|
+
};
|
|
511
|
+
onComplete({ status: 200, usage, responseBody });
|
|
422
512
|
} catch {}
|
|
423
513
|
}
|
|
424
514
|
return;
|
|
@@ -497,10 +587,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
|
|
497
587
|
status: "200 OK",
|
|
498
588
|
}).catch(() => {});
|
|
499
589
|
}
|
|
500
|
-
// Notify caller for call log persistence
|
|
590
|
+
// Notify caller for call log persistence (include full response body with accumulated content)
|
|
501
591
|
if (onComplete) {
|
|
502
592
|
try {
|
|
503
|
-
|
|
593
|
+
const u = state?.usage as Record<string, unknown> | null | undefined;
|
|
594
|
+
const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0);
|
|
595
|
+
const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0);
|
|
596
|
+
const content = (state?.accumulatedContent ?? "").trim() || "";
|
|
597
|
+
const responseBody = {
|
|
598
|
+
choices: [
|
|
599
|
+
{
|
|
600
|
+
message: {
|
|
601
|
+
role: "assistant",
|
|
602
|
+
content,
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
usage: {
|
|
607
|
+
prompt_tokens: prompt,
|
|
608
|
+
completion_tokens: completion,
|
|
609
|
+
total_tokens: prompt + completion,
|
|
610
|
+
},
|
|
611
|
+
_streamed: true,
|
|
612
|
+
};
|
|
613
|
+
onComplete({ status: 200, usage: state?.usage, responseBody });
|
|
504
614
|
} catch {}
|
|
505
615
|
}
|
|
506
616
|
} catch (error) {
|
|
@@ -400,8 +400,10 @@ export function logUsage(provider, usage, model = null, connectionId = null, api
|
|
|
400
400
|
console.log(msg);
|
|
401
401
|
|
|
402
402
|
// Save to usage DB
|
|
403
|
+
// input = total input tokens (non-cached + cache_read + cache_creation)
|
|
404
|
+
// This ensures analytics show correct totals for heavily-cached requests
|
|
403
405
|
const tokens = {
|
|
404
|
-
input: inTokens,
|
|
406
|
+
input: inTokens + (cacheRead || 0) + (cacheCreation || 0),
|
|
405
407
|
output: outTokens,
|
|
406
408
|
cacheRead: cacheRead || 0,
|
|
407
409
|
cacheCreation: cacheCreation || 0,
|
package/app/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omniroute",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omniroute",
|
|
9
|
-
"version": "2.8.
|
|
9
|
+
"version": "2.8.1",
|
|
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": "2.8.
|
|
3
|
+
"version": "2.8.1",
|
|
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": {
|
|
@@ -1477,6 +1477,7 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
|
|
|
1477
1477
|
const [editingModelId, setEditingModelId] = useState<string | null>(null);
|
|
1478
1478
|
const [editingApiFormat, setEditingApiFormat] = useState("chat-completions");
|
|
1479
1479
|
const [editingEndpoints, setEditingEndpoints] = useState<string[]>(["chat"]);
|
|
1480
|
+
const [editingNormalizeToolCallId, setEditingNormalizeToolCallId] = useState(false);
|
|
1480
1481
|
const [savingModelId, setSavingModelId] = useState<string | null>(null);
|
|
1481
1482
|
|
|
1482
1483
|
const fetchCustomModels = useCallback(async () => {
|
|
@@ -1548,12 +1549,14 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
|
|
|
1548
1549
|
? model.supportedEndpoints
|
|
1549
1550
|
: ["chat"]
|
|
1550
1551
|
);
|
|
1552
|
+
setEditingNormalizeToolCallId(Boolean(model.normalizeToolCallId));
|
|
1551
1553
|
};
|
|
1552
1554
|
|
|
1553
1555
|
const cancelEdit = () => {
|
|
1554
1556
|
setEditingModelId(null);
|
|
1555
1557
|
setEditingApiFormat("chat-completions");
|
|
1556
1558
|
setEditingEndpoints(["chat"]);
|
|
1559
|
+
setEditingNormalizeToolCallId(false);
|
|
1557
1560
|
setSavingModelId(null);
|
|
1558
1561
|
};
|
|
1559
1562
|
|
|
@@ -1577,6 +1580,7 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
|
|
|
1577
1580
|
source: model?.source || "manual",
|
|
1578
1581
|
apiFormat: editingApiFormat,
|
|
1579
1582
|
supportedEndpoints: editingEndpoints,
|
|
1583
|
+
normalizeToolCallId: editingNormalizeToolCallId,
|
|
1580
1584
|
}),
|
|
1581
1585
|
});
|
|
1582
1586
|
|
|
@@ -1738,6 +1742,14 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
|
|
|
1738
1742
|
🔊 Audio
|
|
1739
1743
|
</span>
|
|
1740
1744
|
)}
|
|
1745
|
+
{model.normalizeToolCallId && (
|
|
1746
|
+
<span
|
|
1747
|
+
className="text-[10px] px-1.5 py-0.5 rounded-full bg-slate-500/15 text-slate-400 font-medium"
|
|
1748
|
+
title="9-char tool call ID (Mistral)"
|
|
1749
|
+
>
|
|
1750
|
+
ID×9
|
|
1751
|
+
</span>
|
|
1752
|
+
)}
|
|
1741
1753
|
</div>
|
|
1742
1754
|
|
|
1743
1755
|
{editingModelId === model.id && (
|
|
@@ -1790,6 +1802,16 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
|
|
|
1790
1802
|
))}
|
|
1791
1803
|
</div>
|
|
1792
1804
|
</div>
|
|
1805
|
+
|
|
1806
|
+
<label className="flex items-center gap-2 text-xs text-text-main cursor-pointer">
|
|
1807
|
+
<input
|
|
1808
|
+
type="checkbox"
|
|
1809
|
+
checked={editingNormalizeToolCallId}
|
|
1810
|
+
onChange={(e) => setEditingNormalizeToolCallId(e.target.checked)}
|
|
1811
|
+
className="rounded border-border"
|
|
1812
|
+
/>
|
|
1813
|
+
Normalize Tool Call ID (9 chars, Mistral)
|
|
1814
|
+
</label>
|
|
1793
1815
|
</div>
|
|
1794
1816
|
<div className="mt-3 flex items-center gap-2">
|
|
1795
1817
|
<Button
|
|
@@ -55,9 +55,26 @@ export async function PATCH(request, { params }) {
|
|
|
55
55
|
if (isValidationFailure(validation)) {
|
|
56
56
|
return NextResponse.json({ error: validation.error }, { status: 400 });
|
|
57
57
|
}
|
|
58
|
-
const {
|
|
58
|
+
const {
|
|
59
|
+
name,
|
|
60
|
+
allowedModels,
|
|
61
|
+
allowedConnections,
|
|
62
|
+
noLog,
|
|
63
|
+
autoResolve,
|
|
64
|
+
isActive,
|
|
65
|
+
accessSchedule,
|
|
66
|
+
} = validation.data;
|
|
67
|
+
|
|
68
|
+
const payload: Parameters<typeof updateApiKeyPermissions>[1] = {};
|
|
69
|
+
if (name !== undefined) payload.name = name;
|
|
70
|
+
if (allowedModels !== undefined) payload.allowedModels = allowedModels;
|
|
71
|
+
if (allowedConnections !== undefined) payload.allowedConnections = allowedConnections;
|
|
72
|
+
if (noLog !== undefined) payload.noLog = noLog;
|
|
73
|
+
if (autoResolve !== undefined) payload.autoResolve = autoResolve;
|
|
74
|
+
if (isActive !== undefined) payload.isActive = isActive;
|
|
75
|
+
if (accessSchedule !== undefined) payload.accessSchedule = accessSchedule;
|
|
59
76
|
|
|
60
|
-
const updated = await updateApiKeyPermissions(id,
|
|
77
|
+
const updated = await updateApiKeyPermissions(id, payload);
|
|
61
78
|
if (!updated) {
|
|
62
79
|
return NextResponse.json({ error: "Key not found" }, { status: 404 });
|
|
63
80
|
}
|
|
@@ -67,8 +84,13 @@ export async function PATCH(request, { params }) {
|
|
|
67
84
|
|
|
68
85
|
return NextResponse.json({
|
|
69
86
|
message: "API key settings updated successfully",
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
...(name !== undefined && { name }),
|
|
88
|
+
...(allowedModels !== undefined && { allowedModels }),
|
|
89
|
+
...(allowedConnections !== undefined && { allowedConnections }),
|
|
90
|
+
...(noLog !== undefined && { noLog }),
|
|
91
|
+
...(autoResolve !== undefined && { autoResolve }),
|
|
92
|
+
...(isActive !== undefined && { isActive }),
|
|
93
|
+
...(accessSchedule !== undefined && { accessSchedule }),
|
|
72
94
|
});
|
|
73
95
|
} catch (error) {
|
|
74
96
|
console.log("Error updating key permissions:", error);
|
|
@@ -113,12 +113,14 @@ export async function PUT(request) {
|
|
|
113
113
|
return Response.json({ error: validation.error }, { status: 400 });
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
const { provider, modelId, modelName, apiFormat, supportedEndpoints } =
|
|
116
|
+
const { provider, modelId, modelName, apiFormat, supportedEndpoints, normalizeToolCallId } =
|
|
117
|
+
validation.data;
|
|
117
118
|
|
|
118
119
|
const model = await updateCustomModel(provider, modelId, {
|
|
119
120
|
modelName,
|
|
120
121
|
apiFormat,
|
|
121
122
|
supportedEndpoints,
|
|
123
|
+
normalizeToolCallId,
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
if (!model) {
|