mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
* chore(release): v3.2.8 — Docker auto-update UI and cache analytics fixes * fix(sse): remove race condition in cache metrics tracking (#758) - Remove in-memory metrics tracking (currentMetrics, trackCacheMetrics, updateCacheMetrics) - Cache metrics now computed on-the-fly from usage_history table (single source of truth) - Fixes CRITICAL issue from code review: concurrent requests overwriting metrics - Fixes WARNING: duplicate metric tracking logic in streaming/non-streaming paths Ref: PR #752 (merged before this fix was included) * fix: handle allRateLimited credentials & forward extra body keys in embeddings/images routes (#757) * fix: handle allRateLimited credentials in embeddings and images routes When getProviderCredentials() returns an allRateLimited object (truthy, but without apiKey/accessToken), the embeddings and images routes incorrectly passed it to handlers as valid credentials. The handlers then sent upstream requests without Authorization headers, causing 401 errors from providers (e.g. NVIDIA NIM). This only manifested under concurrent requests: a chat/completions call could trigger rate limiting on a provider account, and a simultaneous embeddings request would receive the allRateLimited sentinel — but treat it as valid credentials. The chat pipeline already handled this case correctly. This commit adds the same allRateLimited guard to all affected routes: - POST /v1/embeddings - POST /v1/providers/{provider}/embeddings - POST /v1/images/generations - POST /v1/providers/{provider}/images/generations Also adds a defense-in-depth guard in the embeddings handler itself: if no auth token is available for a non-local provider, return 401 immediately instead of sending an unauthenticated request upstream. Made-with: Cursor * fix(embeddings): forward extra body keys to upstream providers The embeddings handler only forwarded model, input, dimensions, and encoding_format to upstream providers, silently dropping any additional fields. This broke asymmetric embedding APIs (e.g. NVIDIA NIM nv-embedqa-e5-v5) that require input_type, and other providers expecting user or truncate parameters. Add a KNOWN_FIELDS exclusion set and forward all unrecognized body keys to the upstream request, matching the passthrough pattern used by the chat pipeline's DefaultExecutor.transformRequest(). Made-with: Cursor * fix(auth): redirect and unconditional 401 on disabled requireLogin + fix test cases * fix(build): remove legacy proxy.ts causing Next.js build collision * fix(build): revert middleware.ts rename to proxy.ts because of Next.js Edge constraints --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: tombii <tombii@users.noreply.github.com> Co-authored-by: Gorchakov-Pressure <117600961+Gorchakov-Pressure@users.noreply.github.com>
208 lines
6.9 KiB
Plaintext
208 lines
6.9 KiB
Plaintext
[CREDENTIALS] No external credentials file found, using defaults.
|
|
[DB] SQLite database ready: /home/diegosouzapw/.omniroute/storage.sqlite
|
|
[MODEL] Ambiguous model 'claude-haiku-4.5'. Use provider/model prefix (ex: gh/claude-haiku-4.5 or kr/claude-haiku-4.5). Candidates: gh, kr, anthropic
|
|
TAP version 13
|
|
# Subtest: getModelInfoCore resolves unique non-openai unprefixed model
|
|
ok 1 - getModelInfoCore resolves unique non-openai unprefixed model
|
|
---
|
|
duration_ms: 3.403766
|
|
type: 'test'
|
|
...
|
|
# Subtest: getModelInfoCore keeps openai fallback for gpt-4o
|
|
ok 2 - getModelInfoCore keeps openai fallback for gpt-4o
|
|
---
|
|
duration_ms: 0.535726
|
|
type: 'test'
|
|
...
|
|
# Subtest: getModelInfoCore resolves gpt-5.4 to codex
|
|
ok 3 - getModelInfoCore resolves gpt-5.4 to codex
|
|
---
|
|
duration_ms: 0.321781
|
|
type: 'test'
|
|
...
|
|
# Subtest: getModelInfoCore returns explicit ambiguity metadata for ambiguous unprefixed model
|
|
ok 4 - getModelInfoCore returns explicit ambiguity metadata for ambiguous unprefixed model
|
|
---
|
|
duration_ms: 1.079896
|
|
type: 'test'
|
|
...
|
|
# Subtest: getModelInfoCore canonicalizes github legacy alias with explicit provider prefix
|
|
ok 5 - getModelInfoCore canonicalizes github legacy alias with explicit provider prefix
|
|
---
|
|
duration_ms: 0.370547
|
|
type: 'test'
|
|
...
|
|
# Subtest: GithubExecutor routes codex-family model to /responses
|
|
ok 6 - GithubExecutor routes codex-family model to /responses
|
|
---
|
|
duration_ms: 0.47113
|
|
type: 'test'
|
|
...
|
|
# Subtest: GithubExecutor keeps non-codex model on /chat/completions
|
|
ok 7 - GithubExecutor keeps non-codex model on /chat/completions
|
|
---
|
|
duration_ms: 0.38457
|
|
type: 'test'
|
|
...
|
|
# Subtest: DefaultExecutor uses x-api-key for kimi-coding-apikey
|
|
ok 8 - DefaultExecutor uses x-api-key for kimi-coding-apikey
|
|
---
|
|
duration_ms: 0.451443
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor forces stream=true for upstream compatibility
|
|
ok 9 - CodexExecutor forces stream=true for upstream compatibility
|
|
---
|
|
duration_ms: 1.203259
|
|
type: 'test'
|
|
...
|
|
# Subtest: Claude native messages can be round-tripped through OpenAI into Claude OAuth format
|
|
ok 10 - Claude native messages can be round-tripped through OpenAI into Claude OAuth format
|
|
---
|
|
duration_ms: 7.232512
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor maps fast service tier to priority
|
|
ok 11 - CodexExecutor maps fast service tier to priority
|
|
---
|
|
duration_ms: 0.489993
|
|
type: 'test'
|
|
...
|
|
# Subtest: shouldUseNativeCodexPassthrough only enables responses-native Codex requests
|
|
ok 12 - shouldUseNativeCodexPassthrough only enables responses-native Codex requests
|
|
---
|
|
duration_ms: 0.441911
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor can force fast service tier from settings
|
|
ok 13 - CodexExecutor can force fast service tier from settings
|
|
---
|
|
duration_ms: 0.299575
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor always requests SSE accept header
|
|
ok 14 - CodexExecutor always requests SSE accept header
|
|
---
|
|
duration_ms: 0.602914
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor does not request SSE accept header for compact requests
|
|
ok 15 - CodexExecutor does not request SSE accept header for compact requests
|
|
---
|
|
duration_ms: 0.322611
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor preserves native responses payloads for Codex passthrough
|
|
not ok 16 - CodexExecutor preserves native responses payloads for Codex passthrough
|
|
---
|
|
duration_ms: 1.856261
|
|
type: 'test'
|
|
location: '/home/diegosouzapw/dev/proxys/9router/tests/unit/plan3-p0.test.mjs:221:1'
|
|
failureType: 'testCodeFailure'
|
|
error: |-
|
|
Expected values to be strictly equal:
|
|
|
|
false !== true
|
|
|
|
code: 'ERR_ASSERTION'
|
|
name: 'AssertionError'
|
|
expected: true
|
|
actual: false
|
|
operator: 'strictEqual'
|
|
stack: |-
|
|
TestContext.<anonymous> (file:///home/diegosouzapw/dev/proxys/9router/tests/unit/plan3-p0.test.mjs:242:10)
|
|
Test.runInAsyncScope (node:async_hooks:214:14)
|
|
Test.run (node:internal/test_runner/test:1047:25)
|
|
Test.processPendingSubtests (node:internal/test_runner/test:744:18)
|
|
Test.postRun (node:internal/test_runner/test:1173:19)
|
|
Test.run (node:internal/test_runner/test:1101:12)
|
|
async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
|
|
...
|
|
# Subtest: CodexExecutor strips streaming fields for compact passthrough
|
|
ok 17 - CodexExecutor strips streaming fields for compact passthrough
|
|
---
|
|
duration_ms: 0.296176
|
|
type: 'test'
|
|
...
|
|
# Subtest: CodexExecutor routes responses subpaths to matching upstream paths
|
|
ok 18 - CodexExecutor routes responses subpaths to matching upstream paths
|
|
---
|
|
duration_ms: 0.546657
|
|
type: 'test'
|
|
...
|
|
# Subtest: translateNonStreamingResponse converts Responses API payload to OpenAI chat.completion
|
|
ok 19 - translateNonStreamingResponse converts Responses API payload to OpenAI chat.completion
|
|
---
|
|
duration_ms: 1.483788
|
|
type: 'test'
|
|
...
|
|
# Subtest: extractUsageFromResponse reads usage from Responses API payload
|
|
ok 20 - extractUsageFromResponse reads usage from Responses API payload
|
|
---
|
|
duration_ms: 0.398039
|
|
type: 'test'
|
|
...
|
|
# Subtest: detectFormat identifies OpenAI Responses when input is string
|
|
ok 21 - detectFormat identifies OpenAI Responses when input is string
|
|
---
|
|
duration_ms: 0.359174
|
|
type: 'test'
|
|
...
|
|
# Subtest: detectFormat identifies OpenAI Responses by max_output_tokens without input array
|
|
ok 22 - detectFormat identifies OpenAI Responses by max_output_tokens without input array
|
|
---
|
|
duration_ms: 0.271215
|
|
type: 'test'
|
|
...
|
|
# Subtest: detectFormatFromEndpoint forces OpenAI for /v1/chat/completions
|
|
ok 23 - detectFormatFromEndpoint forces OpenAI for /v1/chat/completions
|
|
---
|
|
duration_ms: 0.52054
|
|
type: 'test'
|
|
...
|
|
# Subtest: detectFormatFromEndpoint forces Claude for /v1/messages
|
|
ok 24 - detectFormatFromEndpoint forces Claude for /v1/messages
|
|
---
|
|
duration_ms: 0.433035
|
|
type: 'test'
|
|
...
|
|
# Subtest: translateRequest normalizes openai-responses input string into list payload
|
|
ok 25 - translateRequest normalizes openai-responses input string into list payload
|
|
---
|
|
duration_ms: 0.358109
|
|
type: 'test'
|
|
...
|
|
# Subtest: translateRequest preserves service_tier when converting openai to openai-responses
|
|
ok 26 - translateRequest preserves service_tier when converting openai to openai-responses
|
|
---
|
|
duration_ms: 1.10454
|
|
type: 'test'
|
|
...
|
|
# Subtest: parseSSEToResponsesOutput parses completed response from SSE payload
|
|
ok 27 - parseSSEToResponsesOutput parses completed response from SSE payload
|
|
---
|
|
duration_ms: 0.575476
|
|
type: 'test'
|
|
...
|
|
# Subtest: parseSSEToResponsesOutput returns null for invalid payload
|
|
ok 28 - parseSSEToResponsesOutput returns null for invalid payload
|
|
---
|
|
duration_ms: 0.302714
|
|
type: 'test'
|
|
...
|
|
# Subtest: parseSSEToOpenAIResponse merges split tool call chunks by id without duplication
|
|
ok 29 - parseSSEToOpenAIResponse merges split tool call chunks by id without duplication
|
|
---
|
|
duration_ms: 0.916032
|
|
type: 'test'
|
|
...
|
|
1..29
|
|
# tests 29
|
|
# suites 0
|
|
# pass 28
|
|
# fail 1
|
|
# cancelled 0
|
|
# skipped 0
|
|
# todo 0
|
|
# duration_ms 65.394285
|