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:
Syed Raheemuddin
2026-08-30 11:21:53 +05:30
committed by GitHub
parent da678bd3ff
commit 92574de164
2 changed files with 59 additions and 3 deletions

View File

@@ -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,

View 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);
});