Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions open-sse/config/providerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,29 @@ export function findModelName(aliasOrId: string, modelId: string): string {
}

export function getModelTargetFormat(aliasOrId: string, modelId: string): string | null {
const models = PROVIDER_MODELS[aliasOrId];
const found = models?.find((m) => m.id === modelId) || getGlobalModel(modelId);
const alias = PROVIDER_ID_TO_ALIAS[aliasOrId] || aliasOrId;
const models = PROVIDER_MODELS[alias] || PROVIDER_MODELS[aliasOrId];
// Wire-format metadata is provider-scoped. A model's capabilities can be
// shared globally, but an OpenAI registry entry's Responses target must not
// leak into another provider's Chat Completions connection (e.g.
// Nous/OpenRouter `openai/gpt-5.6-luna`).
// Preserve the provider-prefix normalization used before the global model
// fallback was added: `openai/openai/gpt-5.6-luna` is an OpenAI-local
// spelling, not a reason to consult another provider's metadata.
const prefixes = [aliasOrId, alias].filter(
(value, index, values) => values.indexOf(value) === index
);
const bareModelId =
typeof modelId === "string"
? prefixes.reduce(
(current, prefix) =>
current === modelId && modelId.startsWith(`${prefix}/`)
? modelId.slice(prefix.length + 1)
: current,
modelId
)
: modelId;
const found = models?.find((m) => m.id === bareModelId);
if (found?.targetFormat) return found.targetFormat;
// #5842: OpenAI "*-pro" reasoning models (o1-pro, gpt-5.x-pro) are only served by
// the native /v1/responses endpoint — /v1/chat/completions 404s ("only supported
Expand Down
44 changes: 43 additions & 1 deletion tests/unit/openai-gpt56-responses-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@
import test from "node:test";
import assert from "node:assert/strict";

import { getModelTargetFormat } from "../../open-sse/config/providerModels.ts";
import {
getModelTargetFormat,
getProviderModel,
PROVIDER_ID_TO_ALIAS,
} from "../../open-sse/config/providerModels.ts";
import { DefaultExecutor } from "../../open-sse/executors/default.ts";
import { resolveChatCoreTargetFormat } from "../../open-sse/handlers/chatCore/targetFormat.ts";

test("getModelTargetFormat routes the public OpenAI GPT-5.6 family through Responses", () => {
for (const modelId of ["gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]) {
Expand All @@ -43,3 +48,40 @@ test("DefaultExecutor keeps /v1/chat/completions for gpt-5.4", () => {
const url = executor.buildUrl("gpt-5.4", true, 0, null);
assert.equal(url, "https://api.openai.com/v1/chat/completions");
});

test("cross-provider GPT-5.6 keeps the provider's Chat Completions wire format", () => {
for (const provider of ["nous", "openrouter"]) {
assert.equal(
getModelTargetFormat(provider, "openai/gpt-5.6-luna"),
null,
`${provider} must not inherit OpenAI's Responses target format`
);
assert.equal(
getModelTargetFormat(provider, "gpt-5.6-luna"),
null,
`${provider} must not inherit OpenAI's Responses target format for bare ids`
);
}
});

test("chatCore target-format resolution keeps provider defaults for cross-provider GPT-5.6", () => {
for (const provider of ["nous", "openrouter"]) {
const result = resolveChatCoreTargetFormat({
provider,
resolvedModel: "openai/gpt-5.6-luna",
apiFormat: undefined,
sourceFormat: "openai",
customModelTargetFormat: undefined,
providerSpecificData: undefined,
});
assert.equal(result.alias, PROVIDER_ID_TO_ALIAS[provider] || provider);
assert.equal(result.targetFormat, "openai");
}
});

test("global capability fallback remains available without leaking wire format", () => {
const metadata = getProviderModel("nous", "gpt-5.6-luna");
assert.equal(metadata?.supportsVision, true);
assert.equal(metadata?.supportsReasoning, true);
assert.equal(getModelTargetFormat("nous", "gpt-5.6-luna"), null);
});