mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 00:53:28 +00:00
fix(agents): avoid xAI web_search tool-name collisions
This commit is contained in:
42
src/agents/pi-tools.model-provider-collision.test.ts
Normal file
42
src/agents/pi-tools.model-provider-collision.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { __testing } from "./pi-tools.js";
|
||||
import type { AnyAgentTool } from "./pi-tools.types.js";
|
||||
|
||||
const baseTools = [
|
||||
{ name: "read" },
|
||||
{ name: "web_search" },
|
||||
{ name: "exec" },
|
||||
] as unknown as AnyAgentTool[];
|
||||
|
||||
function toolNames(tools: AnyAgentTool[]): string[] {
|
||||
return tools.map((tool) => tool.name);
|
||||
}
|
||||
|
||||
describe("applyModelProviderToolPolicy", () => {
|
||||
it("keeps web_search for non-xAI models", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools, {
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-4o-mini",
|
||||
});
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "web_search", "exec"]);
|
||||
});
|
||||
|
||||
it("removes web_search for OpenRouter xAI model ids", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools, {
|
||||
modelProvider: "openrouter",
|
||||
modelId: "x-ai/grok-4.1-fast",
|
||||
});
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "exec"]);
|
||||
});
|
||||
|
||||
it("removes web_search for direct xAI providers", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools, {
|
||||
modelProvider: "x-ai",
|
||||
modelId: "grok-4.1",
|
||||
});
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "exec"]);
|
||||
});
|
||||
});
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
import { cleanToolSchemaForGemini, normalizeToolParameters } from "./pi-tools.schema.js";
|
||||
import type { AnyAgentTool } from "./pi-tools.types.js";
|
||||
import type { SandboxContext } from "./sandbox.js";
|
||||
import { isXaiProvider } from "./schema/clean-for-xai.js";
|
||||
import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
|
||||
import { createToolFsPolicy, resolveToolFsConfig } from "./tool-fs-policy.js";
|
||||
import {
|
||||
@@ -65,6 +66,7 @@ function isOpenAIProvider(provider?: string) {
|
||||
const TOOL_DENY_BY_MESSAGE_PROVIDER: Readonly<Record<string, readonly string[]>> = {
|
||||
voice: ["tts"],
|
||||
};
|
||||
const TOOL_DENY_FOR_XAI_PROVIDERS = new Set(["web_search"]);
|
||||
|
||||
function normalizeMessageProvider(messageProvider?: string): string | undefined {
|
||||
const normalized = messageProvider?.trim().toLowerCase();
|
||||
@@ -87,6 +89,18 @@ function applyMessageProviderToolPolicy(
|
||||
return tools.filter((tool) => !deniedSet.has(tool.name));
|
||||
}
|
||||
|
||||
function applyModelProviderToolPolicy(
|
||||
tools: AnyAgentTool[],
|
||||
params?: { modelProvider?: string; modelId?: string },
|
||||
): AnyAgentTool[] {
|
||||
if (!isXaiProvider(params?.modelProvider, params?.modelId)) {
|
||||
return tools;
|
||||
}
|
||||
// xAI/Grok providers expose a native web_search tool; sending OpenClaw's
|
||||
// web_search alongside it causes duplicate-name request failures.
|
||||
return tools.filter((tool) => !TOOL_DENY_FOR_XAI_PROVIDERS.has(tool.name));
|
||||
}
|
||||
|
||||
function isApplyPatchAllowedForModel(params: {
|
||||
modelProvider?: string;
|
||||
modelId?: string;
|
||||
@@ -177,6 +191,7 @@ export const __testing = {
|
||||
patchToolSchemaForClaudeCompatibility,
|
||||
wrapToolParamNormalization,
|
||||
assertRequiredParams,
|
||||
applyModelProviderToolPolicy,
|
||||
} as const;
|
||||
|
||||
export function createOpenClawCodingTools(options?: {
|
||||
@@ -501,9 +516,13 @@ export function createOpenClawCodingTools(options?: {
|
||||
}),
|
||||
];
|
||||
const toolsForMessageProvider = applyMessageProviderToolPolicy(tools, options?.messageProvider);
|
||||
const toolsForModelProvider = applyModelProviderToolPolicy(toolsForMessageProvider, {
|
||||
modelProvider: options?.modelProvider,
|
||||
modelId: options?.modelId,
|
||||
});
|
||||
// Security: treat unknown/undefined as unauthorized (opt-in, not opt-out)
|
||||
const senderIsOwner = options?.senderIsOwner === true;
|
||||
const toolsByAuthorization = applyOwnerOnlyToolPolicy(toolsForMessageProvider, senderIsOwner);
|
||||
const toolsByAuthorization = applyOwnerOnlyToolPolicy(toolsForModelProvider, senderIsOwner);
|
||||
const subagentFiltered = applyToolPolicyPipeline({
|
||||
tools: toolsByAuthorization,
|
||||
toolMeta: (tool) => getPluginToolMeta(tool),
|
||||
|
||||
Reference in New Issue
Block a user