mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-07 10:34:44 +00:00
fix(tavily): keep web search contract executable
Keep the Tavily public artifact lightweight while lazily executing through the provider runtime. Verified with focused Tavily/provider artifact tests and clean CI.
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
// Tavily provider module implements model/runtime integration.
|
||||
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
||||
import type { WebSearchProviderPlugin } from "openclaw/plugin-sdk/provider-web-search-contract";
|
||||
import { buildTavilyWebSearchProviderBase } from "../web-search-shared.js";
|
||||
import {
|
||||
buildTavilyWebSearchProviderBase,
|
||||
TAVILY_GENERIC_SEARCH_DESCRIPTION,
|
||||
TAVILY_GENERIC_SEARCH_SCHEMA,
|
||||
} from "../web-search-shared.js";
|
||||
|
||||
type TavilyClientModule = typeof import("./tavily-client.js");
|
||||
|
||||
@@ -12,27 +16,12 @@ function loadTavilyClientModule(): Promise<TavilyClientModule> {
|
||||
return tavilyClientModulePromise;
|
||||
}
|
||||
|
||||
const GenericTavilySearchSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "Search query string." },
|
||||
count: {
|
||||
type: "integer",
|
||||
description: "Number of results to return (1-20).",
|
||||
minimum: 1,
|
||||
maximum: 20,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
} satisfies Record<string, unknown>;
|
||||
|
||||
export function createTavilyWebSearchProvider(): WebSearchProviderPlugin {
|
||||
return {
|
||||
...buildTavilyWebSearchProviderBase(),
|
||||
createTool: (ctx) => ({
|
||||
description:
|
||||
"Search the web using Tavily. Returns structured results with snippets. Use tavily_search for Tavily-specific options like search depth, topic filtering, or AI answers.",
|
||||
parameters: GenericTavilySearchSchema,
|
||||
description: TAVILY_GENERIC_SEARCH_DESCRIPTION,
|
||||
parameters: TAVILY_GENERIC_SEARCH_SCHEMA,
|
||||
execute: async (args) => {
|
||||
const { runTavilySearch } = await loadTavilyClientModule();
|
||||
return await runTavilySearch({
|
||||
|
||||
@@ -47,6 +47,7 @@ function fakeApi(): OpenClawPluginApi {
|
||||
|
||||
describe("tavily tools", () => {
|
||||
let createTavilyWebSearchProvider: typeof import("./tavily-search-provider.js").createTavilyWebSearchProvider;
|
||||
let createTavilyContractWebSearchProvider: typeof import("../web-search-contract-api.js").createTavilyWebSearchProvider;
|
||||
let createTavilySearchTool: typeof import("./tavily-search-tool.js").createTavilySearchTool;
|
||||
let createTavilyExtractTool: typeof import("./tavily-extract-tool.js").createTavilyExtractTool;
|
||||
let tavilyClientTesting: typeof import("./tavily-client.js").testing;
|
||||
@@ -54,6 +55,8 @@ describe("tavily tools", () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
({ createTavilyWebSearchProvider } = await import("./tavily-search-provider.js"));
|
||||
({ createTavilyWebSearchProvider: createTavilyContractWebSearchProvider } =
|
||||
await import("../web-search-contract-api.js"));
|
||||
({ createTavilySearchTool } = await import("./tavily-search-tool.js"));
|
||||
({ createTavilyExtractTool } = await import("./tavily-extract-tool.js"));
|
||||
({ testing: tavilyClientTesting } =
|
||||
@@ -110,6 +113,32 @@ describe("tavily tools", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the contract web search provider executable", async () => {
|
||||
const provider = createTavilyContractWebSearchProvider();
|
||||
const tool = provider.createTool({
|
||||
config: { test: "contract" },
|
||||
} as never);
|
||||
if (!tool) {
|
||||
throw new Error("Expected contract provider tool definition");
|
||||
}
|
||||
|
||||
const result = await tool.execute({
|
||||
query: "runtime registration",
|
||||
count: 3,
|
||||
});
|
||||
|
||||
expect(runTavilySearch).toHaveBeenCalledWith({
|
||||
cfg: { test: "contract" },
|
||||
query: "runtime registration",
|
||||
maxResults: 3,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
cfg: { test: "contract" },
|
||||
query: "runtime registration",
|
||||
maxResults: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes generic Tavily search count before dispatch", async () => {
|
||||
const provider = createTavilyWebSearchProvider();
|
||||
const tool = provider.createTool({
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
// Tavily API module exposes the plugin public contract.
|
||||
import type { WebSearchProviderPlugin } from "openclaw/plugin-sdk/provider-web-search-contract";
|
||||
import { buildTavilyWebSearchProviderBase } from "./web-search-shared.js";
|
||||
import type { WebSearchProviderPlugin } from "openclaw/plugin-sdk/provider-web-search-config-contract";
|
||||
import {
|
||||
buildTavilyWebSearchProviderBase,
|
||||
TAVILY_GENERIC_SEARCH_DESCRIPTION,
|
||||
TAVILY_GENERIC_SEARCH_SCHEMA,
|
||||
} from "./web-search-shared.js";
|
||||
|
||||
type TavilySearchProviderModule = typeof import("./src/tavily-search-provider.js");
|
||||
|
||||
let tavilySearchProviderModulePromise: Promise<TavilySearchProviderModule> | undefined;
|
||||
|
||||
function loadTavilySearchProviderModule(): Promise<TavilySearchProviderModule> {
|
||||
tavilySearchProviderModulePromise ??= import("./src/tavily-search-provider.js");
|
||||
return tavilySearchProviderModulePromise;
|
||||
}
|
||||
|
||||
export function createTavilyWebSearchProvider(): WebSearchProviderPlugin {
|
||||
return {
|
||||
...buildTavilyWebSearchProviderBase(),
|
||||
createTool: () => null,
|
||||
createTool: (ctx) => ({
|
||||
description: TAVILY_GENERIC_SEARCH_DESCRIPTION,
|
||||
parameters: TAVILY_GENERIC_SEARCH_SCHEMA,
|
||||
execute: async (args) => {
|
||||
const { createTavilyWebSearchProvider: createRuntimeProvider } =
|
||||
await loadTavilySearchProviderModule();
|
||||
const tool = createRuntimeProvider().createTool(ctx);
|
||||
if (!tool) {
|
||||
throw new Error("Tavily web_search provider did not create a runtime tool.");
|
||||
}
|
||||
return await tool.execute(args);
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,23 @@ import {
|
||||
|
||||
export const TAVILY_CREDENTIAL_PATH = "plugins.entries.tavily.config.webSearch.apiKey";
|
||||
|
||||
export const TAVILY_GENERIC_SEARCH_DESCRIPTION =
|
||||
"Search the web using Tavily. Returns structured results with snippets. Use tavily_search for Tavily-specific options like search depth, topic filtering, or AI answers.";
|
||||
|
||||
export const TAVILY_GENERIC_SEARCH_SCHEMA = {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "Search query string." },
|
||||
count: {
|
||||
type: "integer",
|
||||
description: "Number of results to return (1-20).",
|
||||
minimum: 1,
|
||||
maximum: 20,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
} satisfies Record<string, unknown>;
|
||||
|
||||
export function buildTavilyWebSearchProviderBase(): Omit<WebSearchProviderPlugin, "createTool"> {
|
||||
return {
|
||||
id: "tavily",
|
||||
|
||||
Reference in New Issue
Block a user