From 437c1e01838651e26138de5f3c6f08f7157d08a2 Mon Sep 17 00:00:00 2001
From: arthuridea w <2337583+arthuridea@users.noreply.github.com>
Date: Tue, 5 Mar 2024 16:54:16 +0800
Subject: [PATCH] feat:add db persistance.(on development).
---
NetCore.AIGC.sln | 14 ++
.../ChatDbContextFactory.cs | 81 ++++++
.../LLMService.DataProvider.Migrator.csproj | 32 +++
.../README.md | 52 ++++
.../appsettings.json | 9 +
.../Data/ChatDbContext.cs | 230 ++++++++++++++++++
.../Data/DbExtensions.cs | 139 +++++++++++
.../Entity/ChatFolder.cs | 72 ++++++
.../Entity/ChatMessage.cs | 124 ++++++++++
.../Entity/Conversation.cs | 75 ++++++
.../Entity/EntityConsts.cs | 50 ++++
.../Entity/ICreatedUtcTime.cs | 22 ++
.../LLMService.DataProvider.Relational.csproj | 35 +++
.../Provider/ChatDataRelationalDbProvider.cs | 71 ++++++
.../Provider/ChatStorage.cs | 48 ++++
.../Provider/IChatStorage.cs | 14 ++
16 files changed, 1068 insertions(+)
create mode 100644 src/LLMService.DataProvider.Migrator/ChatDbContextFactory.cs
create mode 100644 src/LLMService.DataProvider.Migrator/LLMService.DataProvider.Migrator.csproj
create mode 100644 src/LLMService.DataProvider.Migrator/README.md
create mode 100644 src/LLMService.DataProvider.Migrator/appsettings.json
create mode 100644 src/LLMService.DataProvider.Relational/Data/ChatDbContext.cs
create mode 100644 src/LLMService.DataProvider.Relational/Data/DbExtensions.cs
create mode 100644 src/LLMService.DataProvider.Relational/Entity/ChatFolder.cs
create mode 100644 src/LLMService.DataProvider.Relational/Entity/ChatMessage.cs
create mode 100644 src/LLMService.DataProvider.Relational/Entity/Conversation.cs
create mode 100644 src/LLMService.DataProvider.Relational/Entity/EntityConsts.cs
create mode 100644 src/LLMService.DataProvider.Relational/Entity/ICreatedUtcTime.cs
create mode 100644 src/LLMService.DataProvider.Relational/LLMService.DataProvider.Relational.csproj
create mode 100644 src/LLMService.DataProvider.Relational/Provider/ChatDataRelationalDbProvider.cs
create mode 100644 src/LLMService.DataProvider.Relational/Provider/ChatStorage.cs
create mode 100644 src/LLMService.DataProvider.Relational/Provider/IChatStorage.cs
diff --git a/NetCore.AIGC.sln b/NetCore.AIGC.sln
index 430b9bc..6e512c3 100644
--- a/NetCore.AIGC.sln
+++ b/NetCore.AIGC.sln
@@ -34,6 +34,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.OpenAI.ChatGPT",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02.Sample", "02.Sample", "{0B000384-47E8-4013-A1CE-59A465017EF2}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.DataProvider.Relational", "src\LLMService.DataProvider.Relational\LLMService.DataProvider.Relational.csproj", "{3B8B2207-56F2-4D98-858F-7BAB3858CD74}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.DataProvider.Migrator", "src\LLMService.DataProvider.Migrator\LLMService.DataProvider.Migrator.csproj", "{E2CC4393-ABF1-400F-A9B2-A56F99A2285E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -60,6 +64,14 @@ Global
{09322116-1405-47CE-BBB5-35E02167905D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09322116-1405-47CE-BBB5-35E02167905D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09322116-1405-47CE-BBB5-35E02167905D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B8B2207-56F2-4D98-858F-7BAB3858CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B8B2207-56F2-4D98-858F-7BAB3858CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B8B2207-56F2-4D98-858F-7BAB3858CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B8B2207-56F2-4D98-858F-7BAB3858CD74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2CC4393-ABF1-400F-A9B2-A56F99A2285E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2CC4393-ABF1-400F-A9B2-A56F99A2285E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2CC4393-ABF1-400F-A9B2-A56F99A2285E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2CC4393-ABF1-400F-A9B2-A56F99A2285E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -70,6 +82,8 @@ Global
{E73CB18F-26D3-4A6C-8AA2-E89FF17B65C5} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16}
{E3AE89E4-31A0-4EDA-9154-2D089E1FD8A4} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16}
{09322116-1405-47CE-BBB5-35E02167905D} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16}
+ {3B8B2207-56F2-4D98-858F-7BAB3858CD74} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16}
+ {E2CC4393-ABF1-400F-A9B2-A56F99A2285E} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4FD3BE39-36D7-4A01-8EEC-39063ED813B4}
diff --git a/src/LLMService.DataProvider.Migrator/ChatDbContextFactory.cs b/src/LLMService.DataProvider.Migrator/ChatDbContextFactory.cs
new file mode 100644
index 0000000..0d15a67
--- /dev/null
+++ b/src/LLMService.DataProvider.Migrator/ChatDbContextFactory.cs
@@ -0,0 +1,81 @@
+using LLMService.DataProvider.Relational.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.Extensions.Configuration;
+using Org.BouncyCastle.Tls;
+
+namespace LLMService.DataProvider.Migrator
+{
+ ///
+ ///
+ ///
+ internal class ChatDbContextFactory : ChatDbContextFactoryGeneric
+ {
+ }
+
+ ///
+ ///
+ ///
+ ///
+ internal class ChatDbContextFactoryGeneric : IDesignTimeDbContextFactory
+ where TContext : DbContext
+ {
+ const string connectionKey = "DbConnection";
+ const string assemblyName = "LLMService.DataProvider.Migrator";
+
+ ///
+ /// Creates a new instance of a derived context.
+ ///
+ /// Arguments provided by the design-time service.
+ ///
+ /// An instance of .
+ ///
+ public TContext CreateDbContext(string[] args)
+ {
+ //
+ var configuration = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json")
+ .AddCommandLine(args)
+ .Build();
+ var dbContextBuilder = new DbContextOptionsBuilder();
+ var connectionString = configuration.GetConnectionString(connectionKey);
+
+ string migrationDbProvider = configuration["DbProvider"] ?? "SqlServer";
+ //string contextTypeName = configuration["contextType"] ?? nameof(TContext);
+ /*
+ * Sample DI code goes here:
+ services.AddDbContext(
+ options => _ = provider switch
+ {
+ "Sqlite" => options.UseSqlite(
+ configuration.GetConnectionString("SqliteConnection"),
+ x => x.MigrationsAssembly("SqliteMigrations")),
+
+ "SqlServer" => options.UseSqlServer(
+ configuration.GetConnectionString("SqlServerConnection"),
+ x => x.MigrationsAssembly("SqlServerMigrations")),
+
+ _ => throw new Exception($"Unsupported provider: {provider}")
+ });
+ *
+ */
+
+ if (migrationDbProvider.Contains("SqlServer", StringComparison.InvariantCultureIgnoreCase)){
+ dbContextBuilder.UseSqlServer(connectionString, action => action.MigrationsAssembly(assemblyName));
+ }
+ else if(migrationDbProvider.Contains("MySql", StringComparison.InvariantCultureIgnoreCase))
+ {
+ dbContextBuilder.UseMySQL(connectionString, action => action.MigrationsAssembly(assemblyName));
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid DbProvider");
+ }
+
+ var dbContextInstance = (TContext)Activator.CreateInstance(typeof(TContext), dbContextBuilder.Options);
+
+ return dbContextInstance;
+ }
+ }
+}
diff --git a/src/LLMService.DataProvider.Migrator/LLMService.DataProvider.Migrator.csproj b/src/LLMService.DataProvider.Migrator/LLMService.DataProvider.Migrator.csproj
new file mode 100644
index 0000000..f815156
--- /dev/null
+++ b/src/LLMService.DataProvider.Migrator/LLMService.DataProvider.Migrator.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net8.0
+ enable
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/LLMService.DataProvider.Migrator/README.md b/src/LLMService.DataProvider.Migrator/README.md
new file mode 100644
index 0000000..ab61898
--- /dev/null
+++ b/src/LLMService.DataProvider.Migrator/README.md
@@ -0,0 +1,52 @@
+# 项目说明
+
+本项目为独立的数据库迁移项目,支持在`appsettings.json`中配置数据库提供程序,目前支持`SqlServer`和`MySql`.
+
+## 目录结构
+
+例如,有3次迁移Migration1,Migration2,Migration3,文件结构大致如下:
+
+```
+.
+├── Migrations /*迁移文件夹*/
+│ ├── 时间戳1_Migration1.cs /*第1次迁移*/
+│ ├── 时间戳2_Migration2.cs /*第2次迁移*/
+│ ├── 时间戳3_Migration3.cs /*第3次迁移*/
+│ ├── ChatDbContextModelSnapshot.cs /*迁移快照文件*/
+│ └── Scripts /*迁移脚本文件夹*/
+│ ├── Migration1.sql /*第1次迁移脚本*/
+│ ├── Migration2.sql /*第2次迁移脚本*/
+│ └── Migration3.sql /*第3次迁移脚本*/
+├── ChatDbContextFactory.cs /*迁移配置程序*/
+└── README.md /*说明文件*/
+
+```
+
+#### 创建迁移
+
+以`NewMigration`为迁移名称
+
+##### VisualStudio 包管理器命令行
+```
+add-migration -c ChatDbContext NewMigration
+```
+
+#### 创建迁移脚本
+
+`NewMigration`为本次迁移名称,`LastMigration`为上次迁移名称(可选),加入`-from` 参数创建`LastMigration`之后所做的增量脚本
+
+##### VisualStudio 包管理器命令行
+
+需要注意当前命令行执行的目录.
+
++ 仅创建迁移增量脚本
+```
+script-migration -o .\src\LLMService.DataProvider.Migrator\Migrations\Scripts\NewMigration.sql -Idempotent -from LastMigration
+```
+
++ 创建整个数据库的建库脚本
+
+`DbProvider`参数可选值:SqlServer,MySql
+```
+script-dbcontext -c ChatDbContext -Args "--DbProvider SqlServer"
+```
diff --git a/src/LLMService.DataProvider.Migrator/appsettings.json b/src/LLMService.DataProvider.Migrator/appsettings.json
new file mode 100644
index 0000000..2fe7645
--- /dev/null
+++ b/src/LLMService.DataProvider.Migrator/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "ConnectionStrings": {
+ "DbConnection": "Data Source=(localdb)\\mssqllocaldb;Database=LLMServiceHub_Db;Trusted_Connection=True;MultipleActiveResultSets=true"
+ //"DbConnection": "Server=.,3306;Database=LLMServiceHub_Db;Uid=sa;Pwd=pass;"
+ },
+ //database provider,optional values:SqlServer|MySql
+ "DbProvider": "SqlServer"
+}
+
diff --git a/src/LLMService.DataProvider.Relational/Data/ChatDbContext.cs b/src/LLMService.DataProvider.Relational/Data/ChatDbContext.cs
new file mode 100644
index 0000000..c81ed3d
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Data/ChatDbContext.cs
@@ -0,0 +1,230 @@
+using LLMService.DataProvider.Relational.Entity;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace LLMService.DataProvider.Relational.Data
+{
+ ///
+ ///
+ ///
+ public class ChatDbContext : ChatDbContext
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// See DbContext lifetime, configuration, and initialization
+ /// for more information.
+ ///
+ public ChatDbContext()
+ {
+ }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for this context.
+ ///
+ /// See DbContext lifetime, configuration, and initialization and
+ /// Using DbContextOptions for more information.
+ ///
+ public ChatDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ internal override void OnModelCreatingFinishing(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingFinishing(modelBuilder);
+
+ modelBuilder.Entity()
+ .Property(x => x.TenantId)
+ .HasMaxLength(128);
+ modelBuilder.Entity()
+ .Property(x => x.UserId)
+ .HasMaxLength(128);
+ modelBuilder.Entity()
+ .Property(x => x.TenantId)
+ .HasMaxLength(128);
+ modelBuilder.Entity()
+ .Property(x => x.UserId)
+ .HasMaxLength(128);
+ modelBuilder.Entity()
+ .Property(x => x.TenantId)
+ .HasMaxLength(128);
+ modelBuilder.Entity()
+ .Property(x => x.UserId)
+ .HasMaxLength(128);
+
+ }
+ }
+
+ ///
+ ///
+ ///
+ /// The type of the chat folder.
+ /// The type of the conversation.
+ /// The type of the chat message.
+ /// The type of the tenant key.
+ /// The type of the user key.
+ /// The type of the conversation key.
+ /// The type of the message key.
+ public class ChatDbContext: DbContext
+ where TUserKey : IEquatable
+ where TTenantKey : IEquatable
+ where TMessageKey : IEquatable
+ where TConversationKey : IEquatable
+ where TChatFolder: ChatFolder
+ where TConversation: Conversation
+ where TMessage: ChatMessage
+ {
+ #region Entities
+ ///
+ /// Gets or sets the chat folders.
+ ///
+ ///
+ /// The chat folders.
+ ///
+ public virtual DbSet ChatFolders { get; set; }
+ ///
+ /// Gets or sets the conversations.
+ ///
+ ///
+ /// The conversations.
+ ///
+ public virtual DbSet Conversations { get; set; }
+ ///
+ /// Gets or sets the chat messages.
+ ///
+ ///
+ /// The chat messages.
+ ///
+ public virtual DbSet ChatMessages { get; set; }
+ #endregion
+
+ #region ctor
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// See DbContext lifetime, configuration, and initialization
+ /// for more information.
+ ///
+ public ChatDbContext()
+ {
+
+ }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for this context.
+ ///
+ /// See DbContext lifetime, configuration, and initialization and
+ /// Using DbContextOptions for more information.
+ ///
+ public ChatDbContext(DbContextOptions options)
+ :base(options)
+ {
+
+ }
+ #endregion
+
+ #region FluentAPI config
+
+ ///
+ /// Override this method to further configure the model that was discovered by convention from the entity types
+ /// exposed in properties on your derived context. The resulting model may be cached
+ /// and re-used for subsequent instances of your derived context.
+ ///
+ /// The builder being used to construct the model for this context. Databases (and other extensions) typically
+ /// define extension methods on this object that allow you to configure aspects of the model that are specific
+ /// to a given database.
+ ///
+ ///
+ /// If a model is explicitly set on the options for this context (via )
+ /// then this method will not be run.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information.
+ ///
+ ///
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ // overriding configurations
+ modelBuilder.Entity(_configureChatFolder);
+ modelBuilder.Entity(_configureConversation);
+ modelBuilder.Entity(_configureChatMessage);
+
+ modelBuilder.ConfigEntitiesThatImplementedFromIDomainEntity(this);
+
+ OnModelCreatingFinishing(modelBuilder);
+ }
+
+ ///
+ /// Called when [model creating internal].
+ /// This method is called after all default model initialization.
+ ///
+ /// The model builder.
+ internal virtual void OnModelCreatingFinishing(ModelBuilder modelBuilder)
+ {
+ // override in derrived class.
+ }
+
+ ///
+ /// Configures the chat folder.
+ ///
+ /// The builder.
+ ///
+ private void _configureChatFolder(EntityTypeBuilder builder)
+ {
+ builder.ToTable("ChatFolder");
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Name)
+ .HasMaxLength(512);
+
+ builder.HasMany()
+ .WithOne()
+ .HasForeignKey(x => x.ParentId)
+ .IsRequired(false);
+
+ }
+ ///
+ /// Configures the conversation.
+ ///
+ /// The builder.
+ ///
+ private void _configureConversation(EntityTypeBuilder builder)
+ {
+ builder.ToTable("Conversation");
+ builder.HasKey(x => x.Id);
+
+ // one to many
+ builder.HasMany()
+ .WithOne()
+ .HasForeignKey(x => x.ConversationId)
+ .IsRequired(false);
+
+ }
+ ///
+ /// Configures the chat message.
+ ///
+ /// The builder.
+ ///
+ private void _configureChatMessage(EntityTypeBuilder builder)
+ {
+ builder.ToTable("ChatMessage");
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Role)
+ .HasMaxLength(256);
+
+ }
+
+
+
+ #endregion
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Data/DbExtensions.cs b/src/LLMService.DataProvider.Relational/Data/DbExtensions.cs
new file mode 100644
index 0000000..5e1eaa2
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Data/DbExtensions.cs
@@ -0,0 +1,139 @@
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using LLMService.DataProvider.Relational.Entity;
+
+namespace LLMService.DataProvider.Relational.Data
+{
+ ///
+ ///
+ ///
+ public static class DbExtensions
+ {
+
+ ///
+ /// Configurations the entities that implemented from i domain entity.
+ ///
+ /// The model builder.
+ /// The database context.
+ ///
+ public static ModelBuilder ConfigEntitiesThatImplementedFromIDomainEntity(this ModelBuilder modelBuilder, DbContext dbContext)
+ {
+ foreach (Type domain in from e in modelBuilder.Model.GetEntityTypes()
+ where !e.IsOwned()
+ select e.ClrType)
+ {
+ modelBuilder.Entity(domain, delegate (EntityTypeBuilder b)
+ {
+
+ if (domain.IsDerivedFrom(typeof(ICreatedUtcTime)))
+ {
+ string defaultDateSql = "";
+ if (dbContext.Database.IsSqlServer())
+ {
+ defaultDateSql = "getutcdate()";
+ }
+ else if(dbContext.Database.IsMysql())
+ {
+ defaultDateSql = "UTC_DATE()";
+ }
+ else
+ {
+ throw new Exception($"DbType {dbContext.Database.ProviderName} not supported");
+ }
+
+ b.Property("CreatedUtcTime").HasDefaultValueSql(defaultDateSql);
+ }
+
+ });
+ }
+
+ return modelBuilder;
+ }
+
+ ///
+ /// Determines whether [is derived from] [the specified pattern].
+ ///
+ /// The type.
+ /// The pattern.
+ ///
+ /// true if [is derived from] [the specified pattern]; otherwise, false.
+ ///
+ ///
+ /// type
+ /// or
+ /// pattern
+ ///
+ public static bool IsDerivedFrom(this Type type, Type pattern)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException("type");
+ }
+
+ if (pattern == null)
+ {
+ throw new ArgumentNullException("pattern");
+ }
+
+ if (type.IsSubclassOf(pattern))
+ {
+ return true;
+ }
+
+ if (pattern.IsAssignableFrom(type))
+ {
+ return true;
+ }
+
+ if (type.GetInterfaces().Any(IsTheRawGenericType))
+ {
+ return true;
+ }
+
+ while (type != null && type != typeof(object))
+ {
+ if (IsTheRawGenericType(type))
+ {
+ return true;
+ }
+
+ type = type.BaseType;
+ }
+
+ return false;
+ bool IsTheRawGenericType(Type test)
+ {
+ return pattern == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
+ }
+ }
+
+ ///
+ /// Determines whether [is SQL server].
+ ///
+ /// The database.
+ ///
+ /// true if [is SQL server] [the specified database]; otherwise, false.
+ ///
+ public static bool IsSqlServer(this DatabaseFacade database)
+ {
+ return database.ProviderName.Equals("Microsoft.EntityFrameworkCore.SqlServer", StringComparison.InvariantCultureIgnoreCase);
+ }
+ ///
+ /// Determines whether this instance is mysql.
+ ///
+ /// The database.
+ ///
+ /// true if the specified database is mysql; otherwise, false.
+ ///
+ public static bool IsMysql(this DatabaseFacade database)
+ {
+ return database.ProviderName.Contains("Mysql", StringComparison.InvariantCultureIgnoreCase);
+ }
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Entity/ChatFolder.cs b/src/LLMService.DataProvider.Relational/Entity/ChatFolder.cs
new file mode 100644
index 0000000..84c5072
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Entity/ChatFolder.cs
@@ -0,0 +1,72 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace LLMService.DataProvider.Relational.Entity
+{
+ ///
+ ///
+ ///
+ public class ChatFolder : ChatFolder
+ {
+ }
+
+ ///
+ ///
+ ///
+ /// The type of the user key.
+ /// The type of the tenant key.
+ public class ChatFolder : ICreatedUtcTime
+ where TUserKey : IEquatable
+ where TTenantKey : IEquatable
+ {
+ ///
+ /// Gets or sets the identifier.
+ ///
+ ///
+ /// The identifier.
+ ///
+ public Guid Id { get; set; }
+ ///
+ /// Gets or sets the parent identifier.
+ ///
+ ///
+ /// The parent identifier.
+ ///
+ public Guid? ParentId { get; set; }
+ ///
+ /// Gets or sets the user identifier.
+ ///
+ ///
+ /// The user identifier.
+ ///
+ public virtual TUserKey UserId { get; set; }
+ ///
+ /// Gets or sets the tenant identifier.
+ ///
+ ///
+ /// The tenant identifier.
+ ///
+ public virtual TTenantKey TenantId { get; set; }
+ ///
+ /// Gets or sets the name.
+ ///
+ ///
+ /// The name.
+ ///
+ public string Name { get; set; }
+ ///
+ /// Gets or sets the created UTC time.
+ ///
+ ///
+ /// The created UTC time.
+ ///
+ public DateTime CreatedUtcTime { get; set; } = DateTime.UtcNow;
+ ///
+ /// Gets or sets a value indicating whether this instance is deleted.
+ ///
+ ///
+ /// true if this instance is deleted; otherwise, false.
+ ///
+ public bool IsDeleted { get; set; } = false;
+
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Entity/ChatMessage.cs b/src/LLMService.DataProvider.Relational/Entity/ChatMessage.cs
new file mode 100644
index 0000000..150cfdf
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Entity/ChatMessage.cs
@@ -0,0 +1,124 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using static LLMService.DataProvider.Relational.Entity.EntityConsts;
+using static LLMService.Shared.Models.LLMApiDefaults;
+
+namespace LLMService.DataProvider.Relational.Entity
+{
+#nullable enable
+ ///
+ ///
+ ///
+ public class ChatMessage: ChatMessage
+ {
+ }
+
+ ///
+ ///
+ ///
+ /// The type of the tenant key.
+ /// The type of the message key.
+ /// The type of the conversation key.
+ /// The type of the user key.
+ public class ChatMessage: ICreatedUtcTime
+ where TTenantKey : IEquatable
+ where TMessageKey : IEquatable
+ where TConversationKey : IEquatable
+ where TUserKey : IEquatable
+ {
+ ///
+ /// Gets or sets the identifier.
+ ///
+ ///
+ /// The identifier.
+ ///
+ public TMessageKey Id { get; set; }
+ ///
+ /// Gets or sets the conversation identifier.
+ ///
+ ///
+ /// The conversation identifier.
+ ///
+ public TConversationKey? ConversationId { get; set; }
+ ///
+ /// Gets or sets the tenant identifier.
+ ///
+ ///
+ /// The tenant identifier.
+ ///
+ public virtual TTenantKey TenantId { get; set; }
+ ///
+ /// Gets or sets the user identifier.
+ ///
+ ///
+ /// The user identifier.
+ ///
+ public virtual TUserKey UserId { get; set; }
+ ///
+ /// Gets or sets the role.
+ ///
+ ///
+ /// The role.
+ ///
+ public string Role { get; set; } = "user";
+ ///
+ /// Gets or sets the content.
+ ///
+ ///
+ /// The content.
+ ///
+ public string Content { get; set; } = "";
+ ///
+ /// Gets or sets the display content.
+ ///
+ ///
+ /// The display content.
+ ///
+ public string DisplayContent { get; set; } = "";
+ ///
+ /// Gets or sets a value indicating whether [use display content].
+ ///
+ ///
+ /// true if [use display content]; otherwise, false.
+ ///
+ public bool UseDisplayContent { get; set; } = false;
+ ///
+ /// Gets or sets the type of the model.
+ ///
+ ///
+ /// The type of the model.
+ ///
+ public LLM_ModelType ModelType { get; set; }
+ ///
+ /// Gets or sets the type of the content.
+ ///
+ ///
+ /// The type of the content.
+ ///
+ public MessageDataType DataType { get; set; } = MessageDataType.PlainText;
+ ///
+ /// Gets or sets the type of the content.
+ ///
+ ///
+ /// The type of the content.
+ ///
+ public MessageContentType ContentType { get; set; } = MessageContentType.Text;
+ ///
+ /// Gets or sets the created UTC time.
+ ///
+ ///
+ /// The created UTC time.
+ ///
+ public DateTime CreatedUtcTime { get; set; } = DateTime.UtcNow;
+ ///
+ /// Gets or sets a value indicating whether this instance is deleted.
+ ///
+ ///
+ /// true if this instance is deleted; otherwise, false.
+ ///
+ public bool IsDeleted { get; set; } = false;
+
+
+ }
+#nullable disable
+}
diff --git a/src/LLMService.DataProvider.Relational/Entity/Conversation.cs b/src/LLMService.DataProvider.Relational/Entity/Conversation.cs
new file mode 100644
index 0000000..c3ab796
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Entity/Conversation.cs
@@ -0,0 +1,75 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace LLMService.DataProvider.Relational.Entity
+{
+ ///
+ ///
+ ///
+ public class Conversation : Conversation
+ {
+ }
+
+
+ ///
+ ///
+ ///
+ /// The type of the conversation key.
+ /// The type of the tenant key.
+ /// The type of the user key.
+ public class Conversation: ICreatedUtcTime
+ where TConversationKey : IEquatable
+ where TTenantKey : IEquatable
+ where TUserKey : IEquatable
+ {
+ ///
+ /// Gets or sets the identifier.
+ ///
+ ///
+ /// The identifier.
+ ///
+ public virtual TConversationKey Id { get; set; }
+ ///
+ /// Gets or sets the tenant identifier.
+ ///
+ ///
+ /// The tenant identifier.
+ ///
+ public virtual TTenantKey TenantId { get; set; }
+ ///
+ /// Gets or sets the user identifier.
+ ///
+ ///
+ /// The user identifier.
+ ///
+ public virtual TUserKey UserId { get; set; }
+ ///
+ /// Gets or sets the folder identifier.
+ ///
+ ///
+ /// The folder identifier.
+ ///
+ public Guid? FolderId { get; set; }
+ ///
+ /// Gets or sets the topic.
+ ///
+ ///
+ /// The topic.
+ ///
+ public string Topic { get; set; } = "";
+ ///
+ /// Gets or sets the created UTC time.
+ ///
+ ///
+ /// The created UTC time.
+ ///
+ public DateTime CreatedUtcTime { get; set; } = DateTime.UtcNow;
+ ///
+ /// Gets or sets a value indicating whether this instance is deleted.
+ ///
+ ///
+ /// true if this instance is deleted; otherwise, false.
+ ///
+ public bool IsDeleted { get; set; } = false;
+
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Entity/EntityConsts.cs b/src/LLMService.DataProvider.Relational/Entity/EntityConsts.cs
new file mode 100644
index 0000000..6d6a3d7
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Entity/EntityConsts.cs
@@ -0,0 +1,50 @@
+namespace LLMService.DataProvider.Relational.Entity
+{
+ ///
+ ///
+ ///
+ public static class EntityConsts
+ {
+ ///
+ ///
+ ///
+ public enum MessageDataType
+ {
+ ///
+ /// The plain text
+ ///
+ PlainText = 0,
+ ///
+ /// The HTML
+ ///
+ Html = 1,
+ ///
+ /// The json
+ ///
+ Json = 2,
+ }
+
+ ///
+ ///
+ ///
+ public enum MessageContentType
+ {
+ ///
+ /// The text
+ ///
+ Text = 0,
+ ///
+ /// The image
+ ///
+ Image = 1,
+ ///
+ /// The audio
+ ///
+ Audio = 2,
+ ///
+ /// The video
+ ///
+ Video = 3
+ }
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Entity/ICreatedUtcTime.cs b/src/LLMService.DataProvider.Relational/Entity/ICreatedUtcTime.cs
new file mode 100644
index 0000000..b9cf122
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Entity/ICreatedUtcTime.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LLMService.DataProvider.Relational.Entity
+{
+ ///
+ ///
+ ///
+ public interface ICreatedUtcTime
+ {
+ ///
+ /// Gets or sets the created UTC time.
+ ///
+ ///
+ /// The created UTC time.
+ ///
+ DateTime CreatedUtcTime { get; set; }
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/LLMService.DataProvider.Relational.csproj b/src/LLMService.DataProvider.Relational/LLMService.DataProvider.Relational.csproj
new file mode 100644
index 0000000..ed500d9
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/LLMService.DataProvider.Relational.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net6.0;net8.0
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 6.0.0
+
+
+
+
+
+
+
+ 8.0.0
+
+
+
+
diff --git a/src/LLMService.DataProvider.Relational/Provider/ChatDataRelationalDbProvider.cs b/src/LLMService.DataProvider.Relational/Provider/ChatDataRelationalDbProvider.cs
new file mode 100644
index 0000000..ef886b1
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Provider/ChatDataRelationalDbProvider.cs
@@ -0,0 +1,71 @@
+using LLMService.Shared.Models;
+using LLMService.Shared.ServiceInterfaces;
+
+namespace LLMService.DataProvider.Relational.Provider
+{
+ ///
+ ///
+ ///
+ /// The type of the chat message.
+ /// The type of the message content.
+ ///
+ public class ChatDataRelationalDbProvider : IChatDataProvider
+ where TChatMessage : IChatMessage, new()
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChatDataRelationalDbProvider()
+ {
+
+ }
+
+ ///
+ /// Adds the chat message.
+ /// not write to cache.
+ ///
+ /// The conversation.
+ /// The message.
+ /// The role.
+ ///
+ ///
+ public Task> AddChatMessage(List conversation, TMessageContent message, string role = "user")
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Gets the conversation.
+ ///
+ /// The conversation identifier.
+ ///
+ ///
+ public Task> GetConversationHistory(string conversationId)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Resets the session.
+ ///
+ /// The conversation identifier.
+ ///
+ ///
+ public bool ResetSession(string conversationId)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Saves the chat.
+ ///
+ /// The conversation identifier.
+ ///
+ ///
+ ///
+ public Task SaveChat(string conversationId, IEnumerable conversation)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Provider/ChatStorage.cs b/src/LLMService.DataProvider.Relational/Provider/ChatStorage.cs
new file mode 100644
index 0000000..dfb80c9
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Provider/ChatStorage.cs
@@ -0,0 +1,48 @@
+using LLMService.DataProvider.Relational.Entity;
+using LLMService.Shared.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LLMService.DataProvider.Relational.Provider
+{
+ ///
+ ///
+ ///
+ /// The type of the chat message.
+ /// The type of the message content.
+ /// The type of the chat database context.
+ public class ChatStorage : IChatStorage
+ where TChatMessage : IChatMessage, new()
+ {
+
+ }
+
+ ///
+ ///
+ ///
+ /// The type of the chat message.
+ /// The type of the message content.
+ /// The type of the chat folder.
+ /// The type of the conversation.
+ /// The type of the message.
+ /// The type of the tenant key.
+ /// The type of the user key.
+ /// The type of the conversation key.
+ /// The type of the message key.
+ ///
+ public class ChatStorage : IChatStorage
+ where TChatMessage : IChatMessage, new()
+ where TUserKey : IEquatable
+ where TTenantKey : IEquatable
+ where TMessageKey : IEquatable
+ where TConversationKey : IEquatable
+ where TChatFolder : ChatFolder
+ where TConversation : Conversation
+ where TMessage : ChatMessage
+ {
+ }
+}
diff --git a/src/LLMService.DataProvider.Relational/Provider/IChatStorage.cs b/src/LLMService.DataProvider.Relational/Provider/IChatStorage.cs
new file mode 100644
index 0000000..8ccbebd
--- /dev/null
+++ b/src/LLMService.DataProvider.Relational/Provider/IChatStorage.cs
@@ -0,0 +1,14 @@
+using LLMService.Shared.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LLMService.DataProvider.Relational.Provider
+{
+ internal interface IChatStorage
+ where TChatMessage : IChatMessage, new()
+ {
+ }
+}