diff --git a/blazor/SyncfusionAISamples/Components/Service/AzureAIService.cs b/blazor/SyncfusionAISamples/Components/Service/AzureAIService.cs index 229b964..a4aabcc 100644 --- a/blazor/SyncfusionAISamples/Components/Service/AzureAIService.cs +++ b/blazor/SyncfusionAISamples/Components/Service/AzureAIService.cs @@ -1,18 +1,34 @@ using Microsoft.Extensions.AI; using Syncfusion.Blazor.SmartComponents; +using System.Collections.Generic; +using System.Threading.Tasks; +using SyncfusionAISamples.Service; +using Syncfusion.Blazor.Charts.Chart.Internal; +using Azure.AI.OpenAI; +using System.ClientModel; +using Microsoft.JSInterop; namespace SyncfusionAISamples.Service { public class AzureAIService { - private OpenAIConfiguration _openAIConfiguration; + //private readonly OpenAIConfiguration _openAIConfiguration; + private readonly UserTokenService _userTokenService; private ChatParameters chatParameters_history = new ChatParameters(); + private IChatClient _azureAIClient; - public AzureAIService(OpenAIConfiguration openAIConfiguration) + public AzureAIService(UserTokenService userTokenService) { - _openAIConfiguration = openAIConfiguration; + string apiKey = "your-api-key"; + string deploymentName = "your-deployment-name"; + // your-azure-endpoint-url + string endpoint = ""; + //_openAIConfiguration = openAIConfiguration; + _userTokenService = userTokenService; + _azureAIClient = new AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey)).AsChatClient(deploymentName); } + /// /// Gets a text completion from the Azure OpenAI service. /// @@ -23,6 +39,14 @@ public AzureAIService(OpenAIConfiguration openAIConfiguration) /// The AI-generated completion as a string. public async Task GetCompletionAsync(string prompt, bool returnAsJson = true, bool appendPreviousResponse = false, string systemRole = null) { + string userCode = await _userTokenService.GetUserFingerprintAsync(); + int remainingTokens = await _userTokenService.GetRemainingTokensAsync(userCode); + + if (remainingTokens <= 0) + { + await _userTokenService.ShowAlert(); + return string.Empty; + } string systemMessage = returnAsJson ? "You are a helpful assistant that only returns and replies with valid, iterable RFC8259 compliant JSON in your responses unless I ask for any other format. Do not provide introductory words such as 'Here is your result' or '```json', etc. in the response" : !string.IsNullOrEmpty(systemRole) ? systemRole : "You are a helpful assistant"; try { @@ -44,12 +68,13 @@ public async Task GetCompletionAsync(string prompt, bool returnAsJson = new ChatMessage(ChatRole.User,prompt) }; } - var completion = await _openAIConfiguration.GetChatResponseAsync(chatParameters); + var completion = await GetChatResponseAsync(chatParameters); if (appendPreviousResponse) { chatParameters_history?.Messages?.Add(new ChatMessage(ChatRole.Assistant, completion.ToString())); } - return completion.ToString(); + await _userTokenService.UpdateTokensAsync(userCode, (int)(remainingTokens - completion.Usage.TotalTokenCount)); + return completion.Message.Text.ToString(); } catch (Exception ex) { @@ -57,5 +82,28 @@ public async Task GetCompletionAsync(string prompt, bool returnAsJson = return ""; } } + + public async Task GetChatResponseAsync(ChatParameters options) + { + // Create a completion request with the provided parameters + var completionRequest = new ChatOptions + { + Temperature = options.Temperature ?? 0.5f, + TopP = options.TopP ?? 1.0f, + MaxOutputTokens = options.MaxTokens ?? 2000, + FrequencyPenalty = options.FrequencyPenalty ?? 0.0f, + PresencePenalty = options.PresencePenalty ?? 0.0f, + StopSequences = options.StopSequences + }; + try + { + ChatCompletion completion = await _azureAIClient.CompleteAsync(options.Messages, completionRequest); + return completion; + } + catch (Exception ex) + { + throw; + } + } } } diff --git a/blazor/SyncfusionAISamples/Components/Service/CustomInferenceBackend.cs b/blazor/SyncfusionAISamples/Components/Service/CustomInferenceBackend.cs new file mode 100644 index 0000000..7abd94e --- /dev/null +++ b/blazor/SyncfusionAISamples/Components/Service/CustomInferenceBackend.cs @@ -0,0 +1,57 @@ +using Azure.AI.OpenAI; +using Microsoft.Extensions.AI; +using Microsoft.JSInterop; +using OpenAI; +using Syncfusion.Blazor.SmartComponents; +using SyncfusionAISamples.Service; +using System.ClientModel; + +namespace SyncfusionAISamples.Components.Service +{ + public class CustomInferenceBackend : IInferenceBackend + { + private IChatClient client; + private readonly UserTokenService _userTokenService; + public CustomInferenceBackend(UserTokenService userTokenService) + { + string apiKey = "your-api-key"; + string deploymentName = "your-deployment-name"; + // your-azure-endpoint-url + string endpoint = ""; + client = new AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey)).AsChatClient(deploymentName); + _userTokenService = userTokenService; + } + + public async Task GetChatResponseAsync(ChatParameters options) + { + //string userCode = await _userTokenService.GetUserFingerprintAsync(); + //int remainingTokens = await _userTokenService.GetRemainingTokensAsync(userCode); + + //if (remainingTokens <= 0) + //{ + // await _userTokenService.ShowAlert(); + // return string.Empty; + //} + // Create a completion request with the provided parameters + var completionRequest = new ChatOptions + { + Temperature = options.Temperature ?? 0.5f, + TopP = options.TopP ?? 1.0f, + MaxOutputTokens = options.MaxTokens ?? 2000, + FrequencyPenalty = options.FrequencyPenalty ?? 0.0f, + PresencePenalty = options.PresencePenalty ?? 0.0f, + StopSequences = options.StopSequences + }; + try + { + ChatCompletion completion = await client.CompleteAsync(options.Messages, completionRequest); + //await _userTokenService.UpdateTokensAsync(userCode, (int)(remainingTokens - completion.Usage.TotalTokenCount)); + return completion.Message.Text; + } + catch (Exception ex) + { + throw; + } + } + } +} diff --git a/blazor/SyncfusionAISamples/Components/Service/UserTokenService.cs b/blazor/SyncfusionAISamples/Components/Service/UserTokenService.cs new file mode 100644 index 0000000..f9978da --- /dev/null +++ b/blazor/SyncfusionAISamples/Components/Service/UserTokenService.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.JSInterop; + +namespace SyncfusionAISamples.Service +{ + public class UserTokenService + { + private readonly IJSRuntime _jsRuntime; + private const string TokenFilePath = "user_tokens.json"; + private static readonly TimeZoneInfo IndianStandardTime = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time"); + + public UserTokenService(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } + + public async Task GetUserFingerprintAsync() + { + return await _jsRuntime.InvokeAsync("fingerPrint"); + } + + public async Task GetRemainingTokensAsync(string userCode) + { + var tokens = await CheckAndResetTokensAsync(userCode); + return tokens.ContainsKey(userCode) ? tokens[userCode].RemainingTokens : 10000; + } + + public async Task UpdateTokensAsync(string userCode, int tokens) + { + var tokenData = await ReadTokensFromFileAsync(); + if (tokenData.ContainsKey(userCode)) + { + tokenData[userCode].RemainingTokens = tokens; + } + else + { + tokenData[userCode] = new UserTokenInfo + { + UserId = userCode, + DateOfLogin = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, IndianStandardTime), + RemainingTokens = tokens + }; + } + await WriteTokensToFileAsync(tokenData); + } + + public async Task> CheckAndResetTokensAsync(string userCode) + { + var tokenData = await ReadTokensFromFileAsync(); + if (tokenData.ContainsKey(userCode)) + { + var userTokenInfo = tokenData[userCode]; + var currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, IndianStandardTime); + var timeDifference = currentTime - userTokenInfo.DateOfLogin; + + if (timeDifference.TotalHours > 1) + { + userTokenInfo.RemainingTokens = 10000; // Reset tokens + userTokenInfo.DateOfLogin = currentTime; // Update login time + await WriteTokensToFileAsync(tokenData); + } + } + return tokenData; + } + + private async Task> ReadTokensFromFileAsync() + { + if (!File.Exists(TokenFilePath)) + { + var initialData = new Dictionary(); + await WriteTokensToFileAsync(initialData); + return initialData; + } + + var json = await File.ReadAllTextAsync(TokenFilePath); + return JsonSerializer.Deserialize>(json) ?? new Dictionary(); + } + + private async Task WriteTokensToFileAsync(Dictionary tokenData) + { + var json = JsonSerializer.Serialize(tokenData, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(TokenFilePath, json); + } + + public async Task ShowAlert() + { + await _jsRuntime.InvokeVoidAsync("showAlert", "You have no remaining tokens."); + } + } + + public class UserTokenInfo + { + public string UserId { get; set; } + public DateTime DateOfLogin { get; set; } + public int RemainingTokens { get; set; } + } +} diff --git a/blazor/SyncfusionAISamples/Program.cs b/blazor/SyncfusionAISamples/Program.cs index 68abb69..35f466f 100644 --- a/blazor/SyncfusionAISamples/Program.cs +++ b/blazor/SyncfusionAISamples/Program.cs @@ -1,8 +1,10 @@ using FileManagerAI.Services; +using Microsoft.AspNetCore.DataProtection.KeyManagement; using SmartComponents.LocalEmbeddings; using Syncfusion.Blazor; using Syncfusion.Blazor.SmartComponents; using SyncfusionAISamples.Components; +using SyncfusionAISamples.Components.Service; using SyncfusionAISamples.Service; var builder = WebApplication.CreateBuilder(args); @@ -23,19 +25,13 @@ // Local Embeddings builder.Services.AddSingleton(); -// OpenAI Service -string apiKey = "your-api-key"; -string deploymentName = "your-deployment-name"; -// your-azure-endpoint-url -string endpoint = ""; -//Injecting smart components -builder.Services.AddSyncfusionSmartComponents() - .ConfigureCredentials(new AIServiceCredentials(apiKey, deploymentName, endpoint)) //Configuring credentials for AI functionality to work - .InjectOpenAIInference(); // Injecting OpenAI Services -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); +//builder.Services.AddSingleton(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddSyncfusionSmartComponents(); #endregion var app = builder.Build(); diff --git a/blazor/SyncfusionAISamples/SyncfusionAISamples.csproj b/blazor/SyncfusionAISamples/SyncfusionAISamples.csproj index c799c8d..6c929ac 100644 --- a/blazor/SyncfusionAISamples/SyncfusionAISamples.csproj +++ b/blazor/SyncfusionAISamples/SyncfusionAISamples.csproj @@ -7,6 +7,7 @@ + diff --git a/blazor/SyncfusionAISamples/wwwroot/scripts/smart-component.js b/blazor/SyncfusionAISamples/wwwroot/scripts/smart-component.js index e902a96..812cb77 100644 --- a/blazor/SyncfusionAISamples/wwwroot/scripts/smart-component.js +++ b/blazor/SyncfusionAISamples/wwwroot/scripts/smart-component.js @@ -435,4 +435,23 @@ window.preventTabDefault = function (textareaId, dotnetRef) { }); } -/* Diagram scripts end */ \ No newline at end of file +/* Diagram scripts end */ + +async function fingerPrint() { + try { + // Import FingerprintJS and load the agent + const FingerprintJS = await import('https://openfpcdn.io/fingerprintjs/v4'); + const fp = await FingerprintJS.load(); + + // Get the visitor identifier + const result = await fp.get(); + return result.visitorId; + } catch (error) { + console.error(error); + return null; + } +} + +function showAlert(message) { + alert(message); +}