// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Http;
namespace Microsoft.SemanticKernel.Plugins.Web.Brave;
///
/// A Brave Text Search implementation that can be used to perform searches using the Brave Web Search API.
///
#pragma warning disable CS0618 // ITextSearch is obsolete
public sealed class BraveTextSearch : ITextSearch, ITextSearch
#pragma warning restore CS0618
{
///
/// Create an instance of the with API key authentication.
///
/// The API key credential used to authenticate requests against the Search service.
/// Options used when creating this instance of .
public BraveTextSearch(string apiKey, BraveTextSearchOptions? options = null)
{
Verify.NotNullOrWhiteSpace(apiKey);
this._apiKey = apiKey;
this._uri = options?.Endpoint ?? new Uri(DefaultUri);
this._logger = options?.LoggerFactory?.CreateLogger(typeof(BraveTextSearch)) ?? NullLogger.Instance;
this._httpClient = options?.HttpClient ?? HttpClientProvider.GetHttpClient();
this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpHeaderConstant.Values.UserAgent);
this._httpClient.DefaultRequestHeaders.Add(HttpHeaderConstant.Names.SemanticKernelVersion, HttpHeaderConstant.Values.GetAssemblyVersion(typeof(BraveTextSearch)));
this._stringMapper = options?.StringMapper ?? s_defaultStringMapper;
this._resultMapper = options?.ResultMapper ?? s_defaultResultMapper;
}
#pragma warning disable CS0618 // Obsolete ITextSearch, TextSearchOptions, TextSearchFilter, FilterClause
#region Legacy ITextSearch Implementation
///
public async Task> SearchAsync(string query, TextSearchOptions? searchOptions = null, CancellationToken cancellationToken = new CancellationToken())
{
searchOptions ??= new TextSearchOptions();
var filters = ExtractFiltersFromLegacy(searchOptions.Filter);
BraveSearchResponse? searchResponse = await this.ExecuteSearchAsync(query, searchOptions.Top, searchOptions.Skip, filters, cancellationToken).ConfigureAwait(false);
long? totalCount = searchOptions.IncludeTotalCount ? searchResponse?.Web?.Results.Count : null;
return new KernelSearchResults(this.GetResultsAsStringAsync(searchResponse, cancellationToken), totalCount, GetResultsMetadata(searchResponse));
}
///
public async Task> GetTextSearchResultsAsync(string query, TextSearchOptions? searchOptions = null, CancellationToken cancellationToken = new CancellationToken())
{
searchOptions ??= new TextSearchOptions();
var filters = ExtractFiltersFromLegacy(searchOptions.Filter);
BraveSearchResponse? searchResponse = await this.ExecuteSearchAsync(query, searchOptions.Top, searchOptions.Skip, filters, cancellationToken).ConfigureAwait(false);
long? totalCount = searchOptions.IncludeTotalCount ? searchResponse?.Web?.Results.Count : null;
return new KernelSearchResults(this.GetResultsAsTextSearchResultAsync(searchResponse, cancellationToken), totalCount, GetResultsMetadata(searchResponse));
}
///
public async Task> GetSearchResultsAsync(string query, TextSearchOptions? searchOptions = null,
CancellationToken cancellationToken = new CancellationToken())
{
searchOptions ??= new TextSearchOptions();
var filters = ExtractFiltersFromLegacy(searchOptions.Filter);
BraveSearchResponse? searchResponse = await this.ExecuteSearchAsync(query, searchOptions.Top, searchOptions.Skip, filters, cancellationToken).ConfigureAwait(false);
long? totalCount = searchOptions.IncludeTotalCount ? searchResponse?.Web?.Results.Count : null;
return new KernelSearchResults