Skip to content

feat: nginx/njs endpoint-aware model routing (no OmniRoute code changes) - #1

Closed
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1783366885-nginx-endpoint-routing
Closed

devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1783366885-nginx-endpoint-routing

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

Adds an nginx/njs layer that transparently routes requests to the correct KFC provider based on which OpenAI API endpoint the client calls — without modifying OmniRoute code or the request body.

Problem: OmniRoute doesn't allow two aliases with the same name pointing to different providers. Clients switching between /v1/responses and /v1/chat/completions had to manually change model prefixes.

Solution: njs handler in contrib/nginx/endpoint_routing.js:

POST /v1/responses        + model="gpt-5.5-pro"      → X-Route-Model: main/gpt-5.5-pro  → KFC (responses)
POST /v1/chat/completions + model="gpt-5.5-pro"      → X-Route-Model: old/gpt-5.5-pro   → KFC-Old (chat)
POST /v1/chat/completions + model="main/gpt-5.5-pro"  → X-Route-Model: old/gpt-5.5-pro   → prefix rewritten
POST /v1/responses        + model="old/gpt-5.5-pro"   → X-Route-Model: main/gpt-5.5-pro  → prefix rewritten

Key design: handleRequest(r) reads r.requestText, extracts body.model, calls stripPrefix() + prefixForEndpoint(r.uri), stores result in $route_model (js_var), then r.internalRedirect("/_omni_proxy" + r.uri). The internal proxy location adds proxy_set_header X-Route-Model $route_model — OmniRoute's existing resolveRoutingModel() picks it up. Original body untouched → SSE streaming preserved.

Non-KFC models (e.g. gemini-*, any model with unknown provider prefix like gemini-cli/) are skipped by shouldSkipModel() and resolved via OmniRoute aliases as before.

Deployed & tested on production — all 5 routing scenarios verified. 24 KFC/KFC-Old aliases removed from DB (9 gemini aliases kept).

Link to Devin session: https://app.devin.ai/sessions/0a727cac8ccc444e8356683bb6bed778
Requested by: @fyodoregor

Adds njs module and nginx config for transparent endpoint-based
provider routing without modifying OmniRoute code:

- /v1/responses → main/ prefix → KFC (responses provider)
- /v1/chat/completions → old/ prefix → KFC-Old (chat provider)

The njs handler reads body.model, strips/rewrites the prefix based
on which endpoint the client called, sets X-Route-Model header via
internalRedirect. OmniRoute's resolveRoutingModel() picks up the
header for routing. Original request body is never modified, SSE
streaming is fully preserved.

Non-KFC models (gemini-*) are skipped by the rewriter and resolved
via OmniRoute aliases as before.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fyodoregor fyodoregor self-assigned this Jul 6, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

antonsherbakov86 and others added 2 commits July 6, 2026 19:53
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review

Comment on lines +72 to +98
location /v1 {
proxy_pass http://127.0.0.1:20131;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400;

# CORS
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept, Origin, User-Agent, X-Requested-With' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;

if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept, Origin, User-Agent, X-Requested-With';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Fallback-location /v1 не фильтрует клиентский заголовок X-Route-Model

Блок location /v1 (omnirouter.conf:72-98) проксирует запросы к OmniRoute без установки proxy_set_header X-Route-Model. В отличие от /_omni_proxy/ (строка 51), где заголовок явно устанавливается из $route_model, здесь nginx пропускает оригинальный клиентский заголовок X-Route-Model как есть. Это означает, что клиент может напрямую отправить X-Route-Model: any/model на любой /v1/* эндпоинт (кроме /v1/responses и /v1/chat/completions) и переопределить маршрутизацию. Функция resolveRoutingModel() в src/sse/handlers/resolveRoutingModel.ts:15-16 примет этот заголовок. Это может быть намеренным (документировано в PR cheaper-inference#4863), но стоит убедиться, что это не создаёт нежелательный обход логики маршрутизации для эндпоинтов, которые не проходят через njs.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional and not a regression. X-Route-Model is a documented OmniRoute feature (PR cheaper-inference#4863) — it's designed to let proxies/callers override routing. The /v1 fallback location handles endpoints like /v1/models, /v1/embeddings etc. that don't need model prefix rewriting. Clients who send X-Route-Model directly are using the feature as designed, and the header still passes through enforceApiKeyPolicy so it can't bypass per-key allowlists.

Note: this OmniRoute PR is a duplicate — the canonical PR was merged in ai-service (cheaper-inference#140).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants