mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-09 03:22:40 +00:00
fix(xai): suppress unsupported multi-agent models
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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": [
|
||||
|
||||
34
extensions/xai/openclaw.plugin.test.ts
Normal file
34
extensions/xai/openclaw.plugin.test.ts
Normal 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}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user