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
53 changes: 52 additions & 1 deletion packages/actions/src/prepare-request-body.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,57 @@ describe("prepareRequestBody - Moonshot thinking", () => {
});
});

describe("prepareRequestBody - xAI reasoning_effort", () => {
async function prepareXai(
provider: Parameters<typeof prepareRequestBody>[0],
model: string,
externalId: string,
effort: Parameters<typeof prepareRequestBody>[14],
) {
return (await prepareRequestBody(
provider,
model,
null,
externalId,
[{ role: "user", content: "Hello!" }],
false, // stream
undefined, // temperature
undefined, // max_tokens
undefined, // top_p
undefined, // frequency_penalty
undefined, // presence_penalty
undefined, // response_format
undefined, // tools
undefined, // tool_choice
effort,
true, // supportsReasoning
)) as unknown as Record<string, unknown>;
}

test("forwards reasoning_effort for azure-ai-foundry/grok-4.3", async () => {
const requestBody = await prepareXai(
"azure-ai-foundry",
"grok-4-3",
"grok-4.3",
"high",
);
expect(requestBody.reasoning_effort).toBe("high");
});

test.each([
["xai", "grok-4", "grok-4"],
["xai", "grok-4-20-beta-0309-reasoning", "grok-4.20-beta-0309-reasoning"],
["vertex-openai", "grok-4-20-reasoning", "grok-4.20-reasoning"],
["xai", "grok-build-0-1", "grok-build-0.1"],
] as const)(
"drops reasoning_effort for %s/%s, which does not declare it",
async (provider, model, externalId) => {
const requestBody = await prepareXai(provider, model, externalId, "high");
expect(requestBody.reasoning_effort).toBeUndefined();
},
);
});

describe("prepareRequestBody - Alibaba thinking", () => {
async function prepare(options: {
model: string;
Expand Down Expand Up @@ -4304,7 +4355,7 @@ describe("prepareRequestBody - max_tokens forwarding", () => {
expect(requestBody.max_tokens).toBe(8192);
expect(requestBody.max_completion_tokens).toBeUndefined();
expect(requestBody.top_p).toBe(0.9);
expect(requestBody.reasoning_effort).toBeUndefined();
expect(requestBody.reasoning_effort).toBe("medium");
expect(requestBody.inferenceConfig).toBeUndefined();
});
});
Expand Down
21 changes: 21 additions & 0 deletions packages/models/src/model-metadata.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,25 @@ describe("model metadata", () => {

expect(offenders).toEqual([]);
});

// xAI accepts `reasoning_effort` on some deployments and rejects it on
// others, and the gateway forwards the value verbatim. A mapping that
// advertises the parameter has therefore been verified against its
// deployment, and that verification is what `reasoningEfforts` records —
// without it neither the gateway nor a client knows which tiers are safe.
it("declares reasoningEfforts for every xAI mapping that accepts reasoning_effort", () => {
const offenders = (models as readonly ModelDefinition[])
.filter((model) => model.family === "xai")
.flatMap((model) =>
(model.providers as readonly ProviderModelMapping[])
.filter(
(provider) =>
provider.supportedParameters?.includes("reasoning_effort") &&
!provider.reasoningEfforts?.length,
)
.map((provider) => `${provider.providerId}/${model.id}`),
);

expect(offenders).toEqual([]);
});
});
8 changes: 8 additions & 0 deletions packages/models/src/models/xai.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { ModelDefinition } from "@/models.js";

// `reasoning_effort` is deliberately absent: xAI accepts it only on some
// deployments (grok-4.3 and grok-4.5 do, grok-4 does not), and the gateway
// forwards it verbatim, so a mapping that advertises it on a deployment that
// rejects it turns working requests into upstream 400s. A mapping only leaves
// this list once its deployment is verified live, and then it also declares
// `reasoningEfforts` so clients can discover the tiers it accepts.
const xaiSupportedParamsNoFreqPresence = [
"temperature",
"max_tokens",
Expand Down Expand Up @@ -761,6 +767,7 @@ export const xaiModels = [
streaming: true,
vision: false,
reasoning: true,
reasoningEfforts: ["none", "low", "medium", "high"],
reasoningOutput: "omit",
tools: true,
jsonOutput: false,
Expand All @@ -770,6 +777,7 @@ export const xaiModels = [
"top_p",
"tools",
"tool_choice",
"reasoning_effort",
],
},
],
Expand Down
Loading