webSearchTool function

AgentTool webSearchTool({
  1. required WebSearchConfig config,
})

Creates the web_search tool.

Parameters:

  • query (string, required): the search query.
  • count (integer, optional): max results (default WebSearchConfig.maxResults, clamped to 1..20).
  • site (string, optional): restrict results to one domain (appended as a site: filter, which all ported providers support).

Implementation

AgentTool webSearchTool({required WebSearchConfig config}) {
  return AgentTool(
    name: 'web_search',
    label: 'web_search',
    tier: ApprovalTier.read,
    description:
        'Search the web and return ranked results with [n] citation markers, '
        'titles, URLs, and snippets. Runs a provider chain (DuckDuckGo first, '
        'keyed providers when configured) and falls through on failure. '
        'Follow up with web_fetch on a result URL to read the full page.',
    parameters: const {
      'type': 'object',
      'properties': {
        'query': {'type': 'string', 'description': 'The search query'},
        'count': {
          'type': 'integer',
          'description':
              'Maximum number of results to return (default: 10, max: 20)',
        },
        'site': {
          'type': 'string',
          'description':
              "Restrict results to a single domain, e.g. 'pub.dev' "
              '(appended as a site: filter)',
        },
      },
      'required': ['query'],
    },
    execute: (arguments, cancelToken, onUpdate) async {
      cancelToken?.throwIfCancelled();
      final query = (arguments['query'] as String).trim();
      if (query.isEmpty) throw StateError('query must not be empty');
      final count = clampWebSearchCount(
        (arguments['count'] as num?)?.toInt() ?? config.maxResults,
      );
      final site = (arguments['site'] as String?)?.trim();
      final effectiveQuery = site != null && site.isNotEmpty
          ? '$query site:$site'
          : query;

      final secrets = await config.secrets?.readAll() ?? const {};
      final chain = resolveWebSearchChain(config.providers, secrets: secrets);
      if (chain.isEmpty) {
        throw StateError(
          'No web search provider configured. DuckDuckGo needs no key; set '
          'BRAVE_API_KEY or TAVILY_API_KEY for the keyed providers.',
        );
      }

      final client = config.httpClient ?? http.Client();
      try {
        final request = WebSearchRequest(
          query: effectiveQuery,
          count: count,
          client: client,
          timeout: config.timeout,
          secrets: secrets,
        );
        return await _executeChain(chain, request, query, cancelToken);
      } finally {
        if (config.httpClient == null) client.close();
      }
    },
  );
}