Add Grok 4.20 reasoning and non-reasoning to xAI model catalog (#50772)
Merged via squash. Prepared head SHA: 095e645ea58b2259b25c923aeaf11bbcb2990c8f Co-authored-by: Jaaneek <25470423+Jaaneek@users.noreply.github.com> Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com> Reviewed-by: @huntharo
This commit is contained in:
parent
f6b3245a7b
commit
916f496b51
@ -152,6 +152,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Telegram: stabilize pairing/session/forum routing and reply formatting tests (#50155) Thanks @joshavant.
|
- Telegram: stabilize pairing/session/forum routing and reply formatting tests (#50155) Thanks @joshavant.
|
||||||
- Hardening: refresh stale device pairing requests and pending metadata (#50695) Thanks @smaeljaish771 and @joshavant.
|
- Hardening: refresh stale device pairing requests and pending metadata (#50695) Thanks @smaeljaish771 and @joshavant.
|
||||||
- Gateway: harden OpenResponses file-context escaping (#50782) Thanks @YLChen-007 and @joshavant.
|
- Gateway: harden OpenResponses file-context escaping (#50782) Thanks @YLChen-007 and @joshavant.
|
||||||
|
- xAI/models: rename the bundled Grok 4.20 catalog entries to the GA IDs and normalize saved deprecated beta IDs at runtime so existing configs and sessions keep resolving. (#50772) thanks @Jaaneek
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
|||||||
@ -34,8 +34,7 @@ OpenClaw now includes these xAI model families out of the box:
|
|||||||
- `grok-4`, `grok-4-0709`
|
- `grok-4`, `grok-4-0709`
|
||||||
- `grok-4-fast-reasoning`, `grok-4-fast-non-reasoning`
|
- `grok-4-fast-reasoning`, `grok-4-fast-non-reasoning`
|
||||||
- `grok-4-1-fast-reasoning`, `grok-4-1-fast-non-reasoning`
|
- `grok-4-1-fast-reasoning`, `grok-4-1-fast-non-reasoning`
|
||||||
- `grok-4.20-experimental-beta-0304-reasoning`
|
- `grok-4.20-reasoning`, `grok-4.20-non-reasoning`
|
||||||
- `grok-4.20-experimental-beta-0304-non-reasoning`
|
|
||||||
- `grok-code-fast-1`
|
- `grok-code-fast-1`
|
||||||
|
|
||||||
The plugin also forward-resolves newer `grok-4*` and `grok-code-fast*` ids when
|
The plugin also forward-resolves newer `grok-4*` and `grok-code-fast*` ids when
|
||||||
|
|||||||
@ -59,14 +59,14 @@ const XAI_MODEL_CATALOG = [
|
|||||||
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "grok-4.20-experimental-beta-0304-reasoning",
|
id: "grok-4.20-reasoning",
|
||||||
name: "Grok 4.20 Experimental Beta 0304 (Reasoning)",
|
name: "Grok 4.20 (Reasoning)",
|
||||||
reasoning: true,
|
reasoning: true,
|
||||||
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "grok-4.20-experimental-beta-0304-non-reasoning",
|
id: "grok-4.20-non-reasoning",
|
||||||
name: "Grok 4.20 Experimental Beta 0304 (Non-Reasoning)",
|
name: "Grok 4.20 (Non-Reasoning)",
|
||||||
reasoning: false,
|
reasoning: false,
|
||||||
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
contextWindow: XAI_LARGE_CONTEXT_WINDOW,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,8 +16,21 @@ describe("xai provider models", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("publishes Grok 4.20 reasoning and non-reasoning models", () => {
|
||||||
|
expect(resolveXaiCatalogEntry("grok-4.20-reasoning")).toMatchObject({
|
||||||
|
id: "grok-4.20-reasoning",
|
||||||
|
reasoning: true,
|
||||||
|
contextWindow: 2_000_000,
|
||||||
|
});
|
||||||
|
expect(resolveXaiCatalogEntry("grok-4.20-non-reasoning")).toMatchObject({
|
||||||
|
id: "grok-4.20-non-reasoning",
|
||||||
|
reasoning: false,
|
||||||
|
contextWindow: 2_000_000,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("marks current Grok families as modern while excluding multi-agent ids", () => {
|
it("marks current Grok families as modern while excluding multi-agent ids", () => {
|
||||||
expect(isModernXaiModel("grok-4.20-experimental-beta-0304-reasoning")).toBe(true);
|
expect(isModernXaiModel("grok-4.20-reasoning")).toBe(true);
|
||||||
expect(isModernXaiModel("grok-code-fast-1")).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(false);
|
||||||
expect(isModernXaiModel("grok-4.20-multi-agent-experimental-beta-0304")).toBe(false);
|
expect(isModernXaiModel("grok-4.20-multi-agent-experimental-beta-0304")).toBe(false);
|
||||||
@ -40,7 +53,7 @@ describe("xai provider models", () => {
|
|||||||
providerId: "xai",
|
providerId: "xai",
|
||||||
ctx: {
|
ctx: {
|
||||||
provider: "xai",
|
provider: "xai",
|
||||||
modelId: "grok-4.20-experimental-beta-0304-reasoning",
|
modelId: "grok-4.20-reasoning",
|
||||||
modelRegistry: { find: () => null } as never,
|
modelRegistry: { find: () => null } as never,
|
||||||
providerConfig: {
|
providerConfig: {
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
@ -59,7 +72,7 @@ describe("xai provider models", () => {
|
|||||||
});
|
});
|
||||||
expect(grok420).toMatchObject({
|
expect(grok420).toMatchObject({
|
||||||
provider: "xai",
|
provider: "xai",
|
||||||
id: "grok-4.20-experimental-beta-0304-reasoning",
|
id: "grok-4.20-reasoning",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
baseUrl: "https://api.x.ai/v1",
|
baseUrl: "https://api.x.ai/v1",
|
||||||
reasoning: true,
|
reasoning: true,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { normalizeXaiModelId } from "openclaw/plugin-sdk/provider-models";
|
||||||
import { postTrustedWebToolsJson, wrapWebContent } from "openclaw/plugin-sdk/provider-web-search";
|
import { postTrustedWebToolsJson, wrapWebContent } from "openclaw/plugin-sdk/provider-web-search";
|
||||||
|
|
||||||
export const XAI_WEB_SEARCH_ENDPOINT = "https://api.x.ai/v1/responses";
|
export const XAI_WEB_SEARCH_ENDPOINT = "https://api.x.ai/v1/responses";
|
||||||
@ -79,7 +80,7 @@ export function resolveXaiSearchConfig(searchConfig?: Record<string, unknown>):
|
|||||||
export function resolveXaiWebSearchModel(searchConfig?: Record<string, unknown>): string {
|
export function resolveXaiWebSearchModel(searchConfig?: Record<string, unknown>): string {
|
||||||
const config = resolveXaiSearchConfig(searchConfig);
|
const config = resolveXaiSearchConfig(searchConfig);
|
||||||
return typeof config.model === "string" && config.model.trim()
|
return typeof config.model === "string" && config.model.trim()
|
||||||
? config.model.trim()
|
? normalizeXaiModelId(config.model.trim())
|
||||||
: XAI_DEFAULT_WEB_SEARCH_MODEL;
|
: XAI_DEFAULT_WEB_SEARCH_MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,19 @@ describe("xai web search config resolution", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("normalizes deprecated grok 4.20 beta model ids to GA ids", () => {
|
||||||
|
expect(
|
||||||
|
resolveXaiWebSearchModel({
|
||||||
|
grok: { model: "grok-4.20-experimental-beta-0304-reasoning" },
|
||||||
|
}),
|
||||||
|
).toBe("grok-4.20-reasoning");
|
||||||
|
expect(
|
||||||
|
resolveXaiWebSearchModel({
|
||||||
|
grok: { model: "grok-4.20-experimental-beta-0304-non-reasoning" },
|
||||||
|
}),
|
||||||
|
).toBe("grok-4.20-non-reasoning");
|
||||||
|
});
|
||||||
|
|
||||||
it("defaults inlineCitations to false", () => {
|
it("defaults inlineCitations to false", () => {
|
||||||
expect(resolveXaiInlineCitations({})).toBe(false);
|
expect(resolveXaiInlineCitations({})).toBe(false);
|
||||||
expect(resolveXaiInlineCitations(undefined)).toBe(false);
|
expect(resolveXaiInlineCitations(undefined)).toBe(false);
|
||||||
|
|||||||
18
src/agents/model-id-normalization.test.ts
Normal file
18
src/agents/model-id-normalization.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { normalizeXaiModelId } from "./model-id-normalization.js";
|
||||||
|
|
||||||
|
describe("normalizeXaiModelId", () => {
|
||||||
|
it("maps deprecated grok 4.20 beta ids to GA ids", () => {
|
||||||
|
expect(normalizeXaiModelId("grok-4.20-experimental-beta-0304-reasoning")).toBe(
|
||||||
|
"grok-4.20-reasoning",
|
||||||
|
);
|
||||||
|
expect(normalizeXaiModelId("grok-4.20-experimental-beta-0304-non-reasoning")).toBe(
|
||||||
|
"grok-4.20-non-reasoning",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("leaves current xai model ids unchanged", () => {
|
||||||
|
expect(normalizeXaiModelId("grok-4.20-reasoning")).toBe("grok-4.20-reasoning");
|
||||||
|
expect(normalizeXaiModelId("grok-4")).toBe("grok-4");
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -21,3 +21,13 @@ export function normalizeGoogleModelId(id: string): string {
|
|||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeXaiModelId(id: string): string {
|
||||||
|
if (id === "grok-4.20-experimental-beta-0304-reasoning") {
|
||||||
|
return "grok-4.20-reasoning";
|
||||||
|
}
|
||||||
|
if (id === "grok-4.20-experimental-beta-0304-non-reasoning") {
|
||||||
|
return "grok-4.20-non-reasoning";
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|||||||
@ -194,6 +194,15 @@ describe("model-selection", () => {
|
|||||||
defaultProvider: "google",
|
defaultProvider: "google",
|
||||||
expected: { provider: "google", model: "gemini-3.1-flash-lite-preview" },
|
expected: { provider: "google", model: "gemini-3.1-flash-lite-preview" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "normalizes deprecated xai grok 4.20 beta ids",
|
||||||
|
variants: [
|
||||||
|
"xai/grok-4.20-experimental-beta-0304-reasoning",
|
||||||
|
"grok-4.20-experimental-beta-0304-reasoning",
|
||||||
|
],
|
||||||
|
defaultProvider: "xai",
|
||||||
|
expected: { provider: "xai", model: "grok-4.20-reasoning" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "keeps OpenAI codex refs on the openai provider",
|
name: "keeps OpenAI codex refs on the openai provider",
|
||||||
variants: ["openai/gpt-5.3-codex", "gpt-5.3-codex"],
|
variants: ["openai/gpt-5.3-codex", "gpt-5.3-codex"],
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
} from "./agent-scope.js";
|
} from "./agent-scope.js";
|
||||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
||||||
import type { ModelCatalogEntry } from "./model-catalog.js";
|
import type { ModelCatalogEntry } from "./model-catalog.js";
|
||||||
import { normalizeGoogleModelId } from "./model-id-normalization.js";
|
import { normalizeGoogleModelId, normalizeXaiModelId } from "./model-id-normalization.js";
|
||||||
import { splitTrailingAuthProfile } from "./model-ref-profile.js";
|
import { splitTrailingAuthProfile } from "./model-ref-profile.js";
|
||||||
import {
|
import {
|
||||||
findNormalizedProviderKey,
|
findNormalizedProviderKey,
|
||||||
@ -121,6 +121,9 @@ function normalizeProviderModelId(provider: string, model: string): string {
|
|||||||
if (provider === "google" || provider === "google-vertex") {
|
if (provider === "google" || provider === "google-vertex") {
|
||||||
return normalizeGoogleModelId(model);
|
return normalizeGoogleModelId(model);
|
||||||
}
|
}
|
||||||
|
if (provider === "xai") {
|
||||||
|
return normalizeXaiModelId(model);
|
||||||
|
}
|
||||||
// OpenRouter-native models (e.g. "openrouter/aurora-alpha") need the full
|
// OpenRouter-native models (e.g. "openrouter/aurora-alpha") need the full
|
||||||
// "openrouter/<name>" as the model ID sent to the API. Models from external
|
// "openrouter/<name>" as the model ID sent to the API. Models from external
|
||||||
// providers already contain a slash (e.g. "anthropic/claude-sonnet-4-5") and
|
// providers already contain a slash (e.g. "anthropic/claude-sonnet-4-5") and
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { isRecord } from "../utils.js";
|
|||||||
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
||||||
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
|
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
|
||||||
import { discoverBedrockModels } from "./bedrock-discovery.js";
|
import { discoverBedrockModels } from "./bedrock-discovery.js";
|
||||||
import { normalizeGoogleModelId } from "./model-id-normalization.js";
|
import { normalizeGoogleModelId, normalizeXaiModelId } from "./model-id-normalization.js";
|
||||||
import { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
|
import { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
|
||||||
export { buildKimiCodingProvider } from "../../extensions/kimi-coding/provider-catalog.js";
|
export { buildKimiCodingProvider } from "../../extensions/kimi-coding/provider-catalog.js";
|
||||||
export { buildKilocodeProvider } from "../../extensions/kilocode/provider-catalog.js";
|
export { buildKilocodeProvider } from "../../extensions/kilocode/provider-catalog.js";
|
||||||
@ -42,7 +42,7 @@ import {
|
|||||||
} from "./model-auth-markers.js";
|
} from "./model-auth-markers.js";
|
||||||
import { resolveAwsSdkEnvVarName, resolveEnvApiKey } from "./model-auth.js";
|
import { resolveAwsSdkEnvVarName, resolveEnvApiKey } from "./model-auth.js";
|
||||||
export { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
|
export { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
|
||||||
export { normalizeGoogleModelId };
|
export { normalizeGoogleModelId, normalizeXaiModelId };
|
||||||
|
|
||||||
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
||||||
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
|
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
|
||||||
|
|||||||
@ -341,6 +341,15 @@ describe("web_search grok config resolution", () => {
|
|||||||
expect(resolveGrokModel({ model: "grok-4-fast" })).toBe("grok-4-fast");
|
expect(resolveGrokModel({ model: "grok-4-fast" })).toBe("grok-4-fast");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("normalizes deprecated grok 4.20 beta ids to GA ids", () => {
|
||||||
|
expect(resolveGrokModel({ model: "grok-4.20-experimental-beta-0304-reasoning" })).toBe(
|
||||||
|
"grok-4.20-reasoning",
|
||||||
|
);
|
||||||
|
expect(resolveGrokModel({ model: "grok-4.20-experimental-beta-0304-non-reasoning" })).toBe(
|
||||||
|
"grok-4.20-non-reasoning",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("falls back to default model", () => {
|
it("falls back to default model", () => {
|
||||||
expect(resolveGrokModel({})).toBe("grok-4-1-fast");
|
expect(resolveGrokModel({})).toBe("grok-4-1-fast");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,6 +9,8 @@ vi.mock("../../agents/model-catalog.js", () => ({
|
|||||||
{ provider: "kimi", id: "kimi-code", name: "Kimi Code" },
|
{ provider: "kimi", id: "kimi-code", name: "Kimi Code" },
|
||||||
{ provider: "openai", id: "gpt-4o-mini", name: "GPT-4o mini" },
|
{ provider: "openai", id: "gpt-4o-mini", name: "GPT-4o mini" },
|
||||||
{ provider: "openai", id: "gpt-4o", name: "GPT-4o" },
|
{ provider: "openai", id: "gpt-4o", name: "GPT-4o" },
|
||||||
|
{ provider: "xai", id: "grok-4", name: "Grok 4" },
|
||||||
|
{ provider: "xai", id: "grok-4.20-reasoning", name: "Grok 4.20 (Reasoning)" },
|
||||||
]),
|
]),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -263,6 +265,45 @@ describe("createModelSelectionState respects session model override", () => {
|
|||||||
expect(state.provider).toBe(defaultProvider);
|
expect(state.provider).toBe(defaultProvider);
|
||||||
expect(state.model).toBe("deepseek-v3-4bit-mlx");
|
expect(state.model).toBe("deepseek-v3-4bit-mlx");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("normalizes deprecated xai beta session overrides before allowlist checks", async () => {
|
||||||
|
const cfg = {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: {
|
||||||
|
primary: "xai/grok-4",
|
||||||
|
},
|
||||||
|
models: {
|
||||||
|
"xai/grok-4": {},
|
||||||
|
"xai/grok-4.20-experimental-beta-0304-reasoning": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
const sessionKey = "agent:main:telegram:group:123:topic:99";
|
||||||
|
const sessionEntry = makeEntry({
|
||||||
|
providerOverride: "xai",
|
||||||
|
modelOverride: "grok-4.20-experimental-beta-0304-reasoning",
|
||||||
|
});
|
||||||
|
const sessionStore = { [sessionKey]: sessionEntry };
|
||||||
|
|
||||||
|
const state = await createModelSelectionState({
|
||||||
|
cfg,
|
||||||
|
agentCfg: cfg.agents?.defaults,
|
||||||
|
sessionEntry,
|
||||||
|
sessionStore,
|
||||||
|
sessionKey,
|
||||||
|
defaultProvider: "xai",
|
||||||
|
defaultModel: "grok-4",
|
||||||
|
provider: "xai",
|
||||||
|
model: "grok-4",
|
||||||
|
hasModelDirective: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(state.provider).toBe("xai");
|
||||||
|
expect(state.model).toBe("grok-4.20-reasoning");
|
||||||
|
expect(state.resetModelOverride).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("createModelSelectionState resolveDefaultReasoningLevel", () => {
|
describe("createModelSelectionState resolveDefaultReasoningLevel", () => {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
buildAllowedModelSet,
|
buildAllowedModelSet,
|
||||||
type ModelAliasIndex,
|
type ModelAliasIndex,
|
||||||
modelKey,
|
modelKey,
|
||||||
|
normalizeModelRef,
|
||||||
normalizeProviderId,
|
normalizeProviderId,
|
||||||
resolveModelRefFromString,
|
resolveModelRefFromString,
|
||||||
resolveReasoningDefault,
|
resolveReasoningDefault,
|
||||||
@ -326,7 +327,8 @@ export async function createModelSelectionState(params: {
|
|||||||
const overrideProvider = sessionEntry.providerOverride?.trim() || defaultProvider;
|
const overrideProvider = sessionEntry.providerOverride?.trim() || defaultProvider;
|
||||||
const overrideModel = sessionEntry.modelOverride?.trim();
|
const overrideModel = sessionEntry.modelOverride?.trim();
|
||||||
if (overrideModel) {
|
if (overrideModel) {
|
||||||
const key = modelKey(overrideProvider, overrideModel);
|
const normalizedOverride = normalizeModelRef(overrideProvider, overrideModel);
|
||||||
|
const key = modelKey(normalizedOverride.provider, normalizedOverride.model);
|
||||||
if (allowedModelKeys.size > 0 && !allowedModelKeys.has(key)) {
|
if (allowedModelKeys.size > 0 && !allowedModelKeys.has(key)) {
|
||||||
const { updated } = applyModelOverrideToSessionEntry({
|
const { updated } = applyModelOverrideToSessionEntry({
|
||||||
entry: sessionEntry,
|
entry: sessionEntry,
|
||||||
@ -356,11 +358,14 @@ export async function createModelSelectionState(params: {
|
|||||||
// the regular session/parent model override behavior.
|
// the regular session/parent model override behavior.
|
||||||
const skipStoredOverride = params.hasResolvedHeartbeatModelOverride === true;
|
const skipStoredOverride = params.hasResolvedHeartbeatModelOverride === true;
|
||||||
if (storedOverride?.model && !skipStoredOverride) {
|
if (storedOverride?.model && !skipStoredOverride) {
|
||||||
const candidateProvider = storedOverride.provider || defaultProvider;
|
const normalizedStoredOverride = normalizeModelRef(
|
||||||
const key = modelKey(candidateProvider, storedOverride.model);
|
storedOverride.provider || defaultProvider,
|
||||||
|
storedOverride.model,
|
||||||
|
);
|
||||||
|
const key = modelKey(normalizedStoredOverride.provider, normalizedStoredOverride.model);
|
||||||
if (allowedModelKeys.size === 0 || allowedModelKeys.has(key)) {
|
if (allowedModelKeys.size === 0 || allowedModelKeys.has(key)) {
|
||||||
provider = candidateProvider;
|
provider = normalizedStoredOverride.provider;
|
||||||
model = storedOverride.model;
|
model = normalizedStoredOverride.model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,7 +101,7 @@ describe("model-pricing-cache", () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
mappings: [{ model: "xai/grok-4" }],
|
mappings: [{ model: "xai/grok-4.20-experimental-beta-0304-reasoning" }],
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
subagents: { model: { primary: "zai/glm-5" } },
|
subagents: { model: { primary: "zai/glm-5" } },
|
||||||
@ -130,7 +130,7 @@ describe("model-pricing-cache", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "x-ai/grok-4",
|
id: "x-ai/grok-4.20-experimental-beta-0304-reasoning",
|
||||||
pricing: {
|
pricing: {
|
||||||
prompt: "0.000002",
|
prompt: "0.000002",
|
||||||
completion: "0.00001",
|
completion: "0.00001",
|
||||||
@ -172,12 +172,25 @@ describe("model-pricing-cache", () => {
|
|||||||
cacheRead: 0.3,
|
cacheRead: 0.3,
|
||||||
cacheWrite: 0,
|
cacheWrite: 0,
|
||||||
});
|
});
|
||||||
expect(getCachedGatewayModelPricing({ provider: "xai", model: "grok-4" })).toEqual({
|
expect(
|
||||||
|
getCachedGatewayModelPricing({
|
||||||
|
provider: "xai",
|
||||||
|
model: "grok-4.20-experimental-beta-0304-reasoning",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
input: 2,
|
input: 2,
|
||||||
output: 10,
|
output: 10,
|
||||||
cacheRead: 0,
|
cacheRead: 0,
|
||||||
cacheWrite: 0,
|
cacheWrite: 0,
|
||||||
});
|
});
|
||||||
|
expect(getCachedGatewayModelPricing({ provider: "xai", model: "grok-4.20-reasoning" })).toEqual(
|
||||||
|
{
|
||||||
|
input: 2,
|
||||||
|
output: 10,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
expect(getCachedGatewayModelPricing({ provider: "zai", model: "glm-5" })).toEqual({
|
expect(getCachedGatewayModelPricing({ provider: "zai", model: "glm-5" })).toEqual({
|
||||||
input: 1,
|
input: 1,
|
||||||
output: 4,
|
output: 4,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
resolveModelRefFromString,
|
resolveModelRefFromString,
|
||||||
type ModelRef,
|
type ModelRef,
|
||||||
} from "../agents/model-selection.js";
|
} from "../agents/model-selection.js";
|
||||||
import { normalizeGoogleModelId } from "../agents/models-config.providers.js";
|
import { normalizeGoogleModelId, normalizeXaiModelId } from "../agents/models-config.providers.js";
|
||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||||
|
|
||||||
@ -155,6 +155,9 @@ function canonicalizeOpenRouterLookupId(id: string): string {
|
|||||||
if (provider === "google") {
|
if (provider === "google") {
|
||||||
model = normalizeGoogleModelId(model);
|
model = normalizeGoogleModelId(model);
|
||||||
}
|
}
|
||||||
|
if (provider === "x-ai") {
|
||||||
|
model = normalizeXaiModelId(model);
|
||||||
|
}
|
||||||
return `${provider}/${model}`;
|
return `${provider}/${model}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export {
|
|||||||
XAI_TOOL_SCHEMA_PROFILE,
|
XAI_TOOL_SCHEMA_PROFILE,
|
||||||
} from "../agents/model-compat.js";
|
} from "../agents/model-compat.js";
|
||||||
export { normalizeProviderId } from "../agents/provider-id.js";
|
export { normalizeProviderId } from "../agents/provider-id.js";
|
||||||
|
export { normalizeXaiModelId } from "../agents/model-id-normalization.js";
|
||||||
export { cloneFirstTemplateModel } from "../plugins/provider-model-helpers.js";
|
export { cloneFirstTemplateModel } from "../plugins/provider-model-helpers.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user