chore(deadcode): inline message provider tool filtering

This commit is contained in:
Vincent Koc
2026-06-21 03:44:19 +08:00
parent 4461e257e3
commit bdcc691745
2 changed files with 39 additions and 43 deletions

View File

@@ -4,21 +4,42 @@
* unsafe or redundant for the active channel.
*/
import { describe, expect, it } from "vitest";
import { filterToolNamesByMessageProvider } from "./agent-tools.message-provider-policy.js";
import { filterToolsByMessageProvider } from "./agent-tools.message-provider-policy.js";
const DEFAULT_TOOL_NAMES = ["read", "write", "tts", "web_search"];
const DEFAULT_TOOLS = [
{ name: "read" },
{ name: "write" },
{ name: "tts" },
{ name: "web_search" },
];
function toolNames(tools: readonly { name: string }[]): Set<string> {
return new Set(tools.map((tool) => tool.name));
}
describe("createOpenClawCodingTools message provider policy", () => {
it.each(["voice", "VOICE", " Voice ", "discord-voice", "DISCORD-VOICE", " Discord-Voice "])(
"does not expose tts tool for normalized voice provider: %s",
(messageProvider) => {
const names = new Set(filterToolNamesByMessageProvider(DEFAULT_TOOL_NAMES, messageProvider));
const names = toolNames(filterToolsByMessageProvider(DEFAULT_TOOLS, messageProvider));
expect(names.has("tts")).toBe(false);
},
);
it("keeps tts tool for non-voice providers", () => {
const names = new Set(filterToolNamesByMessageProvider(DEFAULT_TOOL_NAMES, "guildchat"));
const names = toolNames(filterToolsByMessageProvider(DEFAULT_TOOLS, "guildchat"));
expect(names.has("tts")).toBe(true);
});
it("preserves duplicate tool entries while filtering", () => {
const tools = [
{ name: "read", id: 1 },
{ name: "tts", id: 2 },
{ name: "read", id: 3 },
];
expect(filterToolsByMessageProvider(tools, "voice")).toStrictEqual([
{ name: "read", id: 1 },
{ name: "read", id: 3 },
]);
});
});

View File

@@ -14,49 +14,24 @@ const TOOL_ALLOW_BY_MESSAGE_PROVIDER: Readonly<Record<string, readonly string[]>
node: ["canvas", "image", "pdf", "tts", "web_fetch", "web_search"],
};
/** Filters tool names by the active message-provider allow/deny policy. */
export function filterToolNamesByMessageProvider(
toolNames: readonly string[],
messageProvider?: string,
): string[] {
const normalizedProvider = normalizeOptionalLowercaseString(messageProvider);
if (!normalizedProvider) {
return [...toolNames];
}
const allowedTools = TOOL_ALLOW_BY_MESSAGE_PROVIDER[normalizedProvider];
if (allowedTools && allowedTools.length > 0) {
const allowedSet = new Set(allowedTools);
return toolNames.filter((toolName) => allowedSet.has(toolName));
}
const deniedTools = TOOL_DENY_BY_MESSAGE_PROVIDER[normalizedProvider];
if (!deniedTools || deniedTools.length === 0) {
return [...toolNames];
}
const deniedSet = new Set(deniedTools);
return toolNames.filter((toolName) => !deniedSet.has(toolName));
}
/** Applies message-provider filtering while preserving duplicate tool entries. */
export function filterToolsByMessageProvider<TTool extends { name: string }>(
tools: readonly TTool[],
messageProvider?: string,
): TTool[] {
const filteredToolNames = filterToolNamesByMessageProvider(
tools.map((tool) => tool.name),
messageProvider,
);
const remainingCounts = new Map<string, number>();
for (const toolName of filteredToolNames) {
remainingCounts.set(toolName, (remainingCounts.get(toolName) ?? 0) + 1);
const normalizedProvider = normalizeOptionalLowercaseString(messageProvider);
if (!normalizedProvider) {
return [...tools];
}
return tools.filter((tool) => {
// Counted matching preserves the original order and duplicate instances
// after name-level policy filtering.
const remaining = remainingCounts.get(tool.name) ?? 0;
if (remaining <= 0) {
return false;
}
remainingCounts.set(tool.name, remaining - 1);
return true;
});
const allowedTools = TOOL_ALLOW_BY_MESSAGE_PROVIDER[normalizedProvider];
if (allowedTools && allowedTools.length > 0) {
const allowedSet = new Set(allowedTools);
return tools.filter((tool) => allowedSet.has(tool.name));
}
const deniedTools = TOOL_DENY_BY_MESSAGE_PROVIDER[normalizedProvider];
if (!deniedTools || deniedTools.length === 0) {
return [...tools];
}
const deniedSet = new Set(deniedTools);
return tools.filter((tool) => !deniedSet.has(tool.name));
}