fix(xai): suppress unsupported multi-agent models

This commit is contained in:
Peter Steinberger
2026-06-19 05:12:07 -04:00
parent e29576d25f
commit b58d798381
6 changed files with 110 additions and 24 deletions

View File

@@ -504,9 +504,10 @@ Legacy aliases still normalize to the canonical bundled ids:
sign-in URL. xAI decides which accounts can receive OAuth API tokens, and
the consent page may show Grok Build even though OpenClaw does not require
the Grok Build app.
- `grok-4.20-multi-agent-experimental-beta-0304` is not supported on the
normal xAI provider path because it requires a different upstream API
surface than the standard OpenClaw xAI transport.
- OpenClaw does not currently expose the xAI multi-agent model family. xAI
serves these models through the Responses API, but they do not accept the
client-side or custom tools used by OpenClaw's shared agent loop. See the
[xAI multi-agent limitations](https://docs.x.ai/developers/model-capabilities/text/multi-agent#limitations).
- xAI Realtime voice is not registered as an OpenClaw provider yet. It
needs a different bidirectional voice session contract than batch STT or
streaming transcription.

View File

@@ -262,12 +262,6 @@ export default defineSingleProviderPluginEntry({
resolveThinkingProfile,
isModernModelRef: ({ modelId }) => isModernXaiModel(modelId),
classifyFailoverReason: ({ errorMessage }) => classifyXaiFailoverReason(errorMessage),
buildUnknownModelHint: ({ modelId }) => {
if (modelId.toLowerCase().includes("multi-agent")) {
return "xAI multi-agent models are not supported on the standard xAI API-key provider path. See https://docs.openclaw.ai/providers/xai for supported models.";
}
return undefined;
},
},
register(api) {
api.registerWebSearchProvider(createXaiWebSearchProvider());

View File

@@ -31,6 +31,45 @@
}
}
},
"modelCatalog": {
"suppressions": [
{
"provider": "xai",
"model": "grok-4.20-multi-agent-0309",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent-latest",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent-beta-latest",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent-experimental-beta-0304",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent-experimental-beta-latest",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
},
{
"provider": "xai",
"model": "grok-4.20-multi-agent-beta-0309",
"reason": "OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai."
}
]
},
"syntheticAuthRefs": ["xai"],
"setup": {
"providers": [

View File

@@ -0,0 +1,34 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const manifest = JSON.parse(
readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8"),
) as {
modelCatalog?: {
suppressions?: Array<{ provider?: string; model?: string }>;
};
};
const XAI_MULTI_AGENT_MODELS = [
"grok-4.20-multi-agent-0309",
"grok-4.20-multi-agent",
"grok-4.20-multi-agent-latest",
"grok-4.20-multi-agent-beta-latest",
"grok-4.20-multi-agent-experimental-beta-0304",
"grok-4.20-multi-agent-experimental-beta-latest",
"grok-4.20-multi-agent-beta-0309",
] as const;
describe("xAI plugin manifest", () => {
it("suppresses the unsupported multi-agent model aliases", () => {
const suppressionRefs = new Set(
(manifest.modelCatalog?.suppressions ?? []).map(
(suppression) => `${suppression.provider}/${suppression.model}`,
),
);
for (const model of XAI_MULTI_AGENT_MODELS) {
expect(suppressionRefs).toContain(`xai/${model}`);
}
});
});

View File

@@ -61,6 +61,10 @@ vi.mock("../model-suppression.js", () => {
return undefined;
}
function isUnsupportedXaiMultiAgentModel(provider?: string, id?: string): boolean {
return provider === "xai" && id?.trim().toLowerCase() === "grok-4.20-multi-agent-0309";
}
return {
shouldSuppressBuiltInModel: ({
provider,
@@ -79,6 +83,9 @@ vi.mock("../model-suppression.js", () => {
) {
return true;
}
if (isUnsupportedXaiMultiAgentModel(provider, id)) {
return true;
}
return (
(provider === "qwen" || provider === "modelstudio") &&
id?.trim().toLowerCase() === "qwen3.6-plus" &&
@@ -92,7 +99,7 @@ vi.mock("../model-suppression.js", () => {
) {
return true;
}
return false;
return isUnsupportedXaiMultiAgentModel(provider, id);
},
buildSuppressedBuiltInModelError: ({
provider,
@@ -116,6 +123,9 @@ vi.mock("../model-suppression.js", () => {
) {
return `Unknown model: ${provider}/gpt-5.3-codex-spark. gpt-5.3-codex-spark is available only through ChatGPT/Codex OAuth. Run \`openclaw models auth login --provider openai\` and use openai/gpt-5.3-codex-spark with that OAuth profile; OpenAI API-key auth cannot use this model.`;
}
if (isUnsupportedXaiMultiAgentModel(provider, id)) {
return "Unknown model: xai/grok-4.20-multi-agent-0309. OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai.";
}
return undefined;
},
};
@@ -3451,6 +3461,27 @@ describe("resolveModel", () => {
);
});
it("does not build a configured fallback for unsupported xAI multi-agent models", () => {
const cfg = {
models: {
providers: {
xai: {
baseUrl: "https://api.x.ai/v1",
api: "openai-completions",
models: [],
},
},
},
} as unknown as OpenClawConfig;
const result = resolveModelForTest("xai", "grok-4.20-multi-agent-0309", "/tmp/agent", cfg);
expect(result.model).toBeUndefined();
expect(result.error).toBe(
"Unknown model: xai/grok-4.20-multi-agent-0309. OpenClaw does not currently support xAI multi-agent models; choose another xAI model. See https://docs.openclaw.ai/providers/xai.",
);
});
it("rejects stale openai gpt-5.3-codex-spark discovery rows", () => {
mockDiscoveredModel(discoverModels, {
provider: "openai",

View File

@@ -522,7 +522,6 @@ function mergeStaticCatalogInlineModel(
}
function hasConfiguredFallbackSurface(params: {
provider?: string;
providerConfig: InlineProviderConfig | undefined;
configuredModel: ReturnType<typeof findConfiguredProviderModel>;
modelId: string;
@@ -533,18 +532,6 @@ function hasConfiguredFallbackSurface(params: {
if (params.configuredModel) {
return true;
}
// xAI's catalog explicitly excludes multi-agent model ids (they require a
// different upstream API surface than the standard API-key provider path).
// If the provider rejected this model via its dynamic resolution, do not
// synthesize a configured-provider fallback that will silently 400.
// See: https://docs.openclaw.ai/providers/xai
if (
params.provider &&
normalizeProviderId(params.provider) === "xai" &&
params.modelId.toLowerCase().includes("multi-agent")
) {
return false;
}
const baseUrl = params.providerConfig?.baseUrl?.trim();
return Boolean(baseUrl);
}
@@ -1258,7 +1245,7 @@ function resolveConfiguredFallbackModel(params: {
const providerConfig = resolveConfiguredProviderConfig(cfg, provider);
const requestTimeoutMs = resolveProviderRequestTimeoutMs(providerConfig?.timeoutSeconds);
const configuredModel = findConfiguredProviderModel(providerConfig, provider, modelId);
if (!hasConfiguredFallbackSurface({ provider, providerConfig, configuredModel, modelId })) {
if (!hasConfiguredFallbackSurface({ providerConfig, configuredModel, modelId })) {
return undefined;
}
const staticCatalogModel = resolveBundledStaticCatalogModel({