mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 20:40:20 +00:00
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
import { Type } from "@sinclair/typebox";
|
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
|
|
import {
|
|
jsonResult,
|
|
readNumberParam,
|
|
readStringParam,
|
|
} from "openclaw/plugin-sdk/provider-web-search";
|
|
import { runTavilySearch } from "./tavily-client.js";
|
|
|
|
function optionalStringEnum<const T extends readonly string[]>(
|
|
values: T,
|
|
options: { description?: string } = {},
|
|
) {
|
|
return Type.Optional(
|
|
Type.Unsafe<T[number]>({
|
|
type: "string",
|
|
enum: [...values],
|
|
...options,
|
|
}),
|
|
);
|
|
}
|
|
|
|
const TavilySearchToolSchema = Type.Object(
|
|
{
|
|
query: Type.String({ description: "Search query string." }),
|
|
search_depth: optionalStringEnum(["basic", "advanced"] as const, {
|
|
description: 'Search depth: "basic" (default, faster) or "advanced" (more thorough).',
|
|
}),
|
|
topic: optionalStringEnum(["general", "news", "finance"] as const, {
|
|
description: 'Search topic: "general" (default), "news", or "finance".',
|
|
}),
|
|
max_results: Type.Optional(
|
|
Type.Number({
|
|
description: "Number of results to return (1-20).",
|
|
minimum: 1,
|
|
maximum: 20,
|
|
}),
|
|
),
|
|
include_answer: Type.Optional(
|
|
Type.Boolean({
|
|
description: "Include an AI-generated answer summary (default: false).",
|
|
}),
|
|
),
|
|
time_range: optionalStringEnum(["day", "week", "month", "year"] as const, {
|
|
description: "Filter results by recency: 'day', 'week', 'month', or 'year'.",
|
|
}),
|
|
include_domains: Type.Optional(
|
|
Type.Array(Type.String(), {
|
|
description: "Only include results from these domains.",
|
|
}),
|
|
),
|
|
exclude_domains: Type.Optional(
|
|
Type.Array(Type.String(), {
|
|
description: "Exclude results from these domains.",
|
|
}),
|
|
),
|
|
},
|
|
{ additionalProperties: false },
|
|
);
|
|
|
|
export function createTavilySearchTool(api: OpenClawPluginApi) {
|
|
return {
|
|
name: "tavily_search",
|
|
label: "Tavily Search",
|
|
description:
|
|
"Search the web using Tavily Search API. Supports search depth, topic filtering, domain filters, time ranges, and AI answer summaries.",
|
|
parameters: TavilySearchToolSchema,
|
|
execute: async (_toolCallId: string, rawParams: Record<string, unknown>) => {
|
|
const query = readStringParam(rawParams, "query", { required: true });
|
|
const searchDepth = readStringParam(rawParams, "search_depth") || undefined;
|
|
const topic = readStringParam(rawParams, "topic") || undefined;
|
|
const maxResults = readNumberParam(rawParams, "max_results", { integer: true });
|
|
const includeAnswer = rawParams.include_answer === true;
|
|
const timeRange = readStringParam(rawParams, "time_range") || undefined;
|
|
const includeDomains = Array.isArray(rawParams.include_domains)
|
|
? (rawParams.include_domains as string[]).filter(Boolean)
|
|
: undefined;
|
|
const excludeDomains = Array.isArray(rawParams.exclude_domains)
|
|
? (rawParams.exclude_domains as string[]).filter(Boolean)
|
|
: undefined;
|
|
|
|
return jsonResult(
|
|
await runTavilySearch({
|
|
cfg: api.config,
|
|
query,
|
|
searchDepth,
|
|
topic,
|
|
maxResults,
|
|
includeAnswer,
|
|
timeRange,
|
|
includeDomains: includeDomains?.length ? includeDomains : undefined,
|
|
excludeDomains: excludeDomains?.length ? excludeDomains : undefined,
|
|
}),
|
|
);
|
|
},
|
|
};
|
|
}
|