diff --git a/AI/Configuration/Entities/XAiMessageConfiguration.cs b/AI/Configuration/Entities/XAiMessageConfiguration.cs index 7a36f7b..1e1ff9a 100644 --- a/AI/Configuration/Entities/XAiMessageConfiguration.cs +++ b/AI/Configuration/Entities/XAiMessageConfiguration.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using xAiApi.AI.Models.Entities; @@ -8,6 +7,13 @@ namespace xAiApi.AI.Configuration.Entities public class XAiMessageConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) - { } + { + // + builder + .HasOne(x => x.Conversation) + .WithMany(x => x.Messages) + .HasForeignKey(x => x.ConversationId) + .OnDelete(DeleteBehavior.NoAction); + } } } \ No newline at end of file diff --git a/AI/Extensions/AiModelsExtensions.cs b/AI/Extensions/AiModelsExtensions.cs index f5da4e0..b62d4e4 100644 --- a/AI/Extensions/AiModelsExtensions.cs +++ b/AI/Extensions/AiModelsExtensions.cs @@ -1,21 +1,100 @@ using xCommons.Extensions; using System.Collections.Generic; -using Microsoft.Extensions.AI; -using System.Linq; +using xAiApi.AI.Models.Entities; +using xAiModels.Models; namespace xAiApi.AI.Extensions { + /// + /// Extensions which used for AiModels ... + /// public static class AiModelsExtensions { - public static AIContent GetLast(IList source) + /// + /// Converts Entity to Dto regardsless of Owner and NavigationProperties ... + /// + /// + /// + public static XAiMessageDto ToDto(this XAiMessage source) { // - AIContent result = null; + XAiMessageDto result = null; // - if (source.HasChild()) + if (!source.IsNullOrDefault()) { - result = source.ElementAt(source.Count - 1); + // + result = new XAiMessageDto(); + result = result.UpdateData( + updateWith: source, + propertyBlackList: new List + { + nameof(XAiMessage.Deleted), + nameof(XAiMessageDto.Owner), + nameof(XAiMessage.Conversation), + } + ); + } + + // + return result; + } + + /// + /// Converts Entity to Dto regardsless of Owner and NavigationProperties ... + /// + /// + /// + public static XAiProjectDto ToDto(this XAiProject source) + { + // + XAiProjectDto result = null; + + // + if (!source.IsNullOrDefault()) + { + // + result = new XAiProjectDto(); + result = result.UpdateData( + updateWith: source, + propertyBlackList: new List + { + nameof(XAiProject.Deleted), + nameof(XAiProjectDto.Owner), + nameof(XAiProject.Conversations), + } + ); + } + + // + return result; + } + + /// + /// Converts Entity to Dto regardsless of Owner and NavigationProperties ... + /// + /// + /// + public static XAiConversationDto ToDto(this XAiConversation source) + { + // + XAiConversationDto result = null; + + // + if (!source.IsNullOrDefault()) + { + // + result = new XAiConversationDto(); + result = result.UpdateData( + updateWith: source, + propertyBlackList: new List + { + nameof(XAiConversation.Deleted), + nameof(XAiConversation.Project), + nameof(XAiConversationDto.Owner), + nameof(XAiConversation.Messages), + } + ); } // diff --git a/AI/Extensions/ChatRoleExtensions.cs b/AI/Extensions/ChatRoleExtensions.cs deleted file mode 100644 index bfd3b54..0000000 --- a/AI/Extensions/ChatRoleExtensions.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Microsoft.Extensions.AI; -using xAiApi.AI.Constants; -using xCommons.Extensions; - -namespace xAiApi.AI.Extensions -{ - /// - /// Extensions for ChatRole ... - /// - public static class ChatRoleExtensions - { - /// - /// Normalize an String to Propper Chat Role Value ... - /// > - /// - /// - /// - public static string GetChatRoleTitle(this string source) - { - // - var result = XAiChatRoles.None; - - // - if (source.ToNormalString() == XAiChatRoles.None.ToNormalString()) - { - result = XAiChatRoles.None; - } - else if (source.ToNormalString() == XAiChatRoles.User.ToNormalString()) - { - result = XAiChatRoles.User; - } - else if (source.ToNormalString() == XAiChatRoles.Tool.ToNormalString()) - { - result = XAiChatRoles.Tool; - } - else if (source.ToNormalString() == XAiChatRoles.System.ToNormalString()) - { - result = XAiChatRoles.System; - } - else if (source.ToNormalString() == XAiChatRoles.Assistant.ToNormalString()) - { - result = XAiChatRoles.Assistant; - } - - // - return result; - } - - /// - /// Get Chat Role Title from Chat Role Class ... - /// - /// - /// - /// - public static string GetRole(this ChatRole source) - { - // - var result = source.Value - .GetChatRoleTitle(); - - // - return result; - } - - /// - /// Generate Chat Role Class Based on role Title ... - /// - /// - /// - public static ChatRole ToChatRole(this string source) - { - // - var role = source.GetChatRoleTitle(); - ChatRole result = new ChatRole(role); - - // - return result; - } - - /// - /// Extract Chat Role Enum from Chat Role ... - /// - /// - /// - /// - /// - public static XAiChatRole ToChatRole(this ChatRole source) - { - // - var role = source - .GetRole() - .GetChatRoleTitle(); - - // - XAiChatRole result = XAiChatRole.User; - - // - if (role == XAiChatRoles.User) - { - result = XAiChatRole.User; - } - else if (role == XAiChatRoles.Tool) - { - result = XAiChatRole.Tool; - } - else if (role == XAiChatRoles.System) - { - result = XAiChatRole.System; - } - else if (role == XAiChatRoles.Assistant) - { - result = XAiChatRole.Assistant; - } - - // - return result; - } - - /// - /// Validate an String as Chat Role ... - /// - /// - /// - public static bool IsValid(this string source) - { - // - var result = - source != XAiChatRoles.None && - source.ToNormalString() != XAiChatRoles.None.ToNormalString(); - - // - return result; - } - - /// - /// Validate Role ... - /// > - /// - /// - /// - public static bool IsValid(this XAiChatRole source) - { - // - var result = source != XAiChatRole.None; - return result; - } - - /// - /// Validate Role ... - /// > - /// - /// - /// - public static bool IsValid(this ChatRole source) - { - // - var role = source.ToChatRole(); - var result = role.IsValid(); - return result; - } - } -} \ No newline at end of file diff --git a/AI/Interfaces/IXAiProvider.cs b/AI/Interfaces/IXAiProvider.cs index d54e0b6..91f658c 100644 --- a/AI/Interfaces/IXAiProvider.cs +++ b/AI/Interfaces/IXAiProvider.cs @@ -1,10 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using xAiApi.AI.Models.Entities; +using xAiModels.Models; +using xIdentityModels.Models; +using xModels.Dtos; + namespace xAiApi.AI.Interfaces { /// /// Provide Ai Capabilities Using AI Service ... + /// using User Data Info ... /// public interface IXAiProvider { + /// + /// Ai Service for Getting Features ... + /// + IXAIService AiService { get; } + // + #region Helpers ... + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + Task ToDto(XAiProject entity); + + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + Task ToDto(XAiMessage entity); + + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + Task ToDto(XAiConversation entity); + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtos(IEnumerable entities); + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtos(IEnumerable entities); + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtos(IEnumerable entities); + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtoQueryResult(XQueryResult queryResult); + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtoQueryResult(XQueryResult queryResult); + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + Task> ToDtoQueryResult(XQueryResult queryResult); + + /// + /// Check Permission for Ai Actions ... + /// + /// + /// + Task HasPermission( + XUserClaimsInfoDto userInfo + ); + + /// + /// Prepare Prompt ... + /// + /// + /// + /// + Task PreparePrompt( + string prompt, + XUserClaimsInfoDto userInfo + ); + #endregion + + /// + /// Ask a Question ... + /// + /// + /// + /// + /// + Task Ask( + string prompt, + Guid conversationId, + XUserClaimsInfoDto userInfo + ); } } \ No newline at end of file diff --git a/AI/Models/Entities/XAiConversation.cs b/AI/Models/Entities/XAiConversation.cs index 7490041..b695d8c 100644 --- a/AI/Models/Entities/XAiConversation.cs +++ b/AI/Models/Entities/XAiConversation.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using xModels.Base; namespace xAiApi.AI.Models.Entities @@ -9,9 +10,19 @@ namespace xAiApi.AI.Models.Entities /// public class XAiConversation : XBaseGuidIDEntity { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + /// /// Title of Conversation ... /// + [Required] + [StringLength(255)] public string Title { get; set; } /// @@ -32,6 +43,7 @@ namespace xAiApi.AI.Models.Entities /// /// Project ID ... /// + [Required] public Guid ProjectId { get; set; } /// diff --git a/AI/Models/Entities/XAiMessage.cs b/AI/Models/Entities/XAiMessage.cs index 581189c..a0a5341 100644 --- a/AI/Models/Entities/XAiMessage.cs +++ b/AI/Models/Entities/XAiMessage.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema; using xModels.Base; using xCommons.Extensions; using xAiModels.Constants; +using System.ComponentModel.DataAnnotations; namespace xAiApi.AI.Models.Entities { @@ -12,9 +13,18 @@ namespace xAiApi.AI.Models.Entities /// public class XAiMessage : XBaseGuidIDEntity { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + /// /// Message of Chat ... /// + [Required] public string Content { get; set; } /// @@ -35,6 +45,7 @@ namespace xAiApi.AI.Models.Entities /// /// Conversation Id ... /// + [StringLength(255)] public Guid ConversationId { get; set; } /// diff --git a/AI/Models/Entities/XAiProject.cs b/AI/Models/Entities/XAiProject.cs index 23b5208..c73fd8e 100644 --- a/AI/Models/Entities/XAiProject.cs +++ b/AI/Models/Entities/XAiProject.cs @@ -1,19 +1,31 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using xModels.Base; namespace xAiApi.AI.Models.Entities { public class XAiProject : XBaseGuidIDEntity { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + /// /// Title of Project ... /// + [Required] + [StringLength(255)] public string Title { get; set; } /// /// Description of Project ... /// + [StringLength(1000)] public string Description { get; set; } /// diff --git a/AI/Services/XAiProvider.cs b/AI/Services/XAiProvider.cs new file mode 100644 index 0000000..3f1e6a5 --- /dev/null +++ b/AI/Services/XAiProvider.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xAiApi.AI.Extensions; +using xAiApi.AI.Interfaces; +using xAiApi.AI.Models.Entities; +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 +{ + public class XAiProvider : IXAiProvider + { + // + public IXAIService AiService { get; } + private readonly IXIdentityProvider identityProvider; + + public XAiProvider( + IXAIService aiService, + IXIdentityProvider identityProvider + ) + { + // + AiService = aiService; + this.identityProvider = identityProvider; + } + + // + #region Helpers ... + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + public async Task ToDto(XAiProject entity) + { + // + XAiProjectDto result = null; + + // + if (!entity.IsNullOrDefault()) + { + // + result = entity.ToDto(); + if (!entity.OwnerId.IsNullOrEmpty()) + { + result.Owner = await GetPerson(entity.OwnerId); + } + + // + result.Conversations = await ToDtos(entity.Conversations); + } + + // + return result; + } + + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + public async Task ToDto(XAiMessage entity) + { + // + XAiMessageDto result = null; + + // + if (!entity.IsNullOrDefault()) + { + // + result = entity.ToDto(); + if (!entity.OwnerId.IsNullOrEmpty()) + { + result.Owner = await GetPerson(entity.OwnerId); + } + + // + result.Conversation = await ToDto(entity.Conversation); + } + + // + return result; + } + + /// + /// Converts Entity to Dto ... + /// + /// + /// + /// + public async Task ToDto(XAiConversation entity) + { + // + XAiConversationDto result = null; + + // + if (!entity.IsNullOrDefault()) + { + // + result = entity.ToDto(); + if (!entity.OwnerId.IsNullOrEmpty()) + { + result.Owner = await GetPerson(entity.OwnerId); + } + + // + result.Project = await ToDto(entity.Project); + result.Messages = await ToDtos(entity.Messages); + } + + // + return result; + } + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtos(IEnumerable entities) + { + // + IList list = new List(); + + // + var enumerable = entities.ToAsyncEnumerable(); + await foreach (var entity in enumerable) + { + // + var dto = await ToDto(entity); + if (!dto.IsNullOrDefault()) + { + list.Add(dto); + } + } + + // + return list.AsEnumerable(); + } + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtos(IEnumerable entities) + { + // + IList list = new List(); + + // + var enumerable = entities.ToAsyncEnumerable(); + await foreach (var entity in enumerable) + { + // + var dto = await ToDto(entity); + if (!dto.IsNullOrDefault()) + { + list.Add(dto); + } + } + + // + return list.AsEnumerable(); + } + + /// + /// Converts a List of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtos(IEnumerable entities) + { + // + IList list = new List(); + + // + var enumerable = entities.ToAsyncEnumerable(); + await foreach (var entity in enumerable) + { + // + var dto = await ToDto(entity); + if (!dto.IsNullOrDefault()) + { + list.Add(dto); + } + } + + // + return list.AsEnumerable(); + } + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtoQueryResult(XQueryResult queryResult) + { + // + 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); + } + } + + // + return result; + } + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtoQueryResult(XQueryResult queryResult) + { + // + 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); + } + } + + // + return result; + } + + /// + /// Converts a QueryResult of Entities to Dtos ... + /// + /// + /// + /// + public async Task> ToDtoQueryResult(XQueryResult queryResult) + { + // + 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); + } + } + + // + return result; + } + + /// + /// Check Permission for Ai Actions ... + /// + /// + /// + public async Task HasPermission(XUserClaimsInfoDto userInfo) + { + // + var result = true; + + // + // TODO: Complete this ... + + // + return await Task.FromResult(result); + } + + /// + /// Prepare Prompt ... + /// + /// + /// + /// + public async Task PreparePrompt(string prompt, XUserClaimsInfoDto userInfo) + { + // + var result = prompt; + + // + // TODO: Complete this ... + + // + return await Task.FromResult(result); + } + #endregion + + /// + /// Ask a Question ... + /// + /// + /// + /// + /// + public Task Ask( + string prompt, + Guid conversationId, + XUserClaimsInfoDto userInfo + ) + { + throw new NotImplementedException(); + } + + // + #region Private ... + /// + /// Access Person class based on OwnerId ... + /// + /// + /// + /// + private async Task GetPerson(string ownerId) + { + // + 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; + } + #endregion + } +} \ No newline at end of file diff --git a/xAiApi.csproj b/xAiApi.csproj index e10ea0e..0efa366 100644 --- a/xAiApi.csproj +++ b/xAiApi.csproj @@ -32,7 +32,6 @@ -