search method
Runs one search. Throws WebSearchException on failure so the chain advances to the next provider.
Implementation
@override
Future<WebSearchResponse> search(WebSearchRequest request) async {
final apiKey = request.secrets[apiKeyName];
if (apiKey == null || apiKey.isEmpty) {
throw WebSearchException(id, 'TAVILY_API_KEY not found in secrets');
}
final response = await _send(
this,
request,
() => request.client.post(
Uri.parse(tavilySearchUrl),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $apiKey',
},
body: jsonEncode(buildRequestBody(request)),
),
);
if (response.statusCode < 200 || response.statusCode >= 300) {
throw WebSearchException(
id,
'Tavily API error (${response.statusCode}): '
'${_truncateErrorBody(response.body)}',
response.statusCode,
);
}
final Object? data = jsonDecode(response.body);
if (data is! Map) {
throw WebSearchException(id, 'Tavily returned malformed JSON');
}
final answer = data['answer'] as String?;
final sources = _mapJsonResults(
data['results'],
request.count,
(item) => (item['content'] as String?)?.trim(),
);
return WebSearchResponse(
provider: id,
answer: answer?.trim().isNotEmpty == true ? answer!.trim() : null,
sources: sources,
);
}