using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; using Microsoft.SemanticKernel.ChatCompletion; using OllamaSharp; using xAiApi.AI.Configuration; using xAiApi.AI.Constants; using xAiApi.AI.Extensions; using xAiApi.AI.Interfaces; using xAiApi.AI.Interfaces.Entities; using xAiApi.AI.Models.Entities; using xAiModels.Constants; using xAiModels.Extensions; using xAiModels.Models; using xCommons.Extensions; using xExceptions.Constants; using xIdentityModels.Models; using xIdentityService.Extensions; using xIdentityService.Interfaces; using xModels.Dtos; namespace xAiApi.AI.Services { /// /// Provide Ai Capabilities Using AI Service ... /// using User Data Info ... /// public class XAiProvider : IXAiProvider { /// /// Configuration ... /// /// public XAiApiConfiguration Configuration { get; } // private readonly IChatClient chatClient; private readonly ChatOptions chatOptions; private readonly IXIdentityProvider identityProvider; private readonly IXAiProjectRepository aiProjectRepository; private readonly IXAiMessageRepository aiMessagetRepository; private readonly IXAiConversationRepository aiConversationRepository; public XAiProvider( XAiApiConfiguration configuration, IXIdentityProvider identityProvider, IXAiProjectRepository aiProjectRepository, IXAiMessageRepository aiMessagetRepository, IXAiConversationRepository aiConversationRepository ) { // Configuration = configuration; // this.identityProvider = identityProvider; this.aiProjectRepository = aiProjectRepository; this.aiMessagetRepository = aiMessagetRepository; this.aiConversationRepository = aiConversationRepository; // // Prepare Chat Model ... chatClient = new ChatClientBuilder(new OllamaApiClient( new Uri(configuration.Url), configuration.Model )) // .UseFunctionInvocation() .Build(); // // Configuring Chant Options ... chatOptions = new ChatOptions() { // // Preparing Tools ... // Tools = [ // // // // Tempreature Tool ... // AIFunctionFactory.Create((string location, string unit) => { // // // var temp = Random.Shared.Next(5, 20); // var cond = Random.Shared.Next(0, 1) == 0 ? "sunny" : "rainy"; // // // var result = $"The weather is {temp} degrees C and {cond}."; // // // return result; // }, // "get_current_weather", // "Get the current weather in given location" // ) // ], }; } // #region Helpers ... /// /// Converts Entity to Dto ... /// /// /// /// /// public async Task ToDto( XAiProject entity, CancellationToken cancellationToken = default ) { // XAiProjectDto result = null; // if (!entity.IsNullOrDefault()) { // result = entity.ToDto(); if (!entity.OwnerId.IsNullOrEmpty()) { // result.Owner = await GetPerson( entity.OwnerId, cancellationToken ); } // result.Conversations = await ToDtos( entity.Conversations, cancellationToken ); } // return result; } /// /// Converts Entity to Dto ... /// /// /// /// /// public async Task ToDto( XAiMessage entity, CancellationToken cancellationToken = default ) { // XAiMessageDto result = null; // if (!entity.IsNullOrDefault()) { // result = entity.ToDto(); if (!entity.OwnerId.IsNullOrEmpty()) { // result.Owner = await GetPerson( entity.OwnerId, cancellationToken ); } // result.Conversation = await ToDto( entity.Conversation, cancellationToken ); } // return result; } /// /// Converts Entity to Dto ... /// /// /// /// /// public async Task ToDto( XAiConversation entity, CancellationToken cancellationToken = default ) { // XAiConversationDto result = null; // if (!entity.IsNullOrDefault()) { // result = entity.ToDto(); if (!entity.OwnerId.IsNullOrEmpty()) { // result.Owner = await GetPerson( entity.OwnerId, cancellationToken ); } // result.Project = await ToDto( entity.Project, cancellationToken ); // result.Messages = await ToDtos( entity.Messages, cancellationToken ); } // return result; } /// /// Converts a List of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtos( IEnumerable entities, CancellationToken cancellationToken = default ) { // IList list = new List(); // var enumerable = entities.ToAsyncEnumerable(cancellationToken); await foreach (var entity in enumerable) { // var dto = await ToDto( entity, cancellationToken ); if (!dto.IsNullOrDefault()) { list.Add(dto); } } // return list.AsEnumerable(); } /// /// Converts a List of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtos( IEnumerable entities, CancellationToken cancellationToken = default ) { // IList list = new List(); // var enumerable = entities.ToAsyncEnumerable(cancellationToken); await foreach (var entity in enumerable) { // var dto = await ToDto( entity, cancellationToken ); if (!dto.IsNullOrDefault()) { list.Add(dto); } } // return list.AsEnumerable(); } /// /// Converts a List of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtos( IEnumerable entities, CancellationToken cancellationToken = default ) { // IList list = new List(); // var enumerable = entities.ToAsyncEnumerable(cancellationToken); await foreach (var entity in enumerable) { // var dto = await ToDto( entity, cancellationToken ); if (!dto.IsNullOrDefault()) { list.Add(dto); } } // return list.AsEnumerable(); } /// /// Converts a QueryResult of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtoQueryResult( XQueryResult queryResult, CancellationToken cancellationToken = default ) { // var result = new XQueryResult(); // if (!queryResult.IsNullOrDefault()) { // result = result.UpdateData( updateWith: queryResult, propertyBlackList: new List { nameof(XQueryResult.Items) } ); // if (queryResult.Items.HasChild()) { // result.Items = await ToDtos( queryResult.Items, cancellationToken ); } } // return result; } /// /// Converts a QueryResult of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtoQueryResult( XQueryResult queryResult, CancellationToken cancellationToken = default ) { // var result = new XQueryResult(); // if (!queryResult.IsNullOrDefault()) { // result = result.UpdateData( updateWith: queryResult, propertyBlackList: new List { nameof(XQueryResult.Items) } ); // if (queryResult.Items.HasChild()) { // result.Items = await ToDtos( queryResult.Items, cancellationToken ); } } // return result; } /// /// Converts a QueryResult of Entities to Dtos ... /// /// /// /// /// public async Task> ToDtoQueryResult( XQueryResult queryResult, CancellationToken cancellationToken = default ) { // var result = new XQueryResult(); // if (!queryResult.IsNullOrDefault()) { // result = result.UpdateData( updateWith: queryResult, propertyBlackList: new List { nameof(XQueryResult.Items) } ); // if (queryResult.Items.HasChild()) { // result.Items = await ToDtos( queryResult.Items, cancellationToken ); } } // return result; } /// /// Check Permission for Ai Actions ... /// /// /// /// /// public async Task HasPermission( string projectId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // var result = !userInfo.IsNullOrDefault() && !userInfo.UserId.IsNullOrEmpty(); if (!result) { return result; } // try { // var person = await GetPerson( ownerId: userInfo.UserId, cancellationToken: cancellationToken ); // result = !person.IsNullOrDefault(); } catch { result = false; } // return result; } /// /// Prepare Prompt ... /// /// /// /// /// /// public async Task PreparePrompt( string prompt, string projectId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // var result = prompt; // // TODO: Complete this ... // return await Task.FromResult(result); } #endregion /// /// Ask a Question ... /// /// /// /// /// /// /// public async Task Ask( string prompt, string projectId = null, string conversationId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Access Project ... var project = await GetProject( userInfo: userInfo, projectId: projectId, cancellationToken: cancellationToken ); var isValid = !project.IsNullOrDefault(); if (!isValid) { XException.NotFound.Throw(); } // // Check Project is Default Project or not ... var isDefaultProject = project.Title == XAiProjectEnum.Default.GetStringValue(); // // Access Conversation ... var conversation = await GetConversation( userInfo: userInfo, projectId: projectId, conversationId: conversationId, cancellationToken: cancellationToken ); isValid = !conversation.IsNullOrDefault(); if (!isValid) { XException.NotFound.Throw(); } // // Check First Question or not ... var isFirstConversation = !conversation.Messages.HasChild(); // // Check Permissions ... isValid = await HasPermission( userInfo: userInfo, projectId: projectId, cancellationToken: cancellationToken ); if (!isValid) { XException.NotAllowed.Throw(); } // // Preparing Chat History ... IList chatHistory = null; // // Check if First Question ... if (isFirstConversation) { // // Add Introduction Prompt ... chatHistory.Add( new ChatMessage( content: Configuration.Introduction, role: XAiChatRole.System.ToChatRole() ) ); // // Add Project Prompt, if not Default Project ... if (!isDefaultProject) { // chatHistory.Add( new ChatMessage( content: project.Prompt, role: XAiChatRole.System.ToChatRole() ) ); } } else { // // Complete Chat History ... // TODO: Complete this ... } // // Prepare Prompt ... prompt = await PreparePrompt( prompt: prompt, userInfo: userInfo, projectId: projectId, cancellationToken: cancellationToken ); if (chatHistory.IsNull()) { chatHistory = new List(); } chatHistory.Add( new ChatMessage( content: prompt, role: XAiChatRole.User.ToChatRole() ) ); // // Getting Response Message from LLM ... var chatResult = await chatClient .GetResponseAsync( options: chatOptions, messages: chatHistory, cancellationToken: cancellationToken ); // // TODO: Handle Here ... // throw new NotImplementedException(); } // #region Private ... /// /// Access Person class based on OwnerId ... /// /// /// /// /// private async Task GetPerson( string ownerId, CancellationToken cancellationToken = default ) { // XPersonDto result = null; // if (!ownerId.IsNullOrEmpty()) { // var device = identityProvider.GetDevice(); var userInfo = await identityProvider.GetUserInfo( device: device, userSelectByParam: ownerId ); if (userInfo.IsNullOrDefault()) { XException.NotFound.Throw(); } // result = userInfo.ToXPersonDto(); } // return result; } /// /// Retrieve Specified Project or Default ... /// /// /// /// /// private async Task GetProject( string projectId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Validate UserInfo ... var isValid = !userInfo.IsNullOrDefault() && !userInfo.UserId.IsNullOrEmpty(); if (!isValid) { XException.NotAllowed.Throw(); } // // Check UserInfo ... var personDto = await GetPerson( ownerId: userInfo.UserId, cancellationToken: cancellationToken ); isValid = !personDto.IsNullOrDefault(); if (!isValid) { XException.NotFound.Throw(); } // XAiProject project = null; // // Check Default Project is Exists or not ... var defaultProject = await aiProjectRepository.FindOneAsync( containsDetail: true, ignoreSoftDeleteds: true, whereClause: x => x.OwnerId == userInfo.UserId && x.Title == XAiProjectEnum.Default.GetStringValue() ); var isExists = !defaultProject.IsNullOrDefault(); if (!isExists) { // // Create Default Project ... defaultProject = new XAiProject { Prompt = string.Empty, OwnerId = userInfo.UserId, Description = string.Empty, CreatedOn = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, Title = XAiProjectEnum.Default.GetStringValue(), }; // defaultProject = await aiProjectRepository.AddAsync( saveChanges: true, item: defaultProject ); // isExists = !defaultProject.IsNullOrDefault(); } // // Validate Project ID ... if (projectId.IsNullOrEmpty()) { // // Default Project ... project = defaultProject; projectId = defaultProject.Id.ToString(); } else { // // Special Project ... project = await aiProjectRepository.FindOneAsync( containsDetail: false, ignoreSoftDeleteds: true, whereClause: x => x.Id.ToString() == projectId && x.OwnerId == userInfo.UserId ); isExists = !project.IsNullOrDefault(); if (!isExists) { // // If Not Exists Use Default ... project = defaultProject; } } // // Converts to Dto ... var result = await ToDto( entity: project, cancellationToken: cancellationToken ); // return result; } /// /// Retrieve Conversation for Asking ... /// /// /// /// /// /// private async Task GetConversation( string projectId = null, string conversationId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Validate User Info ... var isValid = await HasPermission( userInfo: userInfo, projectId: projectId, cancellationToken: cancellationToken ); if (!isValid) { XException.NotAllowed.Throw(); } // var project = await GetProject( userInfo: userInfo, projectId: projectId, cancellationToken: cancellationToken ); isValid = !project.IsNullOrDefault(); if (!isValid) { XException.NotFound.Throw(); } // // Check Conversation Exists ... var result = project.Conversations .FirstOrDefault(x => x.OwnerId == userInfo.UserId && x.Id.ToString() == conversationId ); var isExists = !result.IsNullOrDefault() && !conversationId.IsNullOrEmpty(); if (!isExists) { // var title = $"Conversation {Guid.NewGuid()}"; var conversationEntity = new XAiConversation { Title = title, ProjectId = project.Id, OwnerId = userInfo.UserId, CreatedOn = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }; // conversationEntity = await aiConversationRepository.AddAsync( saveChanges: true, item: conversationEntity ); isExists = !conversationEntity.IsNullOrDefault(); if (!isExists) { XException.NotFound.Throw(); } // result = await ToDto( entity: conversationEntity, cancellationToken: cancellationToken ); } // return result; } #endregion } }