mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
fix(chat): preserve unstripped model string for passthrough provider routing (#11840)
Boarded with #12003/#11841/#11839 in one combined worktree: typecheck:core, check:file-size, check:changelog-integrity, check:complexity, check:cognitive-complexity, check:cycles all green; 24/24 focused tests pass. Contained fix — preserves the unstripped model string for passthrough providers (cline/kilocode) only when the combo actually redirected to a passthrough provider. Thanks for the regression coverage.
This commit is contained in:
@@ -59,6 +59,7 @@ import {
|
||||
getModelTargetFormat,
|
||||
PROVIDER_ID_TO_ALIAS,
|
||||
} from "@omniroute/open-sse/config/providerModels.ts";
|
||||
import { getPassthroughProviders } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
import * as log from "../utils/logger";
|
||||
import { checkAndRefreshToken } from "../services/tokenRefresh";
|
||||
import { createHookContext, runHooks, initPreRequestRegistry } from "@/lib/middleware/registry";
|
||||
@@ -1098,7 +1099,7 @@ async function handleChatImplementation(
|
||||
return credentials;
|
||||
})(),
|
||||
cachedSettings: settings,
|
||||
providerId: target?.providerId ?? null,
|
||||
providerId: target?.providerId ?? (target as any)?.provider ?? null,
|
||||
correlationId: reqId,
|
||||
conversationId,
|
||||
modelPinned: (target as any)?.modelPinned ?? false,
|
||||
@@ -1389,7 +1390,7 @@ async function handleSingleModelChat(
|
||||
comboExecutionKey: null,
|
||||
skipUpstreamRetry: resolvedTarget?.failoverBeforeRetry === true,
|
||||
allowRateLimitedConnection: resolvedTarget?.allowRateLimitedConnection === true,
|
||||
providerId: resolvedTarget?.providerId ?? null,
|
||||
providerId: resolvedTarget?.providerId ?? (resolvedTarget as any)?.provider ?? null,
|
||||
correlationId: runtimeOptions?.correlationId ?? null,
|
||||
reasoningTransportFallback:
|
||||
redirectCombo.config?.reasoningTransportFallback === "skip" ? "skip" : "drop",
|
||||
@@ -1738,10 +1739,18 @@ async function handleSingleModelChat(
|
||||
// defaultModel, resolve the bare name to that real model ID before the
|
||||
// upstream call so the provider receives a concrete model rather than the
|
||||
// placeholder. A "/"-qualified model name is always left untouched.
|
||||
const effectiveModel =
|
||||
let effectiveModel =
|
||||
resolveBareModelToConnectionDefault(modelStr, model, credentials.defaultModel) ?? model;
|
||||
let requestBody =
|
||||
effectiveModel !== model ? { ...body, model: `${provider}/${effectiveModel}` } : body;
|
||||
|
||||
// If the combo explicitly overrode the provider to a passthrough provider, we
|
||||
// must preserve the original unstripped modelStr so that proxy providers
|
||||
// (e.g., cline, kilocode) get the exact string they expect.
|
||||
if (provider !== resolvedProvider && getPassthroughProviders().has(provider)) {
|
||||
effectiveModel = modelStr;
|
||||
requestBody = { ...body, model: modelStr };
|
||||
}
|
||||
if (!runtimeOptions.reasoningDecision && runtimeOptions.reasoningIntent) {
|
||||
const connectionRouting = await applyConnectionReasoningRule({
|
||||
requestBody,
|
||||
|
||||
47
tests/unit/chat-passthrough-model-routing.test.ts
Normal file
47
tests/unit/chat-passthrough-model-routing.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { getPassthroughProviders } from "../../open-sse/config/providerRegistry.ts";
|
||||
|
||||
test("src/sse/handlers/chat.ts imports cleanly without module or syntax errors", async () => {
|
||||
const chatHandler = await import("../../src/sse/handlers/chat.ts");
|
||||
assert.ok(chatHandler, "chat.ts handler module should export successfully");
|
||||
});
|
||||
|
||||
test("getPassthroughProviders includes expected proxy/passthrough providers", () => {
|
||||
const providers = getPassthroughProviders();
|
||||
assert.ok(providers.has("cline"), "cline should be a passthrough provider");
|
||||
assert.ok(providers.has("kilocode"), "kilocode should be a passthrough provider");
|
||||
});
|
||||
|
||||
test("passthrough model routing preserves original unstripped modelStr when provider overrides to passthrough", () => {
|
||||
const passthroughProviders = getPassthroughProviders();
|
||||
const provider: string = "cline";
|
||||
const resolvedProvider: string = "openai";
|
||||
const modelStr = "cline/gpt-4o-mini";
|
||||
const model = "gpt-4o-mini";
|
||||
const body = { model: "cline/gpt-4o-mini", messages: [] };
|
||||
|
||||
let effectiveModel = model;
|
||||
let requestBody = { ...body, model: `cline/${effectiveModel}` };
|
||||
|
||||
if (provider !== resolvedProvider && passthroughProviders.has(provider)) {
|
||||
effectiveModel = modelStr;
|
||||
requestBody = { ...body, model: modelStr };
|
||||
}
|
||||
|
||||
assert.equal(effectiveModel, "cline/gpt-4o-mini");
|
||||
assert.equal(requestBody.model, "cline/gpt-4o-mini");
|
||||
});
|
||||
|
||||
test("providerId falls back to target.provider when target.providerId is absent", () => {
|
||||
const targetWithProviderId = { providerId: "prov_123", provider: "cline" };
|
||||
const targetWithProviderOnly = { provider: "kilocode" };
|
||||
const targetEmpty = {};
|
||||
|
||||
const resolveProviderId = (target: { providerId?: string; provider?: string }) =>
|
||||
target?.providerId ?? target?.provider ?? null;
|
||||
|
||||
assert.equal(resolveProviderId(targetWithProviderId), "prov_123");
|
||||
assert.equal(resolveProviderId(targetWithProviderOnly), "kilocode");
|
||||
assert.equal(resolveProviderId(targetEmpty), null);
|
||||
});
|
||||
Reference in New Issue
Block a user