fix(executor): respect apiType="chat" in forceResponsesUpstream (#5483 regression) (#10946)

Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados todos verdes. Regressão real corrigida (apiType=chat agora é honrado em vez de forçado para /responses). CI vermelho é o base-red já rastreado em #9985.
This commit is contained in:
Jack Smith
2026-08-21 19:25:03 +08:00
committed by GitHub
parent 7011c5fafa
commit 14d2c90e23
2 changed files with 62 additions and 0 deletions

View File

@@ -28,6 +28,17 @@ export function shouldForceResponsesUpstream(
const providerSpecificData = credentials?.providerSpecificData ?? null;
if (providerSpecificData?._omnirouteForceResponsesUpstream === true) return true;
if (getOpenAICompatibleType(provider, providerSpecificData) === "responses") return false;
// apiType="chat" means the operator explicitly chose the chat/completions
// wire. Don't second-guess that choice by forcing /responses just because the
// body carries namespace tools — the standard namespace→flatten path
// (openai-responses.ts) handles those correctly for chat backends.
if (
providerSpecificData &&
typeof providerSpecificData.apiType === "string" &&
providerSpecificData.apiType === "chat"
) {
return false;
}
const hasResponsesShape =
body.input !== undefined ||

View File

@@ -9,6 +9,7 @@ import {
mergeUpstreamExtraHeaders,
setUserAgentHeader,
} from "../../open-sse/executors/base.ts";
import { shouldForceResponsesUpstream } from "../../open-sse/executors/forceResponsesUpstream.ts";
import { DefaultExecutor } from "../../open-sse/executors/default.ts";
import { PROVIDERS } from "../../open-sse/config/constants.ts";
import {
@@ -1578,3 +1579,53 @@ test("DefaultExecutor.execute does not produce duplicate anthropic-version heade
/^x-anthropic-billing-header: cc_version=2\.1\.220\.1f2; cc_entrypoint=cli; cch=[0-9a-f]{5};$/
);
});
test('shouldForceResponsesUpstream respects explicit apiType="chat" even when namespace tools are present', () => {
const body = {
input: "hi",
tools: [
{
type: "namespace",
name: "collaboration",
tools: [
{
name: "spawn_agent",
description: "Spawn an agent",
parameters: { type: "object", properties: { task: { type: "string" } } },
},
],
},
],
};
const credentials = {
providerSpecificData: {
baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3",
apiType: "chat",
},
};
assert.equal(
shouldForceResponsesUpstream("openai-compatible-responses-demo", body, credentials),
false
);
});
test("shouldForceResponsesUpstream still forces /responses for untyped OpenAI-compatible providers with namespace tools", () => {
const body = {
input: "hi",
tools: [
{
type: "namespace",
name: "collaboration",
tools: [
{ name: "spawn_agent", description: "Spawn an agent", parameters: { type: "object" } },
],
},
],
};
const credentials = {
providerSpecificData: {
baseUrl: "https://proxy.example/v1",
},
};
assert.equal(shouldForceResponsesUpstream("openai-compatible-test", body, credentials), true);
});