Skip to content

Commit e77d35c

Browse files
Merge pull request #3603 from Syncfusion-Content/development
Development
2 parents 46ce88d + 55df722 commit e77d35c

File tree

15 files changed

+1886
-27
lines changed

15 files changed

+1886
-27
lines changed
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
---
2+
layout: post
3+
title: AI-Powered Smart Searching in SfAutocomplete Control | Syncfusion®
4+
description: Learn about how to implement AI-powered smart search using Syncfusion® .NET MAUI Autocomplete control.
5+
platform: maui
6+
control: SfAutocomplete
7+
documentation: ug
8+
---
9+
10+
# Implementing AI-Powered Smart Search in .NET MAUI Autocomplete
11+
12+
This document will walk you through the implementation of an advanced search functionality in the Syncfusion [.NET MAUI Autocomplete](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.Inputs.SfAutocomplete.html) control. The example leverages the power of Azure OpenAI for an intelligent, AI-driven search experience.
13+
14+
## Integrating Azure OpenAI with your .NET MAUI App
15+
16+
First, ensure you have access to [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/overview) and have created a deployment in the Azure portal.
17+
18+
If you don’t have access, please refer to the [create and deploy Azure OpenAI service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/create-resource?pivots=web-portal) guide to set up a new account.
19+
20+
Note down the deployment name, endpoint URL, and API key.
21+
22+
we’ll use the [Azure.AI.OpenAI](https://www.nuget.org/packages/Azure.AI.OpenAI/1.0.0-beta.12) NuGet package from the [NuGet Gallery](https://www.nuget.org/). So, before getting started, install the Azure.AI.OpenAI NuGet package in your .NET MAUI app.
23+
24+
In your base service class (AzureBaseService), initialize the OpenAIClient. Replace the Endpoint, DeploymentName, Key with actual values from your Azure OpenAI resource.
25+
26+
This creates a chat client using your endpoint, API key, and deployment name. It’s stored in the Client property for use in other methods.
27+
28+
ComboBoxAzureAIService use this Client to send prompts and receive completions.
29+
30+
In the `GetCompletion` method, we will construct the prompt and send it to the Azure OpenAI Service. The ChatHistory helps maintain context but is cleared for each new prompt in this implementation to ensure each search is independent.
31+
32+
{% tabs %}
33+
{% highlight c# %}
34+
35+
// AzureBaseService.cs
36+
public abstract class AzureBaseService
37+
{
38+
internal const string Endpoint = "YOUR_END_POINT_NAME";
39+
40+
internal const string DeploymentName = "DEPLOYMENT_NAME";
41+
42+
internal const string Key = "API_KEY";
43+
44+
public AzureBaseService()
45+
{
46+
}
47+
48+
/// <summary>
49+
/// To get the Azure open ai kernal method
50+
/// </summary>
51+
private void GetAzureOpenAIKernal()
52+
{
53+
try
54+
{
55+
var client = new AzureOpenAIClient(new Uri(Endpoint), new AzureKeyCredential(Key)).AsChatClient(modelId: DeploymentName);
56+
this.Client = client;
57+
}
58+
catch (Exception)
59+
{
60+
}
61+
}
62+
63+
}
64+
65+
{% endhighlight %}
66+
67+
{% endtabs %}
68+
69+
{% tabs %}
70+
{% highlight c# %}
71+
72+
//AzureAIService.cs
73+
74+
public class AzureAIService : AzureBaseService
75+
{
76+
/// <summary>
77+
/// Gets a completion response from the AzureAI service based on the provided prompt.
78+
/// </summary>
79+
/// <param name="prompt"></param>
80+
/// <param name="cancellationToken"></param>
81+
/// <returns></returns>
82+
public async Task<string> GetCompletion(string prompt, CancellationToken cancellationToken)
83+
{
84+
ChatHistory = string.Empty;
85+
if(ChatHistory != null)
86+
{
87+
ChatHistory = ChatHistory + "You are a filtering assistant.";
88+
ChatHistory = ChatHistory + prompt;
89+
try
90+
{
91+
if (Client != null)
92+
{
93+
cancellationToken.ThrowIfCancellationRequested();
94+
var chatresponse = await Client.CompleteAsync(prompt);
95+
return chatresponse.ToString();
96+
}
97+
}
98+
catch (RequestFailedException ex)
99+
{
100+
// Log the error message and rethrow the exception or handle it appropriately
101+
Debug.WriteLine($"Request failed: {ex.Message}");
102+
throw;
103+
}
104+
catch (Exception ex)
105+
{
106+
// Handle other potential exceptions
107+
Debug.WriteLine($"An error occurred: {ex.Message}");
108+
throw;
109+
}
110+
}
111+
return "";
112+
}
113+
}
114+
115+
{% endhighlight %}
116+
117+
{% endtabs %}
118+
119+
## Implementing custom filtering in .NET MAUI Autocomplete
120+
121+
The [.NET MAUI Autocomplete](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.Inputs.SfAutocomplete.html) control allows you to apply custom filter logic to suggest items based on your specific filter criteria by utilizing the `FilterBehavior` property, which is the entry point for our smart search logic.
122+
123+
**Step 1:** Let’s create a new business model to search country names. Refer to the following code example.
124+
125+
{% tabs %}
126+
{% highlight c# %}
127+
128+
// Model.cs
129+
130+
public class CountryModel
131+
{
132+
public string? Name { get; set; }
133+
}
134+
135+
//ViewModel.cs
136+
137+
internal class CountryViewModel : INotifyPropertyChanged
138+
{
139+
private ObservableCollection<CountryModel> countries;
140+
141+
public ObservableCollection<CountryModel> Countries
142+
{
143+
get { return countries; }
144+
set { countries = value; OnPropertyChanged(nameof(Countries)); }
145+
}
146+
public CountryViewModel()
147+
{
148+
countries = new ObservableCollection<CountryModel>
149+
{
150+
new CountryModel { Name = "Afghanistan" },
151+
new CountryModel { Name = "Akrotiri" },
152+
new CountryModel { Name = "Albania" },
153+
new CountryModel { Name = "Algeria" },
154+
new CountryModel { Name = "American Samoa" },
155+
new CountryModel { Name = "Andorra" },
156+
new CountryModel { Name = "Angola" },
157+
new CountryModel { Name = "Anguilla" },
158+
....
159+
}
160+
}
161+
162+
public event PropertyChangedEventHandler? PropertyChanged;
163+
164+
private void OnPropertyChanged(string propertyName)
165+
{
166+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
167+
}
168+
}
169+
170+
{% endhighlight %}
171+
172+
{% endtabs %}
173+
174+
**Step 2:** Connecting the Custom Filter to Azure OpenAI
175+
176+
Implement the `GetMatchingItemsAsync` method from the interface. This method is the heart of the custom filter. It is invoked every time the text in the [Autocomplete](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.Inputs.SfAutocomplete.html) control changes.
177+
178+
The logic within [Autocomplete](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.Inputs.SfAutocomplete.html) intelligently decides whether to perform an online AI search based on the availability of Azure credentials.
179+
180+
To get accurate and structured results from the AI, we must provide a detailed prompt. This is constructed inside the
181+
`FilterCountriesUsingAzureAI` method.
182+
183+
The `FilterCountriesUsingAzureAI` method uses prompt engineering to instruct the AI on how to filter the results, including asking it to handle spelling mistakes and providing the response in a clean format.
184+
185+
{% tabs %}
186+
{% highlight c# %}
187+
188+
//CustomFilter.cs
189+
190+
public class CustomFilter : IAutocompleteFilterBehavior
191+
{
192+
private readonly AzureAIService _azureAIService;
193+
public ObservableCollection<CountryModel> Countries { get; set; }
194+
public ObservableCollection<CountryModel> FilteredCountries { get; set; } = new ObservableCollection<CountryModel>();
195+
private CancellationTokenSource? _cancellationTokenSource;
196+
private SoundexAndLevensteinDistance soundexAndLevensteinDistance;
197+
198+
public CustomFilter()
199+
{
200+
_azureAIService = new AzureAIService();
201+
Countries = new ObservableCollection<CountryModel>();
202+
_cancellationTokenSource = new CancellationTokenSource();
203+
soundexAndLevensteinDistance = new SoundexAndLevensteinDistance();
204+
}
205+
206+
/// <summary>
207+
/// Finds matching items using the typed text
208+
/// </summary>
209+
/// <param name="source"></param>
210+
/// <param name="filterInfo"></param>
211+
/// <returns></returns>
212+
public async Task<object?> GetMatchingItemsAsync(SfAutocomplete source, AutocompleteFilterInfo filterInfo)
213+
{
214+
if (string.IsNullOrEmpty(filterInfo.Text))
215+
{
216+
_cancellationTokenSource?.Cancel();
217+
FilteredCountries.Clear();
218+
return await Task.FromResult(FilteredCountries);
219+
}
220+
221+
Countries = (ObservableCollection<CountryModel>)source.ItemsSource;
222+
223+
// If the API key is not provided, perform an offline search using Soundex and Levenshtein algorithms.
224+
if (!AzureBaseService.IsCredentialValid)
225+
{
226+
foreach (CountryModel country in Countries)
227+
{
228+
soundexAndLevensteinDistance.FilterItemsBySoundexAndLevenshtein(filterInfo.Text, country.Name!);
229+
}
230+
var filteredItems = soundexAndLevensteinDistance.GetOrder();
231+
232+
return await Task.FromResult(filteredItems);
233+
}
234+
235+
string listItems = string.Join(", ", Countries!.Select(c => c.Name));
236+
237+
// Join the first five items with newline characters for demo output template for AI
238+
string outputTemplate = string.Join("\n", Countries.Take(5).Select(c => c.Name));
239+
240+
//The cancellationToken was used for cancelling the API request if user types continuously
241+
_cancellationTokenSource?.Cancel();
242+
_cancellationTokenSource = new CancellationTokenSource();
243+
var cancellationToken = _cancellationTokenSource.Token;
244+
245+
//Passing the User Input, ItemsSource, Reference output and CancellationToken
246+
var filterCountries = await FilterCountriesUsingAzureAI(filterInfo.Text, listItems, outputTemplate, cancellationToken);
247+
248+
return await Task.FromResult(filterCountries);
249+
}
250+
251+
/// <summary>
252+
/// Filters country names based on user input using Azure AI.
253+
/// </summary>
254+
/// <param name="userInput"></param>
255+
/// <param name="itemsList"></param>
256+
/// <param name="outputTemplate"></param>
257+
/// <param name="cancellationToken"></param>
258+
/// <returns></returns>
259+
public async Task<ObservableCollection<CountryModel>> FilterCountriesUsingAzureAI(string userInput, string itemsList, string outputTemplate, CancellationToken cancellationToken)
260+
{
261+
if (!string.IsNullOrEmpty(userInput))
262+
{
263+
var prompt = $"Filter the list items based on the user input using character Starting with and Phonetic algorithms like Soundex or Damerau-Levenshtein Distance. " +
264+
$"The filter should ignore spelling mistakes and be case insensitive. " +
265+
$"Return only the filtered items with each item in new line without any additional content like explanations, Hyphen, Numberings and - Minus sign. Ignore the content 'Here are the filtered items or similar things' " +
266+
$"Only return items that are present in the List Items. " +
267+
$"Ensure that each filtered item is returned in its entirety without missing any part of its content. " +
268+
$"Arrange the filtered items that starting with the user input's first letter are at the first index, followed by other matches. " +
269+
$"Examples of filtering behavior: " +
270+
$" userInput: a, filter the items starting with A " +
271+
$" userInput: b, filter items starting with B " +
272+
$" userInput: c, filter items starting with C " +
273+
$" userInput: d, filter items starting with D " +
274+
$" userInput: e, filter items starting with E " +
275+
$" userInput: f, filter items starting with F " +
276+
$" userInput: i, filter items starting with I " +
277+
$" userInput: z, filter items starting with Z " +
278+
$" userInput: l, filter items starting with L " +
279+
$" userInput: q, filter items starting with Q " +
280+
$" userInput: o, filter items starting with O " +
281+
$" userInput: in, filter items starting with In " +
282+
$" userInput: pa, filter items starting with Pa " +
283+
$" userInput: em, filter items starting with Em " +
284+
$"The example data are for reference, dont provide these as output. Filter the item from list items properly" +
285+
$"Here is the User input: {userInput}, " +
286+
$"List of Items: {itemsList}" +
287+
$"If no items found, return \"Empty\" " +
288+
$"Dont use 'Here are the filtered items:' in the output. Check this demo output template, you should return output like this: {outputTemplate} ";
289+
290+
var completion = await _azureAIService.GetCompletion(prompt, cancellationToken);
291+
292+
var filteredCountryNames = completion.Split('\n').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).ToList();
293+
294+
if (FilteredCountries.Count > 0)
295+
FilteredCountries.Clear();
296+
FilteredCountries.AddRange(
297+
Countries
298+
.Where(i => filteredCountryNames.Any(item => i.Name!.StartsWith(item))));
299+
}
300+
return FilteredCountries;
301+
}
302+
303+
}
304+
305+
{% endhighlight %}
306+
307+
{% endtabs %}
308+
309+
**Step:3** Applying Custom Filtering to AutoComplete
310+
311+
Applying custom filtering to the [Autocomplete](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.Inputs.SfAutocomplete.html) control by using the `FilterBehavior` property.
312+
313+
{% tabs %}
314+
{% highlight xaml %}
315+
316+
<editors:SfAutocomplete x:Name="autoComplete"
317+
DropDownPlacement="Bottom"
318+
MaxDropDownHeight="200"
319+
TextSearchMode="Contains"
320+
DisplayMemberPath="Name"
321+
TextMemberPath="Name"
322+
ItemsSource="{Binding Countries}">
323+
<editors:SfAutocomplete.FilterBehavior>
324+
<local:CustomFilter/>
325+
</editors:SfAutocomplete.FilterBehavior>
326+
</editors:SfAutocomplete>
327+
328+
{% endhighlight %}
329+
330+
{% endtabs %}
331+
332+
The following image demonstrates the output of the above AI-based search using a custom filtering sample.
333+
334+
![.NET MAUI AutoComplete With AI Smart Search.](Images/AISmartSearch/ai_smart_search.png)
335+
336+
You can find the complete sample from this [link](https://github.com/SyncfusionExamples/Smart-AI-Searching-using-.NET-MAUI-Autocomplete).
337+
338+
By combining a powerful AI-driven online search with a robust you can create a truly smart and reliable search experience in your .NET MAUI applications.
12.2 KB
Loading

0 commit comments

Comments
 (0)