fix(xai): restore pi model drift coverage

This commit is contained in:
Vincent Koc
2026-03-22 17:51:18 -07:00
parent 8eb7d3543e
commit 88859c974f
3 changed files with 110 additions and 10 deletions

View File

@@ -7,6 +7,8 @@ export const XAI_DEFAULT_CONTEXT_WINDOW = 256_000;
export const XAI_LARGE_CONTEXT_WINDOW = 2_000_000;
export const XAI_CODE_CONTEXT_WINDOW = 256_000;
export const XAI_DEFAULT_MAX_TOKENS = 64_000;
export const XAI_LEGACY_CONTEXT_WINDOW = 131_072;
export const XAI_LEGACY_MAX_TOKENS = 8_192;
type XaiCost = ModelDefinitionConfig["cost"];
@@ -49,10 +51,46 @@ const XAI_CODE_FAST_COST = {
} satisfies XaiCost;
const XAI_MODEL_CATALOG = [
{
id: "grok-3",
name: "Grok 3",
reasoning: false,
input: ["text"],
contextWindow: XAI_LEGACY_CONTEXT_WINDOW,
maxTokens: XAI_LEGACY_MAX_TOKENS,
cost: XAI_GROK_4_COST,
},
{
id: "grok-3-fast",
name: "Grok 3 Fast",
reasoning: false,
input: ["text"],
contextWindow: XAI_LEGACY_CONTEXT_WINDOW,
maxTokens: XAI_LEGACY_MAX_TOKENS,
cost: { input: 5, output: 25, cacheRead: 1.25, cacheWrite: 0 },
},
{
id: "grok-3-mini",
name: "Grok 3 Mini",
reasoning: true,
input: ["text"],
contextWindow: XAI_LEGACY_CONTEXT_WINDOW,
maxTokens: XAI_LEGACY_MAX_TOKENS,
cost: { input: 0.3, output: 0.5, cacheRead: 0.075, cacheWrite: 0 },
},
{
id: "grok-3-mini-fast",
name: "Grok 3 Mini Fast",
reasoning: true,
input: ["text"],
contextWindow: XAI_LEGACY_CONTEXT_WINDOW,
maxTokens: XAI_LEGACY_MAX_TOKENS,
cost: { input: 0.6, output: 4, cacheRead: 0.15, cacheWrite: 0 },
},
{
id: "grok-4",
name: "Grok 4",
reasoning: false,
reasoning: true,
input: ["text"],
contextWindow: XAI_DEFAULT_CONTEXT_WINDOW,
maxTokens: XAI_DEFAULT_MAX_TOKENS,
@@ -71,7 +109,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4-fast",
name: "Grok 4 Fast",
reasoning: true,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_FAST_COST,
@@ -80,7 +118,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4-fast-non-reasoning",
name: "Grok 4 Fast (Non-Reasoning)",
reasoning: false,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_FAST_COST,
@@ -89,7 +127,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4-1-fast",
name: "Grok 4.1 Fast",
reasoning: true,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_FAST_COST,
@@ -98,7 +136,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4-1-fast-non-reasoning",
name: "Grok 4.1 Fast (Non-Reasoning)",
reasoning: false,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_FAST_COST,
@@ -107,7 +145,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4.20-beta-latest-reasoning",
name: "Grok 4.20 Beta Latest (Reasoning)",
reasoning: true,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_GROK_420_COST,
@@ -116,7 +154,7 @@ const XAI_MODEL_CATALOG = [
id: "grok-4.20-beta-latest-non-reasoning",
name: "Grok 4.20 Beta Latest (Non-Reasoning)",
reasoning: false,
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: XAI_GROK_420_COST,
@@ -182,6 +220,29 @@ export function resolveXaiCatalogEntry(modelId: string): ModelDefinitionConfig |
cost: XAI_CODE_FAST_COST,
});
}
if (
lower.startsWith("grok-3-mini-fast") ||
lower.startsWith("grok-3-mini") ||
lower.startsWith("grok-3-fast") ||
lower.startsWith("grok-3")
) {
const legacyCost = lower.startsWith("grok-3-mini-fast")
? { input: 0.6, output: 4, cacheRead: 0.15, cacheWrite: 0 }
: lower.startsWith("grok-3-mini")
? { input: 0.3, output: 0.5, cacheRead: 0.075, cacheWrite: 0 }
: lower.startsWith("grok-3-fast")
? { input: 5, output: 25, cacheRead: 1.25, cacheWrite: 0 }
: XAI_GROK_4_COST;
return toModelDefinition({
id: modelId.trim(),
name: modelId.trim(),
reasoning: lower.includes("mini"),
input: ["text"],
contextWindow: XAI_LEGACY_CONTEXT_WINDOW,
maxTokens: XAI_LEGACY_MAX_TOKENS,
cost: legacyCost,
});
}
if (
lower.startsWith("grok-4.20") ||
lower.startsWith("grok-4-1") ||
@@ -191,7 +252,7 @@ export function resolveXaiCatalogEntry(modelId: string): ModelDefinitionConfig |
id: modelId.trim(),
name: modelId.trim(),
reasoning: !lower.includes("non-reasoning"),
input: ["text"],
input: ["text", "image"],
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
maxTokens: 30_000,
cost: lower.startsWith("grok-4.20") ? XAI_GROK_420_COST : XAI_FAST_COST,

View File

@@ -7,6 +7,7 @@ describe("xai provider models", () => {
expect(resolveXaiCatalogEntry("grok-4-1-fast")).toMatchObject({
id: "grok-4-1-fast",
reasoning: true,
input: ["text", "image"],
contextWindow: 2_000_000,
maxTokens: 30_000,
});
@@ -22,6 +23,7 @@ describe("xai provider models", () => {
expect(resolveXaiCatalogEntry("grok-4.20-beta-latest-reasoning")).toMatchObject({
id: "grok-4.20-beta-latest-reasoning",
reasoning: true,
input: ["text", "image"],
contextWindow: 2_000_000,
});
expect(resolveXaiCatalogEntry("grok-4.20-beta-latest-non-reasoning")).toMatchObject({
@@ -46,10 +48,25 @@ describe("xai provider models", () => {
});
});
it("publishes the remaining Grok 3 family that Pi still carries", () => {
expect(resolveXaiCatalogEntry("grok-3-mini-fast")).toMatchObject({
id: "grok-3-mini-fast",
reasoning: true,
contextWindow: 131_072,
maxTokens: 8_192,
});
expect(resolveXaiCatalogEntry("grok-3-fast")).toMatchObject({
id: "grok-3-fast",
reasoning: false,
contextWindow: 131_072,
maxTokens: 8_192,
});
});
it("marks current Grok families as modern while excluding multi-agent ids", () => {
expect(isModernXaiModel("grok-4.20-beta-latest-reasoning")).toBe(true);
expect(isModernXaiModel("grok-code-fast-1")).toBe(true);
expect(isModernXaiModel("grok-3-mini-fast")).toBe(false);
expect(isModernXaiModel("grok-3-mini-fast")).toBe(true);
expect(isModernXaiModel("grok-4.20-multi-agent-experimental-beta-0304")).toBe(false);
});
@@ -78,6 +95,18 @@ describe("xai provider models", () => {
},
},
});
const grok3Mini = resolveXaiForwardCompatModel({
providerId: "xai",
ctx: {
provider: "xai",
modelId: "grok-3-mini-fast",
modelRegistry: { find: () => null } as never,
providerConfig: {
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
},
},
});
expect(grok41).toMatchObject({
provider: "xai",
@@ -94,9 +123,19 @@ describe("xai provider models", () => {
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
reasoning: true,
input: ["text", "image"],
contextWindow: 2_000_000,
maxTokens: 30_000,
});
expect(grok3Mini).toMatchObject({
provider: "xai",
id: "grok-3-mini-fast",
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
reasoning: true,
contextWindow: 131_072,
maxTokens: 8_192,
});
});
it("refuses the unsupported multi-agent endpoint ids", () => {

View File

@@ -5,7 +5,7 @@ import type {
import { applyXaiModelCompat, normalizeModelCompat } from "openclaw/plugin-sdk/provider-models";
import { resolveXaiCatalogEntry, XAI_BASE_URL } from "./model-definitions.js";
const XAI_MODERN_MODEL_PREFIXES = ["grok-4", "grok-code-fast"] as const;
const XAI_MODERN_MODEL_PREFIXES = ["grok-3", "grok-4", "grok-code-fast"] as const;
export function isModernXaiModel(modelId: string): boolean {
const lower = modelId.trim().toLowerCase();