fix(ai): exclude Cerebras from supportsStrictMode (#9804)

Make strict mode as unsupported in cerebras, as mixed strict/unstrict tool usage doesn't work (400 from API), and extensions may define both strict and unstrict tools
This commit is contained in:
EdenGottlieb 2026-09-20 13:52:18 +03:00 • committed by GitHub
parent 0e283203c7
commit af7359b904
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 71 additions and 6 deletions

View file

@ -670,13 +670,13 @@ function detectOpenAICompletionsCompat(model: Model<"openai-completions">): Open
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
const isNvidia = provider === "nvidia" || baseUrl.includes("integrate.api.nvidia.com");
const isAntLing = provider === "ant-ling" || baseUrl.includes("api.ant-ling.com");
const isCerebras = provider === "cerebras" || baseUrl.includes("cerebras.ai");
const isTogetherReasoningOnly = isTogether && TOGETHER_REASONING_ONLY_MODELS.has(model.id);
const isDeepSeek = provider === "deepseek" || baseUrl.toLowerCase().includes("deepseek.com");
const isNonStandard =
isNvidia ||
provider === "cerebras" ||
baseUrl.includes("cerebras.ai") ||
isCerebras ||
provider === "xai" ||
baseUrl.includes("api.x.ai") ||
isTogether ||
@ -734,7 +734,7 @@ function detectOpenAICompletionsCompat(model: Model<"openai-completions">): Open
chatTemplateKwargs: {},
chatTemplateArgs: {},
zaiToolStream: false,
supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia,
supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isCerebras,
supportsOpenAIGrammarTools: false,
supportsMidConvoSystemMessages: false,
supportsMidConvoToolAdditions: false,

View file

@ -1597,12 +1597,12 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
const isNvidia = provider === "nvidia" || baseUrl.includes("integrate.api.nvidia.com");
const isAntLing = provider === "ant-ling" || baseUrl.includes("api.ant-ling.com");
const isCerebras = provider === "cerebras" || baseUrl.includes("cerebras.ai");
const isDeepSeek = provider === "deepseek" || baseUrl.toLowerCase().includes("deepseek.com");
const isNonStandard =
isNvidia ||
provider === "cerebras" ||
baseUrl.includes("cerebras.ai") ||
isCerebras ||
provider === "xai" ||
baseUrl.includes("api.x.ai") ||
isTogether ||
@ -1661,7 +1661,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
zaiToolStream: false,
supportsThinkingTokenBudget: false,
thinkingTokenBudgetField: undefined,
supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia,
supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isCerebras,
supportsOpenAIGrammarTools: false,
supportsMidConvoSystemMessages: false,
supportsMidConvoToolAdditions: false,

View file

@ -535,5 +535,70 @@ describe("Cache Retention (PI_CACHE_RETENTION)", () => {
expect(capturedPayload?.prompt_cache_key).toBeUndefined();
expect(capturedPayload?.prompt_cache_retention).toBeUndefined();
});
it.each([MODELS.cerebras["gpt-oss-120b"], MODELS.cerebras["qwen-3.8-27b"]] as const)(
"should omit strict field on tools for cerebras/$id",
async (metadata) => {
const model = metadata as Model<"openai-completions">;
const contextWithTools = {
messages: [
{
role: "system" as const,
content: "test",
toolsAdded: [
{
name: "t1",
description: "strict tool",
parameters: {
type: "object" as const,
properties: { x: { type: "string" } },
required: ["x"],
},
constrainedSampling: { type: "json_schema" as const },
},
{
name: "t2",
description: "non-strict tool",
parameters: {
type: "object" as const,
properties: { y: { type: "string" } },
required: ["y"],
},
},
],
timestamp: 0,
},
{ role: "user" as const, content: "hello", timestamp: 1 },
],
};
let capturedPayload: any;
try {
const s = streamOpenAICompletions(model, contextWithTools as any, {
apiKey: "fake-key",
sessionId: "test",
onPayload: stopAfterPayload((payload: any) => {
capturedPayload = payload;
}),
});
for await (const event of s) {
if (event.type === "error") break;
}
} catch {
// Expected to fail
}
expect(model.compat?.supportsStrictMode).toBe(false);
expect(capturedPayload).toBeDefined();
const tools = capturedPayload?.tools as any[] | undefined;
expect(tools).toBeDefined();
for (const tool of tools!) {
expect(tool.function).not.toHaveProperty("strict");
}
},
);
});
});