fix(zenmux): normalize vendor-prefixed GLM system roles (#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.
This commit is contained in:
Arthur Bodera
2026-06-28 01:09:01 +10:00
committed by GitHub
parent 0b42ffe02c
commit f82bd3129e
2 changed files with 45 additions and 1 deletions

View File

@@ -57,6 +57,15 @@ const MODELS_WITHOUT_SYSTEM_ROLE = [
"ernie-", // Baidu ERNIE models
];
const PROVIDER_SCOPED_MODELS_WITHOUT_SYSTEM_ROLE: Record<string, RegExp[]> = {
// ZenMux exposes Z.AI GLM through OpenAI-compatible model ids such as
// "z-ai/glm-5.2". Z.AI rejects compressed histories that start with a
// system summary followed by an assistant/tool bundle, while OpenRouter
// tolerates the same shape. Treat these vendor-prefixed GLM ids like native
// GLM so normalizeSystemRole moves system/developer content into a user turn.
zenmux: [/(?:^|\/)glm(?:-|$)/i],
};
interface MessageContentPart {
type?: string;
text?: string;
@@ -88,9 +97,15 @@ function extractTextFromContent(content: unknown): string {
* Check if a provider+model combo supports the system role.
*/
function supportsSystemRole(provider: string, model: string): boolean {
if (PROVIDERS_WITHOUT_SYSTEM_ROLE.has(provider)) return false;
const providerLower = (provider || "").trim().toLowerCase();
if (PROVIDERS_WITHOUT_SYSTEM_ROLE.has(providerLower)) return false;
const modelLower = (model || "").toLowerCase();
for (const pattern of PROVIDER_SCOPED_MODELS_WITHOUT_SYSTEM_ROLE[providerLower] ?? []) {
if (pattern.test(modelLower)) return false;
}
for (const prefix of MODELS_WITHOUT_SYSTEM_ROLE) {
if (modelLower.startsWith(prefix)) return false;
}

View File

@@ -99,6 +99,35 @@ test("normalizeSystemRole inserts a user message when no user exists and drops e
]);
});
test("normalizeSystemRole treats ZenMux z-ai/glm models as GLM even with vendor prefix", () => {
const messages = [
{ role: "system", content: "[Context compressed: earlier messages removed]" },
{
role: "assistant",
content: null,
tool_calls: [
{
id: "call_1",
type: "function",
function: { name: "read", arguments: "{}" },
},
],
},
{ role: "tool", tool_call_id: "call_1", content: "ok" },
];
const result = normalizeSystemRole(messages, "zenmux", "z-ai/glm-5.2");
assert.deepEqual(result, [
{
role: "user",
content: "[System Instructions]\n[Context compressed: earlier messages removed]",
},
messages[1],
messages[2],
]);
});
test("normalizeRoles composes model, developer and system normalization in order", () => {
const messages = [
{ role: "model", content: "first answer" },