-
Notifications
You must be signed in to change notification settings - Fork 4k
.Net: Added BraveConnector in WebSearchPlugin #11308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
957d6ac
.Net: Add Brave API connector and options for web search functionalit…
N-E-W-T-O-N af06b37
.Net: Add Brave search URL functions for various query types
N-E-W-T-O-N e5c6c38
.Net: Add BraveTextSearch registration methods to WebKernelBuilder an…
N-E-W-T-O-N 29d4882
.Net: Update BraveConnector to handle video results and fix query par…
N-E-W-T-O-N 45a1a9c
.Net:
N-E-W-T-O-N 644b27c
Add support for additional query types in Brave search functionality
N-E-W-T-O-N 4f6a1a0
Add UseGzip property to BraveTextSearchOptions for response data decr…
N-E-W-T-O-N e760877
Add BraveWebResult class to represent web search results
N-E-W-T-O-N 2d14978
Merge branch 'microsoft:main' into brave
N-E-W-T-O-N 73cf9a1
.Net: Remove the Unwanted overide method
N-E-W-T-O-N b916dd3
.NET : Removed property UseGzip from BraveTextSearchOptions & fix mi…
N-E-W-T-O-N 92f0d2f
Refactor BraveSearchResponse: Remove unwanted class & Properties,Clea…
N-E-W-T-O-N e45164f
Refactor BraveWebResult: Improve property initialization and update X…
N-E-W-T-O-N ad30003
Refactor BraveSearchResponse: Remove unused properties and clean up c…
N-E-W-T-O-N 54e33e2
.NET:Refactor BraveTextSearch: Improve code readability, add query va…
N-E-W-T-O-N 0d302e1
.NET:Add test data for Brave search results in JSON format.
N-E-W-T-O-N c945f44
.NET:Add Brave search URL test cases to SearchUrlPluginTests
N-E-W-T-O-N cc6379a
.NET:Add unit tests for BraveTextSearch functionality and response ha…
N-E-W-T-O-N 416c515
Merge branch 'main' into brave
RogerBarreto b22e471
Fix warnings
RogerBarreto c3f56e0
remove comment
RogerBarreto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Text.Json; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Abstractions; | ||
using Microsoft.SemanticKernel.Http; | ||
|
||
namespace Microsoft.SemanticKernel.Plugins.Web.Brave; | ||
|
||
/// <summary> | ||
/// Brave API connector. | ||
/// </summary> | ||
public sealed class BraveConnector : IWebSearchEngineConnector | ||
RogerBarreto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
private readonly ILogger _logger; | ||
private readonly HttpClient _httpClient; | ||
private readonly string? _apiKey; | ||
private readonly Uri? _uri = null; | ||
private const string DefaultUri = "https://api.search.brave.com/res/v1/web/search?q"; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BraveConnector"/> class. | ||
/// </summary> | ||
/// <param name="apiKey">The API key to authenticate the connector.</param> | ||
/// <param name="uri">The URI of the Bing Search instance. Defaults to "https://api.bing.microsoft.com/v7.0/search?q".</param> | ||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param> | ||
public BraveConnector(string apiKey, Uri? uri = null, ILoggerFactory? loggerFactory = null) : | ||
this(apiKey, HttpClientProvider.GetHttpClient(), uri, loggerFactory) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BraveConnector"/> class. | ||
/// </summary> | ||
/// <param name="apiKey">The API key to authenticate the connector.</param> | ||
/// <param name="httpClient">The HTTP client to use for making requests.</param> | ||
/// <param name="uri">The URI of the Bing Search instance. Defaults to "https://api.bing.microsoft.com/v7.0/search?q".</param> | ||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param> | ||
public BraveConnector(string apiKey, HttpClient httpClient, Uri? uri = null, ILoggerFactory? loggerFactory = null) | ||
{ | ||
Verify.NotNull(httpClient); | ||
|
||
this._apiKey = apiKey; | ||
this._logger = loggerFactory?.CreateLogger(typeof(BraveConnector)) ?? NullLogger.Instance; | ||
this._httpClient = httpClient; | ||
this._httpClient.DefaultRequestHeaders.Add("User-Agent", HttpHeaderConstant.Values.UserAgent); | ||
this._httpClient.DefaultRequestHeaders.Add(HttpHeaderConstant.Names.SemanticKernelVersion, HttpHeaderConstant.Values.GetAssemblyVersion(typeof(BraveConnector))); | ||
this._uri = uri ?? new Uri(DefaultUri); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public async Task<IEnumerable<T>> SearchAsync<T>(string query, int count = 1, int offset = 0, CancellationToken cancellationToken = default) | ||
{ | ||
Verify.NotNull(query) ; | ||
|
||
if (count is <= 0 or >= 21) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} value must be greater than 0 and less than 21."); | ||
} | ||
|
||
if (offset is < 0 or > 10) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(offset),offset, $"{nameof(count)} value must be equal or greater than 0 and less than 10."); | ||
} | ||
|
||
Uri uri = new($"{this._uri}={Uri.EscapeDataString(query.Trim())}&count={count}&offset={offset}"); | ||
|
||
this._logger.LogDebug("Sending request: {Uri}", uri); | ||
|
||
using HttpResponseMessage response = await this.SendGetRequestAsync(uri, cancellationToken).ConfigureAwait(false); | ||
|
||
this._logger.LogDebug("Response received: {StatusCode}", response.StatusCode); | ||
|
||
string json = await response.Content.ReadAsStringWithExceptionMappingAsync(cancellationToken).ConfigureAwait(false); | ||
|
||
// Sensitive data, logging as trace, disabled by default | ||
this._logger.LogTrace("Response content received: {Data}", json); | ||
|
||
var data = JsonSerializer.Deserialize<BraveSearchResponse<BraveWebResult>>(json); | ||
|
||
List<T>? returnValues = null; | ||
if (data?.Web?.Results is not null) | ||
{ | ||
if (typeof(T) == typeof(string)) | ||
{ | ||
var results = data?.Web?.Results; | ||
returnValues = results?.Select(x => x.Description).ToList() as List<T>; | ||
} | ||
else if (typeof(T) == typeof(BraveWebResult)) | ||
{ | ||
var results = data?.Web?.Results!; | ||
returnValues = results.Take(count).ToList() as List<T>; | ||
} | ||
else if (typeof(T) == typeof(WebPage)) | ||
{ | ||
List<WebPage>? webPages = data.Web?.Results | ||
.Select(x => new WebPage() { Name = x.Title, Snippet = x.Description, Url = x.Url }).ToList(); | ||
|
||
returnValues = webPages!.Take(count).ToList() as List<T>; | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException($"Type {typeof(T)} is not supported."); | ||
} | ||
} | ||
|
||
if (data?.Videos?.Results is not null) | ||
{ | ||
if (typeof(T) == typeof(string)) | ||
{ | ||
var results = data?.Videos?.Results; | ||
returnValues = results?.Select(x => x.Description).ToList() as List<T>; | ||
} | ||
else if (typeof(T) == typeof(BraveWebResult)) | ||
{ | ||
var results = data?.Videos?.Results!; | ||
returnValues = results.Take(count).ToList() as List<T>; | ||
} | ||
else if (typeof(T) == typeof(WebPage)) | ||
{ | ||
List<WebPage>? webPages = data.Videos?.Results | ||
.Select(x => new WebPage() { Name = x.Title, Snippet = x.Description, Url = x.Url }).ToList(); | ||
|
||
returnValues = webPages!.Take(count).ToList() as List<T>; | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException($"Type {typeof(T)} is not supported."); | ||
} | ||
} | ||
return | ||
returnValues is null ? [] : | ||
returnValues.Count <= count ? returnValues : | ||
returnValues.Take(count); | ||
} | ||
|
||
/// <summary> | ||
/// Sends a GET request to the specified URI. | ||
/// </summary> | ||
/// <param name="uri">The URI to send the request to.</param> | ||
/// <param name="cancellationToken">A cancellation token to cancel the request.</param> | ||
/// <returns>A <see cref="HttpResponseMessage"/> representing the response from the request.</returns> | ||
private async Task<HttpResponseMessage> SendGetRequestAsync(Uri uri, CancellationToken cancellationToken = default) | ||
{ | ||
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri); | ||
|
||
if (!string.IsNullOrEmpty(this._apiKey)) | ||
{ | ||
httpRequestMessage.Headers.Add("X-Subscription-Token", this._apiKey); | ||
} | ||
|
||
return await this._httpClient.SendWithSuccessCheckAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.