mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 10:43:43 +03:00
Model-level targetFormat is provider-scoped endpoint semantics: a catalog entry declares how the DECLARING provider serves the model. getModelTargetFormat() fell back to getGlobalModel() when the provider's own catalog lacked the model id, importing another provider's tag into every provider serving that id. catalog. command-code serves gpt-5.6-luna over its chat-shaped /alpha/generate endpoint but inherited that tag, so chatCore translated the request to Responses format (messages -> input). CommandCodeExecutor.buildCommandCodeBody reads chat-format input.messages -> undefined -> [] -> upstream 502 "Invalid prompt: messages must not be empty" (call log 1786341194167-774a5b). Fix: resolve the provider alias (mirroring getProviderModels), only apply the provider's OWN catalog entry's targetFormat, and skip the global fallback when the provider has a catalog. Catalog-less providers keep the global fallback unchanged; ghe-copilot's Responses routing (#8835) is preserved. Regression test: tests/unit/provider-models-target-format-scoping.test.ts (red before the fix, green after).
39 lines
2.0 KiB
TypeScript
39 lines
2.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { getModelTargetFormat } from "../../open-sse/config/providerModels.ts";
|
|
import { resolveChatCoreTargetFormat } from "../../open-sse/handlers/chatCore/targetFormat.ts";
|
|
|
|
// Regression: #8835 tagged gpt-5.6-* with targetFormat "openai-responses" in the
|
|
// ghe-copilot catalog. getModelTargetFormat falls back to getGlobalModel() when
|
|
// the provider's own catalog lacks the model id, importing the DECLARING
|
|
// provider's endpoint semantics into every other provider serving the same id.
|
|
// command-code's chat-shaped /alpha/generate executor then received a
|
|
// Responses-format body (input, not messages) and shipped `messages: []`
|
|
// upstream — upstream rejected with "Invalid prompt: messages must not be empty"
|
|
// (502). Model-level targetFormat is provider-scoped: it must not leak.
|
|
test("model-level targetFormat does not leak across provider catalogs", () => {
|
|
// command-code serves gpt-5.6-luna over its chat-shaped /alpha/generate endpoint
|
|
assert.equal(getModelTargetFormat("cmd", "gpt-5.6-luna"), null);
|
|
// raw provider id form behaves identically (alias resolution)
|
|
assert.equal(getModelTargetFormat("command-code", "gpt-5.6-luna"), null);
|
|
// the declaring provider (ghe-copilot) keeps its Responses routing
|
|
assert.equal(getModelTargetFormat("gh", "gpt-5.6-luna"), "openai-responses");
|
|
assert.equal(getModelTargetFormat("ghe-copilot", "gpt-5.6-luna"), "openai-responses");
|
|
// unrelated models are unaffected
|
|
assert.equal(getModelTargetFormat("cmd", "kimi-k2"), null);
|
|
});
|
|
|
|
test("chat completions to command-code gpt-5.6-luna resolve to chat format", () => {
|
|
const resolved = resolveChatCoreTargetFormat({
|
|
provider: "command-code",
|
|
resolvedModel: "gpt-5.6-luna",
|
|
apiFormat: undefined,
|
|
sourceFormat: "openai",
|
|
customModelTargetFormat: undefined,
|
|
providerSpecificData: null,
|
|
});
|
|
assert.equal(resolved.alias, "cmd");
|
|
assert.equal(resolved.targetFormat, "openai");
|
|
});
|