diff --git a/Extensions/XAiModelsExtensions.cs b/Extensions/XAiModelsExtensions.cs
new file mode 100644
index 0000000..73d36d1
--- /dev/null
+++ b/Extensions/XAiModelsExtensions.cs
@@ -0,0 +1,108 @@
+using xAiModels.Models.Dtos;
+using xAiModels.Models.Entities;
+using xCommons.Extensions;
+
+namespace xAiModels.Extensions
+{
+ ///
+ /// Extensions Methods for XAiModels ...
+ ///
+ public static class XAiModelsExtensions
+ {
+ ///
+ /// Validate a Project for Add Or Update ...
+ ///
+ ///
+ ///
+ public static bool IsValidForAddOrUpdate(this XAiProject source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ !source.Title.IsNullOrEmpty() &&
+ !source.Prompt.IsNullOrEmpty();
+
+ //
+ return result;
+ }
+ public static bool IsValidForAddOrUpdate(this XAiProjectDto source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ !source.Title.IsNullOrEmpty() &&
+ !source.Prompt.IsNullOrEmpty();
+
+ //
+ return result;
+ }
+
+ ///
+ /// Validate a Conversation for Add Or Update ...
+ ///
+ ///
+ ///
+ public static bool IsValidForAddOrUpdate(this XAiConversation source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ !source.Title.IsNullOrEmpty() &&
+ !source.ProjectId.IsNull() &&
+ !source.ProjectId.IsDefaultGuid();
+
+ //
+ return result;
+ }
+ public static bool IsValidForAddOrUpdate(this XAiConversationDto source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ !source.Title.IsNullOrEmpty() &&
+ !source.ProjectId.IsNull() &&
+ !source.ProjectId.IsDefaultGuid();
+
+ //
+ return result;
+ }
+
+ ///
+ /// Validate a Message for Add Or Update ...
+ ///
+ ///
+ ///
+ public static bool IsValidForAddOrUpdate(this XAiMessage source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ source.Role.IsValid() &&
+ !source.ConversationId.IsNull() &&
+ !source.ConversationId.IsDefaultGuid() &&
+ !source.Content.IsNullOrEmpty();
+
+ //
+ return result;
+ }
+ public static bool IsValidForAddOrUpdate(this XAiMessageDto source)
+ {
+ //
+ var result =
+ !source.IsNullOrDefault() &&
+ !source.OwnerId.IsNullOrEmpty() &&
+ source.Role.IsValid() &&
+ !source.ConversationId.IsNull() &&
+ !source.ConversationId.IsDefaultGuid() &&
+ !source.Content.IsNullOrEmpty();
+
+ //
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Interfaces/IXAiDataProvider.cs b/Interfaces/IXAiDataProvider.cs
index 38ffced..c305df5 100644
--- a/Interfaces/IXAiDataProvider.cs
+++ b/Interfaces/IXAiDataProvider.cs
@@ -1,3 +1,9 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using xAiModels.Models.Dtos;
+
namespace xAiModels.Interfaces
{
///
@@ -5,6 +11,94 @@ namespace xAiModels.Interfaces
///
public interface IXAiDataProvider
{
-
+ //
+ #region Loaders ...
+ ///
+ /// Load Projects List of Specified Owner ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> LoadProjects(
+ string ownerId,
+ bool fillConversations = false,
+ bool fillConversationMessages = false,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Load List of Conversations for Specified Project ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> LoadConversations(
+ Guid projectId,
+ bool fillMessages = false,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Load Messages of Specified Conversation ...
+ ///
+ ///
+ ///
+ ///
+ Task> LoadMessages(
+ Guid conversationId,
+ CancellationToken cancellationToken = default
+ );
+ #endregion
+
+ //
+ #region Creator/Adders ...
+ ///
+ /// Create Project ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task CreateProject(
+ string ownerId,
+ XAiProjectDto item = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Create Conversation ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task CreateConversation(
+ Guid projectId,
+ XAiConversationDto item = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Add Message ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task AddMessage(
+ Guid conversationId,
+ XAiMessageDto item,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+ #endregion
}
}
\ No newline at end of file
diff --git a/Providers/XAiDataProvider.cs b/Providers/XAiDataProvider.cs
index 149f39a..49cf690 100644
--- a/Providers/XAiDataProvider.cs
+++ b/Providers/XAiDataProvider.cs
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
+using MongoDB.Driver.Core.Connections;
using xAiModels.Constants;
+using xAiModels.Extensions;
using xAiModels.Interfaces;
using xAiModels.Interfaces.Dtos;
using xAiModels.Models.Dtos;
@@ -21,6 +24,8 @@ namespace xAiModels.Providers
///
public class XAiDataProvider : IXAiDataProvider
{
+ //
+ #region Properties ...
private readonly IXStringProvider stringProvider;
private readonly XAppConfiguration appConfiguration;
private readonly IHubContext hub;
@@ -30,6 +35,7 @@ namespace xAiModels.Providers
private readonly IXAiConversationRepositoryService conversationRepositoryService;
private readonly IXAiConversationTitleResourceProvider conversationTitleResourceProvider;
private readonly IXAiProjectDescriptionResourceProvider projectDescriptionResourceProvider;
+ #endregion
public XAiDataProvider(
IXStringProvider stringProvider,
@@ -55,7 +61,415 @@ namespace xAiModels.Providers
}
//
- #region Actions ...
+ #region Loaders ...
+ ///
+ /// Load Projects List of Specified Owner ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> LoadProjects(
+ string ownerId,
+ bool fillConversations = false,
+ bool fillConversationMessages = false,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (ownerId.IsNullOrEmpty())
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Handle Default Project and Conversation ...
+ bool hasDefault = await HasDefaultProject(
+ ownerId: ownerId,
+ cancellationToken: cancellationToken
+ );
+ if (!hasDefault)
+ {
+ //
+ await CreateDefaultProject(
+ ownerId: ownerId,
+ connectionId: null,
+ cancellationToken: cancellationToken
+ );
+ }
+
+ //
+ // Retrieve Project List ...
+ var result = await projectRepositoryService.FindManyAsync(
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ fillConversations: fillConversations,
+ predicate: x => x.OwnerId == ownerId,
+ cancellationToken: cancellationToken,
+ orderBuilder: x => x.OrderBy(y => y.CreatedOn),
+ fillConversationMessages: fillConversationMessages
+ );
+
+ //
+ return result;
+ }
+
+ ///
+ /// Load List of Conversations for Specified Project ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> LoadConversations(
+ Guid projectId,
+ bool fillMessages = false,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (projectId.IsNull() ||
+ projectId.IsDefaultGuid()
+ )
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Retrieve Conversation's List ...
+ var result = await conversationRepositoryService.FindManyAsync(
+ includeBuilder: null,
+ ignoreSoftDeleteds: false,
+ fillMessages: fillMessages,
+ cancellationToken: cancellationToken,
+ predicate: x => x.ProjectId == projectId,
+ orderBuilder: x => x.OrderBy(y => y.CreatedOn)
+ );
+
+ //
+ return result;
+ }
+
+ ///
+ /// Load Messages of Specified Conversation ...
+ ///
+ ///
+ ///
+ ///
+ public async Task> LoadMessages(
+ Guid conversationId,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (conversationId.IsNull() ||
+ conversationId.IsDefaultGuid()
+ )
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Retrieve Message Lists of Specified Conversation ...
+ var result = await messageRepositoryService.FindManyAsync(
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ cancellationToken: cancellationToken,
+ orderBuilder: x => x.OrderBy(y => y.CreatedOn),
+ predicate: x => x.ConversationId == conversationId
+ );
+
+ //
+ return result;
+ }
+ #endregion
+
+ //
+ #region Creator/Adders ...
+ public async Task CreateProject(
+ string ownerId,
+ XAiProjectDto item = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (ownerId.IsNullOrEmpty())
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Check Project Provided or not ...
+ var hasProject = !item.IsNullOrDefault();
+
+ //
+ // Normalize Provided Project ...
+ if (hasProject)
+ {
+ //
+ item.CreatedOn = DateTime.UtcNow;
+
+ //
+ // Validate Item ...
+ if (!item.IsValidForAddOrUpdate())
+ {
+ XException.InvalidData.Throw();
+ }
+ }
+
+ //
+ XAiProjectDto result = null;
+
+ //
+ // Create Provided Project ...
+ if (hasProject)
+ {
+ //
+ result = await projectRepositoryService.AddAsync(
+ item: item,
+ saveChanges: true,
+ fillConversations: false,
+ fillConversationMessages: false,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ // Handling Send Push Notification ...
+ if (!result.IsNullOrDefault() &&
+ !connectionId.IsNullOrEmpty()
+ )
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XAiDataProviderHubActions.ProjectCreated.GetStringValue()
+ );
+ }
+ }
+ //
+ // Check Default Project ...
+ else
+ {
+ //
+ // Check Default Project Exists or not ...
+ var hasDefault = await HasDefaultProject(
+ ownerId: ownerId,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ // If Default Project Exists, Throw Exception ...
+ if (hasDefault)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Create and Retrive Default Project ...
+ result = await GetDefaultProject(
+ ownerId: ownerId,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+ }
+
+ //
+ // Validate ...
+ if (result.IsNullOrDefault())
+ {
+ XException.ActionFailed.Throw();
+ }
+
+ //
+ return result;
+ }
+
+ public async Task CreateConversation(
+ Guid projectId,
+ XAiConversationDto item = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (projectId.IsNull() ||
+ projectId.IsDefaultGuid())
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Handle Proejct ...
+ XAiProjectDto project = await projectRepositoryService.GetAsync(
+ id: projectId,
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ fillConversations: false,
+ fillConversationMessages: false,
+ cancellationToken: cancellationToken
+ );
+ if (project.IsNullOrDefault())
+ {
+ XException.NotFound.Throw();
+ }
+
+ //
+ // Check Conversation Provided or not ...
+ var hasConversation = !item.IsNullOrDefault();
+
+ //
+ // Normalize Conversation if Provided ...
+ if (hasConversation)
+ {
+ //
+ item.ProjectId = project.Id;
+ item.CreatedOn = DateTime.UtcNow;
+
+ //
+ // Validate Item ...
+ if (!item.IsValidForAddOrUpdate())
+ {
+ XException.InvalidData.Throw();
+ }
+ }
+
+ //
+ XAiConversationDto result = null;
+
+ //
+ // Create Provided Conversation ...
+ if (hasConversation)
+ {
+ //
+ result = await conversationRepositoryService.AddAsync(
+ item: item,
+ saveChanges: true,
+ fillMessages: false,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ // Handling Send Push Notification ...
+ if (!result.IsNullOrDefault() &&
+ !connectionId.IsNullOrEmpty()
+ )
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XAiDataProviderHubActions.ConversationCreated.GetStringValue()
+ );
+ }
+ }
+ //
+ // Check Default Conversation ...
+ else
+ {
+ //
+ // Check Default Conversation Exists or not ...
+ var hasDefault = await HasDefaultConversation(
+ projectId: project.Id,
+ ownerId: project.OwnerId,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ // If Default Project Exists, Throw Exception ...
+ if (hasDefault)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Create and Retrive Default Conversation ...
+ result = await GetDefaultConversation(
+ projectId: project.Id,
+ ownerId: project.OwnerId,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+ }
+
+ //
+ // Validate ...
+ if (result.IsNullOrDefault())
+ {
+ XException.ActionFailed.Throw();
+ }
+
+ //
+ return result;
+ }
+
+ public async Task AddMessage(
+ Guid conversationId,
+ XAiMessageDto item,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate Args ...
+ if (item.IsNullOrDefault() ||
+ conversationId.IsNull() ||
+ conversationId.IsDefaultGuid()
+ )
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Normalize ...
+ item.CreatedOn = DateTime.UtcNow;
+ item.ConversationId = conversationId;
+
+ //
+ // Validate Item ...
+ if (!item.IsValidForAddOrUpdate())
+ {
+ XException.InvalidData.Throw();
+ }
+
+ //
+ var result = await messageRepositoryService.AddAsync(
+ item: item,
+ saveChanges: true,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ // Handling Send Push Notification ...
+ if (!result.IsNullOrDefault() &&
+ !connectionId.IsNullOrEmpty()
+ )
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XAiDataProviderHubActions.MessageCreated.GetStringValue()
+ );
+ }
+
+ //
+ return result;
+ }
+ #endregion
+
+ //
+ #region Updaters ...
#endregion
//
@@ -64,10 +478,12 @@ namespace xAiModels.Providers
/// Check Default Project Exists or not ...
///
///
+ ///
///
///
private async Task HasDefaultProject(
string ownerId,
+ string connectionId = null,
CancellationToken cancellationToken = default
)
{
@@ -117,6 +533,7 @@ namespace xAiModels.Providers
// Check Default Project Exists or noit ...
var isExists = await HasDefaultProject(
ownerId: ownerId,
+ connectionId: connectionId,
cancellationToken: cancellationToken
);
if (!isExists)
@@ -273,10 +690,14 @@ namespace xAiModels.Providers
/// Check Default Conversation Exists or not ...
///
///
+ ///
+ ///
///
///
private async Task HasDefaultConversation(
string ownerId,
+ Guid projectId,
+ string connectionId = null,
CancellationToken cancellationToken = default
)
{
@@ -288,9 +709,40 @@ namespace xAiModels.Providers
return result;
}
+ //
+ // Prepare Project ...
+ XAiProjectDto project = null;
+ if (projectId.IsNull() ||
+ projectId.IsDefaultGuid()
+ )
+ {
+ //
+ project = await GetDefaultProject(
+ ownerId: ownerId,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+ }
+ else
+ {
+ //
+ project = await projectRepositoryService.GetAsync(
+ id: project.Id,
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ fillConversations: false,
+ fillConversationMessages: false,
+ cancellationToken: cancellationToken
+ );
+ }
+ if (project.IsNullOrDefault())
+ {
+ XException.NotFound.Throw();
+ }
+
//
// Check Default Owner Exists or not ...
- result = !(await stringProvider.FindOne(
+ var resource = await stringProvider.FindOne(
cancellationToken: cancellationToken,
predicate: x =>
x.ResourceTitle
@@ -303,8 +755,23 @@ namespace xAiModels.Providers
x.TranslatedValue
.ToNormalString()
.Contains(XAiDataProviderConstants.XDefaultAiConversationSuffix)
+ );
+ result = !resource.IsNullOrDefault();
+ if (!result)
+ {
+ return result;
+ }
+
+ //
+ result = !(await conversationRepositoryService.FindOneAsync(
+ cancellationToken: cancellationToken,
+ predicate: x =>
+ x.ProjectId == projectId &&
+ x.Title == resource.ResourceTitle
))
.IsNullOrDefault();
+
+ //
return result;
}
@@ -312,11 +779,13 @@ namespace xAiModels.Providers
/// Check Default Conversation Exists or not ...
///
///
+ ///
///
///
///
private async Task CreateDefaultConversation(
string ownerId,
+ Guid projectId,
string connectionId = null,
CancellationToken cancellationToken = default
)
@@ -333,17 +802,41 @@ namespace xAiModels.Providers
var defaultLang = appConfiguration.DefaultLanguage;
//
- // Handle Default Project ...
- var project = await GetDefaultProject(
- ownerId: ownerId,
- connectionId: connectionId,
- cancellationToken: cancellationToken
- );
+ // Prepare Project ...
+ XAiProjectDto project = null;
+ if (projectId.IsNull() ||
+ projectId.IsDefaultGuid()
+ )
+ {
+ //
+ project = await GetDefaultProject(
+ ownerId: ownerId,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+ }
+ else
+ {
+ //
+ project = await projectRepositoryService.GetAsync(
+ id: project.Id,
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ fillConversations: false,
+ fillConversationMessages: false,
+ cancellationToken: cancellationToken
+ );
+ }
+ if (project.IsNullOrDefault())
+ {
+ XException.NotFound.Throw();
+ }
//
// Check Default Owner Exists or not ...
isValid = await HasDefaultConversation(
ownerId: ownerId,
+ projectId: project.Id,
cancellationToken: cancellationToken
);
if (isValid)
@@ -412,19 +905,61 @@ namespace xAiModels.Providers
/// Create it if not Handled ...
///
///
+ ///
///
///
///
private async Task GetDefaultConversation(
string ownerId,
+ Guid projectId,
string connectionId = null,
CancellationToken cancellationToken = default
)
{
+ //
+ // Validate Args ...
+ if (ownerId.IsNullOrEmpty())
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Prepare Project ...
+ XAiProjectDto project = null;
+ if (projectId.IsNull() ||
+ projectId.IsDefaultGuid()
+ )
+ {
+ //
+ project = await GetDefaultProject(
+ ownerId: ownerId,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+ }
+ else
+ {
+ //
+ project = await projectRepositoryService.GetAsync(
+ id: project.Id,
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ fillConversations: false,
+ fillConversationMessages: false,
+ cancellationToken: cancellationToken
+ );
+ }
+ if (project.IsNullOrDefault())
+ {
+ XException.NotFound.Throw();
+ }
+
//
// Check Default Conversation Exists or noit ...
var isExists = await HasDefaultConversation(
ownerId: ownerId,
+ projectId: project.Id,
+ connectionId: connectionId,
cancellationToken: cancellationToken
);
if (!isExists)
@@ -432,12 +967,14 @@ namespace xAiModels.Providers
//
await CreateDefaultConversation(
ownerId: ownerId,
+ projectId: project.Id,
connectionId: connectionId,
cancellationToken: cancellationToken
);
}
//
+ // Detect Conversation Resource ...
var resource = await stringProvider.FindOne(
cancellationToken: cancellationToken,
predicate: x =>
@@ -458,13 +995,15 @@ namespace xAiModels.Providers
}
//
+ // Find Default Conversation ...
var result = await conversationRepositoryService.FindOneAsync(
cancellationToken: cancellationToken,
predicate: x => x.Title
.Equals(
resource.ResourceTitle,
StringComparison.InvariantCultureIgnoreCase
- )
+ ) &&
+ x.ProjectId == project.Id
);
if (result.IsNullOrDefault())
{
@@ -475,6 +1014,29 @@ namespace xAiModels.Providers
return result;
}
+ ///
+ /// Normalize and Validate Language ...
+ ///
+ ///
+ ///
+ private string NormalizeAndValidateLanguage(string language)
+ {
+ //
+ if (language.IsNullOrEmpty())
+ {
+ language = appConfiguration.DefaultLanguage;
+ }
+
+ //
+ if (language.IsNullOrEmpty())
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ return language;
+ }
+
///
/// Send Custom Push Message ...
///