feat: finish xai provider integration

This commit is contained in:
Peter Steinberger
2026-03-17 21:26:02 -07:00
parent 2b5fa0931d
commit a8907d80dd
50 changed files with 1900 additions and 610 deletions

View File

@@ -7,6 +7,7 @@ import { resolveBundledPluginsDir } from "./bundled-dir.js";
const tempDirs: string[] = [];
const originalCwd = process.cwd();
const originalBundledDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
const originalVitest = process.env.VITEST;
function makeRepoRoot(prefix: string): string {
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
@@ -21,6 +22,11 @@ afterEach(() => {
} else {
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledDir;
}
if (originalVitest === undefined) {
delete process.env.VITEST;
} else {
process.env.VITEST = originalVitest;
}
for (const dir of tempDirs.splice(0, tempDirs.length)) {
fs.rmSync(dir, { recursive: true, force: true });
}
@@ -43,4 +49,23 @@ describe("resolveBundledPluginsDir", () => {
fs.realpathSync(path.join(repoRoot, "dist-runtime", "extensions")),
);
});
it("prefers source extensions under vitest to avoid stale staged plugins", () => {
const repoRoot = makeRepoRoot("openclaw-bundled-dir-vitest-");
fs.mkdirSync(path.join(repoRoot, "extensions"), { recursive: true });
fs.mkdirSync(path.join(repoRoot, "dist-runtime", "extensions"), { recursive: true });
fs.mkdirSync(path.join(repoRoot, "dist", "extensions"), { recursive: true });
fs.writeFileSync(
path.join(repoRoot, "package.json"),
`${JSON.stringify({ name: "openclaw" }, null, 2)}\n`,
"utf8",
);
process.chdir(repoRoot);
process.env.VITEST = "true";
expect(fs.realpathSync(resolveBundledPluginsDir() ?? "")).toBe(
fs.realpathSync(path.join(repoRoot, "extensions")),
);
});
});

View File

@@ -10,6 +10,8 @@ export function resolveBundledPluginsDir(env: NodeJS.ProcessEnv = process.env):
return resolveUserPath(override, env);
}
const preferSourceCheckout = Boolean(env.VITEST);
try {
const packageRoots = [
resolveOpenClawPackageRootSync({ cwd: process.cwd() }),
@@ -18,6 +20,10 @@ export function resolveBundledPluginsDir(env: NodeJS.ProcessEnv = process.env):
(entry, index, all): entry is string => Boolean(entry) && all.indexOf(entry) === index,
);
for (const packageRoot of packageRoots) {
const sourceExtensionsDir = path.join(packageRoot, "extensions");
if (preferSourceCheckout && fs.existsSync(sourceExtensionsDir)) {
return sourceExtensionsDir;
}
// Local source checkouts stage a runtime-complete bundled plugin tree under
// dist-runtime/. Prefer that over source extensions only when the paired
// dist/ tree exists; otherwise wrappers can drift ahead of the last build.

View File

@@ -429,6 +429,120 @@ describe("provider runtime contract", () => {
});
});
describe("xai", () => {
it("owns Grok forward-compat resolution for newer fast models", () => {
const provider = requireProviderContractProvider("xai");
const model = provider.resolveDynamicModel?.({
provider: "xai",
modelId: "grok-4-1-fast-reasoning",
modelRegistry: {
find: () => null,
} as never,
providerConfig: {
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
},
});
expect(model).toMatchObject({
id: "grok-4-1-fast-reasoning",
provider: "xai",
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
reasoning: true,
contextWindow: 2_000_000,
});
});
it("owns xai modern-model matching without accepting multi-agent ids", () => {
const provider = requireProviderContractProvider("xai");
expect(
provider.isModernModelRef?.({
provider: "xai",
modelId: "grok-4-1-fast-reasoning",
} as never),
).toBe(true);
expect(
provider.isModernModelRef?.({
provider: "xai",
modelId: "grok-4.20-multi-agent-experimental-beta-0304",
} as never),
).toBe(false);
});
it("owns direct xai compat flags on resolved models", () => {
const provider = requireProviderContractProvider("xai");
expect(
provider.normalizeResolvedModel?.({
provider: "xai",
modelId: "grok-4-1-fast",
model: createModel({
id: "grok-4-1-fast",
provider: "xai",
api: "openai-completions",
baseUrl: "https://api.x.ai/v1",
}),
} as never),
).toMatchObject({
compat: {
toolSchemaProfile: "xai",
nativeWebSearchTool: true,
toolCallArgumentsEncoding: "html-entities",
},
});
});
});
describe("openrouter", () => {
it("owns xai downstream compat flags for x-ai routed models", () => {
const provider = requireProviderContractProvider("openrouter");
expect(
provider.normalizeResolvedModel?.({
provider: "openrouter",
modelId: "x-ai/grok-4-1-fast",
model: createModel({
id: "x-ai/grok-4-1-fast",
provider: "openrouter",
api: "openai-completions",
baseUrl: "https://openrouter.ai/api/v1",
}),
}),
).toMatchObject({
compat: {
toolSchemaProfile: "xai",
nativeWebSearchTool: true,
toolCallArgumentsEncoding: "html-entities",
},
});
});
});
describe("venice", () => {
it("owns xai downstream compat flags for grok-backed Venice models", () => {
const provider = requireProviderContractProvider("venice");
expect(
provider.normalizeResolvedModel?.({
provider: "venice",
modelId: "grok-41-fast",
model: createModel({
id: "grok-41-fast",
provider: "venice",
api: "openai-completions",
baseUrl: "https://api.venice.ai/api/v1",
}),
}),
).toMatchObject({
compat: {
toolSchemaProfile: "xai",
nativeWebSearchTool: true,
toolCallArgumentsEncoding: "html-entities",
},
});
});
});
describe("openai-codex", () => {
it("owns refresh fallback for accountId extraction failures", async () => {
const provider = requireProviderContractProvider("openai-codex");