run method

  1. @override
Future<ToolResult> run(
  1. Map<String, dynamic> input
)
override

Executes the tool with the given input map.

Should never throw — return ToolResult.failure on errors.

Implementation

@override
Future<ToolResult> run(Map<String, dynamic> input) async {
  final query = (input['query'] as String? ?? '').trim();
  if (query.isEmpty) {
    return ToolResult.failure(name, 'Input "query" must not be empty.');
  }

  _logger.info('Searching: "$query"');

  // Return mock results if provided (testing / offline)
  if (_mockResults != null) {
    final results = _mockResults!
        .map((r) => '• ${r["title"] ?? ""}: ${r["snippet"] ?? ""}')
        .join('\n');
    return ToolResult.success(name, results,
        metadata: {'query': query, 'source': 'mock'});
  }

  // Real DuckDuckGo Instant Answers (JSON API, no key needed)
  try {
    final uri = Uri.parse(searchEngineUrl).replace(queryParameters: {
      'q': query,
      'format': 'json',
      'no_html': '1',
      'skip_disambig': '1',
    });

    final response = await _client
        .get(uri, headers: {'Accept': 'application/json'})
        .timeout(const Duration(seconds: 10));

    if (response.statusCode != 200) {
      return ToolResult.failure(
        name,
        'Search API returned HTTP ${response.statusCode}',
      );
    }

    final json = jsonDecode(response.body) as Map<String, dynamic>;
    final abstract_ = (json['AbstractText'] as String? ?? '').trim();
    final relatedTopics = (json['RelatedTopics'] as List<dynamic>? ?? [])
        .whereType<Map<String, dynamic>>()
        .take(3)
        .map((t) => (t['Text'] as String? ?? '').trim())
        .where((t) => t.isNotEmpty)
        .join('\n');

    final output = [
      if (abstract_.isNotEmpty) 'Summary: $abstract_',
      if (relatedTopics.isNotEmpty) 'Related:\n$relatedTopics',
    ].join('\n\n');

    return ToolResult.success(
      name,
      output.isEmpty ? 'No results found for "$query".' : output,
      metadata: {'query': query, 'source': 'duckduckgo'},
    );
  } catch (e) {
    _logger.warning('Search failed: $e');
    return ToolResult.failure(name, 'Search error: $e');
  }
}