From a68aa39d9d77b1d8251a66146f5a371a4694fa09 Mon Sep 17 00:00:00 2001 From: sh940701 Date: Wed, 23 Sep 2026 17:56:17 +0900 Subject: [PATCH 1/2] test(xai): reasoning models must not receive penalty parameters --- tests/providers/xai/xai-transport.test.ts | 60 ++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/providers/xai/xai-transport.test.ts b/tests/providers/xai/xai-transport.test.ts index 157d38f71b2..4faf3866c1d 100644 --- a/tests/providers/xai/xai-transport.test.ts +++ b/tests/providers/xai/xai-transport.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { createOpenAIChatAdapter } from "../../../src/adapters/openai-chat"; +import { buildOpenAIChatPassthroughRequest, createOpenAIChatAdapter } from "../../../src/adapters/openai-chat"; import { parseRequest } from "../../../src/responses/parser"; import { buildModelsRequest } from "../../../src/oauth"; import { @@ -11,6 +11,7 @@ import { XAI_GROK_CLIENT_VERSION, } from "../../../src/providers/xai-transport"; import { getProviderRegistryEntry } from "../../../src/providers/registry"; +import { routedProviderConfig } from "../../../src/router"; import { XAI_RESPONSES_OPT_IN_MODELS, xaiResponsesOptInState } from "../../../src/providers/xai-responses-opt-in"; import { resolveWireProtocolOverride } from "../../../src/server/adapter-resolve"; import type { OcxAssistantMessage, OcxParsedRequest, OcxProviderConfig } from "../../../src/types"; @@ -816,3 +817,60 @@ describe("xAI reasoning_content cache preservation", () => { expect(req.context.messages).toHaveLength(1); }); }); + +// docs.x.ai/docs/guides/reasoning: "presencePenalty, frequencyPenalty, and stop cannot be used +// with reasoning models. Requests that include them return an error." Live 2026-09-23 through +// the local proxy: xai/grok-4.7 answers 200 without penalties and 400 invalid-argument "Model +// grok-4.7 does not support parameter presencePenalty." with presence_penalty (likewise +// frequency_penalty). +describe("xAI reasoning models reject penalty parameters", () => { + const REASONING = [ + "grok-4.7", + "grok-4.6", + "grok-4.5", + "grok-4.3", + "grok-4.20-multi-agent-0309", + "grok-4.20-0309-reasoning", + "grok-build-0.1", + ]; + const penalties = (modelId: string): OcxParsedRequest => ({ + modelId, + context: { messages: [{ role: "user", content: "hi", timestamp: 0 }] }, + stream: false, + options: { presencePenalty: 0.1, frequencyPenalty: 0.2 }, + }); + const routedXai = (): OcxProviderConfig => routedProviderConfig("xai", { + adapter: "openai-chat", + baseUrl: "https://api.x.ai/v1", + apiKey: "sk-test", + authMode: "key", + }); + + test("the registry seeds the documented reasoning ids and leaves non-reasoning ids alone", () => { + const xai = getProviderRegistryEntry("xai"); + expect(xai?.noPenaltyModels).toEqual(REASONING); + expect(xai?.noPenaltyModels).not.toContain("grok-4.20-0309-non-reasoning"); + expect(xai?.noPenaltyModels).not.toContain("grok-composer-2.5-fast"); + }); + + test("openai-chat omits both penalties for grok-4.7 and forwards them for a non-reasoning id", () => { + const adapter = createOpenAIChatAdapter(routedXai()); + const dropped = JSON.parse(adapter.buildRequest(penalties("grok-4.7")).body as string) as Record; + expect(dropped.presence_penalty).toBeUndefined(); + expect(dropped.frequency_penalty).toBeUndefined(); + const kept = JSON.parse(adapter.buildRequest(penalties("grok-composer-2.5-fast")).body as string) as Record; + expect(kept.presence_penalty).toBe(0.1); + expect(kept.frequency_penalty).toBe(0.2); + }); + + test("the Chat passthrough drops both penalties for grok-4.7", () => { + const body = JSON.parse(buildOpenAIChatPassthroughRequest( + routedXai(), + { model: "grok-4.7", messages: [], presence_penalty: 0.1, frequency_penalty: 0.2 }, + "grok-4.7", + false, + ).body) as Record; + expect(body.presence_penalty).toBeUndefined(); + expect(body.frequency_penalty).toBeUndefined(); + }); +}); From 42fc5f81b867a682d0557afdd6717a8cb2da4fb8 Mon Sep 17 00:00:00 2001 From: sh940701 Date: Wed, 23 Sep 2026 18:06:20 +0900 Subject: [PATCH 2/2] fix(xai): drop penalties for the reasoning models that reject them xAI documents that presencePenalty and frequencyPenalty cannot be used with reasoning models and that requests including them return an error (docs.x.ai/docs/guides/reasoning). Live through the proxy, xai/grok-4.7 answers 200 without penalties and 400 invalid-argument "Model grok-4.7 does not support parameter presencePenalty." with presence_penalty, and likewise with frequency_penalty. noPenaltyModels is already honoured by the openai-chat adapter and the Chat passthrough, and routedProviderConfig fills it from the registry, so seeding the xAI entry with its documented reasoning ids is the whole fix. Non-reasoning ids (grok-4.20-0309-non-reasoning, grok-composer-2.5-fast) keep caller penalties. --- src/providers/registry/entries-core.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/providers/registry/entries-core.ts b/src/providers/registry/entries-core.ts index dbb2b4890cc..c3035e2306a 100644 --- a/src/providers/registry/entries-core.ts +++ b/src/providers/registry/entries-core.ts @@ -278,6 +278,19 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [ // absent from xAI's documented API, so a model discovered later has no more support for it // than the seeded ones do. supportsVerbosity: false, + // docs.x.ai/docs/guides/reasoning: presencePenalty and frequencyPenalty "cannot be used with + // reasoning models. Requests that include them return an error." Live 2026-09-23: grok-4.7 + // answers 400 invalid-argument "Model grok-4.7 does not support parameter presencePenalty." + // Non-reasoning ids keep caller penalties. + noPenaltyModels: [ + "grok-4.7", + "grok-4.6", + "grok-4.5", + "grok-4.3", + "grok-4.20-multi-agent-0309", + "grok-4.20-0309-reasoning", + "grok-build-0.1", + ], defaultModel: "grok-4.5", // Grok 4.7/4.6/4.5 subscription Responses callers use the native wire with the existing // namespace/web-search/replay normalization. Chat remains an explicit modelAdapters