diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
new file mode 100644
index 000000000..670b925f3
--- /dev/null
+++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs
new file mode 100644
index 000000000..2590ba079
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorHostedService.cs
@@ -0,0 +1,53 @@
+using LINGYUN.Abp.MicroService.AIService.DbMigrator;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Data;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+public class AIServiceDbMigratorHostedService : IHostedService
+{
+ private readonly IHostApplicationLifetime _hostApplicationLifetime;
+ private readonly IConfiguration _configuration;
+
+ public AIServiceDbMigratorHostedService(
+ IHostApplicationLifetime hostApplicationLifetime,
+ IConfiguration configuration)
+ {
+ _hostApplicationLifetime = hostApplicationLifetime;
+ _configuration = configuration;
+ }
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ using var application = await AbpApplicationFactory
+ .CreateAsync(options =>
+ {
+ options.Services.ReplaceConfiguration(_configuration);
+ options.UseAutofac();
+ options.Services.AddLogging(c => c.AddSerilog());
+ options.AddDataMigrationEnvironment();
+ });
+
+ await application.InitializeAsync();
+
+ await application
+ .ServiceProvider
+ .GetRequiredService()
+ .CheckAndApplyDatabaseMigrationsAsync();
+
+ await application.ShutdownAsync();
+
+ _hostApplicationLifetime.StopApplication();
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+}
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs
new file mode 100644
index 000000000..ff9dd1c85
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/AIServiceDbMigratorModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Autofac;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.MicroService.AIService.DbMigrator;
+
+[DependsOn(
+ typeof(AbpAutofacModule),
+ typeof(AIServiceMigrationsEntityFrameworkCoreModule)
+ )]
+public class AIServiceDbMigratorModule : AbpModule
+{
+
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj
new file mode 100644
index 000000000..662765cf3
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/LINGYUN.Abp.MicroService.AIService.DbMigrator.csproj
@@ -0,0 +1,40 @@
+
+
+
+
+
+ Exe
+ net10.0
+ enable
+ false
+ LINGYUN.Abp.MicroService.AIService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ Always
+
+
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs
new file mode 100644
index 000000000..135bb215b
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/Program.cs
@@ -0,0 +1,43 @@
+using LINGYUN.Abp.MicroService.AIService;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Serilog;
+using Serilog.Events;
+using System;
+
+var defaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}";
+
+Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Information()
+ .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
+ .MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
+#if DEBUG
+ .MinimumLevel.Override("LINGYUN.Abp.MicroService.AIService", LogEventLevel.Debug)
+#else
+ .MinimumLevel.Override("LINGYUN.Abp.MicroService.AIService", LogEventLevel.Information)
+#endif
+ .Enrich.FromLogContext()
+ .WriteTo.Async(x => x.Console(outputTemplate: defaultOutputTemplate))
+ .WriteTo.Async(x => x.File("Logs/migrations.txt", outputTemplate: defaultOutputTemplate))
+ .CreateLogger();
+
+try
+{
+ var builder = Host.CreateDefaultBuilder(args)
+ .AddAppSettingsSecretsJson()
+ .ConfigureLogging((context, logging) => logging.ClearProviders())
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddHostedService();
+ });
+ await builder.RunConsoleAsync();
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "Host terminated unexpectedly!");
+}
+finally
+{
+ await Log.CloseAndFlushAsync();
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json
new file mode 100644
index 000000000..38212c579
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.DbMigrator/appsettings.json
@@ -0,0 +1,5 @@
+{
+ "ConnectionStrings": {
+ "Default": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456"
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs
new file mode 100644
index 000000000..8a088b76d
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDataSeeder.cs
@@ -0,0 +1,26 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+public class AIServiceDataSeeder : ITransientDependency
+{
+ protected ILogger Logger { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+
+ public AIServiceDataSeeder(
+ ICurrentTenant currentTenant)
+ {
+ CurrentTenant = currentTenant;
+
+ Logger = NullLogger.Instance;
+ }
+
+ public virtual Task SeedAsync(DataSeedContext context)
+ {
+ return Task.CompletedTask;
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs
new file mode 100644
index 000000000..b05842e54
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationEventHandler.cs
@@ -0,0 +1,44 @@
+using Microsoft.Extensions.Logging;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DistributedLocking;
+using Volo.Abp.EntityFrameworkCore.Migrations;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+public class AIServiceDbMigrationEventHandler : EfCoreDatabaseMigrationEventHandlerBase
+{
+ protected AIServiceDataSeeder DataSeeder { get; }
+
+ public AIServiceDbMigrationEventHandler(
+ ICurrentTenant currentTenant,
+ IUnitOfWorkManager unitOfWorkManager,
+ ITenantStore tenantStore,
+ IAbpDistributedLock abpDistributedLock,
+ IDistributedEventBus distributedEventBus,
+ ILoggerFactory loggerFactory,
+ AIServiceDataSeeder dataSeeder)
+ : base(
+ ConnectionStringNameAttribute.GetConnStringName(),
+ currentTenant, unitOfWorkManager, tenantStore, abpDistributedLock, distributedEventBus, loggerFactory)
+ {
+ DataSeeder = dataSeeder;
+ }
+
+ protected async override Task AfterTenantCreated(TenantCreatedEto eventData, bool schemaMigrated)
+ {
+ // 新租户数据种子
+ var context = new DataSeedContext(eventData.Id);
+ if (eventData.Properties != null)
+ {
+ foreach (var property in eventData.Properties)
+ {
+ context.WithProperty(property.Key, property.Value);
+ }
+ }
+
+ await DataSeeder.SeedAsync(context);
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs
new file mode 100644
index 000000000..445f0fd92
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceDbMigrationService.cs
@@ -0,0 +1,36 @@
+using LINGYUN.Abp.Data.DbMigrator;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.DistributedLocking;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+public class AIServiceDbMigrationService : EfCoreRuntimeDbMigratorBase, ITransientDependency
+{
+ protected AIServiceDataSeeder DataSeeder { get; }
+ public AIServiceDbMigrationService(
+ ICurrentTenant currentTenant,
+ IUnitOfWorkManager unitOfWorkManager,
+ IServiceProvider serviceProvider,
+ IAbpDistributedLock abpDistributedLock,
+ IDistributedEventBus distributedEventBus,
+ ILoggerFactory loggerFactory,
+ AIServiceDataSeeder dataSeeder)
+ : base(
+ ConnectionStringNameAttribute.GetConnStringName(),
+ unitOfWorkManager, serviceProvider, currentTenant, abpDistributedLock, distributedEventBus, loggerFactory)
+ {
+ DataSeeder = dataSeeder;
+ }
+
+ protected async override Task SeedAsync()
+ {
+ // DbMigrator迁移数据种子
+ await DataSeeder.SeedAsync(new DataSeedContext());
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs
new file mode 100644
index 000000000..35d068464
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs
@@ -0,0 +1,32 @@
+using LINGYUN.Abp.AIManagement.Chats;
+using LINGYUN.Abp.AIManagement.EntityFrameworkCore;
+using LINGYUN.Abp.AIManagement.Tokens;
+using LINGYUN.Abp.AIManagement.Workspaces;
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Data;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+
+[ConnectionStringName("Default")]
+public class AIServiceMigrationsDbContext :
+ AbpDbContext,
+ IAIManagementDbContext
+{
+ public DbSet WorkspaceDefinitions { get; set; }
+ public DbSet TextChatMessageRecords { get; set; }
+ public DbSet ConversationRecords { get; set; }
+ public DbSet TokenUsageRecords { get; set; }
+
+ public AIServiceMigrationsDbContext(
+ DbContextOptions options) : base(options)
+ {
+ }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+
+ builder.ConfigureAIManagement();
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs
new file mode 100644
index 000000000..afd229619
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContextFactory.cs
@@ -0,0 +1,28 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.Extensions.Configuration;
+using System.IO;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+public class AIServiceMigrationsDbContextFactory : IDesignTimeDbContextFactory
+{
+ public AIServiceMigrationsDbContext CreateDbContext(string[] args)
+ {
+ var configuration = BuildConfiguration();
+ var connectionString = configuration.GetConnectionString("Default");
+
+ var builder = new DbContextOptionsBuilder()
+ .UseNpgsql(connectionString);
+
+ return new AIServiceMigrationsDbContext(builder!.Options);
+ }
+
+ private static IConfigurationRoot BuildConfiguration()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../LINGYUN.Abp.MicroService.AIService.DbMigrator/"))
+ .AddJsonFile("appsettings.json", optional: false);
+
+ return builder.Build();
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs
new file mode 100644
index 000000000..1c84f02f9
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsEntityFrameworkCoreModule.cs
@@ -0,0 +1,45 @@
+using LINGYUN.Abp.AIManagement.EntityFrameworkCore;
+using LINGYUN.Abp.Data.DbMigrator;
+using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore;
+using LINGYUN.Abp.Saas.EntityFrameworkCore;
+using LINGYUN.Abp.TextTemplating.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore.PostgreSql;
+using Volo.Abp.FeatureManagement.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+using Volo.Abp.PermissionManagement.EntityFrameworkCore;
+using Volo.Abp.SettingManagement.EntityFrameworkCore;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+
+[DependsOn(
+ typeof(AbpAIManagementEntityFrameworkCoreModule),
+ typeof(AbpSaasEntityFrameworkCoreModule),
+ typeof(AbpSettingManagementEntityFrameworkCoreModule),
+ typeof(AbpPermissionManagementEntityFrameworkCoreModule),
+ typeof(AbpFeatureManagementEntityFrameworkCoreModule),
+ typeof(AbpLocalizationManagementEntityFrameworkCoreModule),
+ typeof(AbpTextTemplatingEntityFrameworkCoreModule),
+ typeof(AbpEntityFrameworkCorePostgreSqlModule),
+ typeof(AbpDataDbMigratorModule)
+ )]
+public class AIServiceMigrationsEntityFrameworkCoreModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
+ AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAbpDbContext();
+
+ Configure(options =>
+ {
+ options.UseNpgsql();
+ });
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj
new file mode 100644
index 000000000..c85236273
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore.csproj
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ false
+ net10.0
+ enable
+ LINGYUN.Abp.MicroService.AIService
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs
new file mode 100644
index 000000000..3b253c2ca
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.Designer.cs
@@ -0,0 +1,303 @@
+//
+using System;
+using LINGYUN.Abp.MicroService.AIService;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Volo.Abp.EntityFrameworkCore;
+
+#nullable disable
+
+namespace LINGYUN.Abp.MicroService.AIService.Migrations
+{
+ [DbContext(typeof(AIServiceMigrationsDbContext))]
+ [Migration("20260127083027_Initial_AI_Service")]
+ partial class Initial_AI_Service
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
+ .HasAnnotation("ProductVersion", "10.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("ExpiredAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("UpdateAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("AbpAIConversations", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)")
+ .HasColumnName("ConcurrencyStamp");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("ConversationId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("ExtraProperties")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("ExtraProperties");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("ReplyAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ReplyMessage")
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Workspace")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId", "ConversationId");
+
+ b.ToTable("AbpAITextChatMessages", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CachedInputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("ConversationId")
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("InputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("MessageId")
+ .HasColumnType("uuid");
+
+ b.Property("OutputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("ReasoningTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("TotalTokenCount")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId", "ConversationId");
+
+ b.ToTable("AbpAITokenUsages", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("ApiBaseUrl")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ApiKey")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)")
+ .HasColumnName("ConcurrencyStamp");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("Description")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ExtraProperties")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("ExtraProperties");
+
+ b.Property("FrequencyPenalty")
+ .HasColumnType("real");
+
+ b.Property("Instructions")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("IsEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("MaxOutputTokens")
+ .HasColumnType("integer");
+
+ b.Property("ModelName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("PresencePenalty")
+ .HasColumnType("real");
+
+ b.Property("Provider")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("StateCheckers")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("SystemPrompt")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Temperature")
+ .HasColumnType("real");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("AbpAIWorkspaceDefinitions", (string)null);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs
new file mode 100644
index 000000000..ed6c57888
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260127083027_Initial_AI_Service.cs
@@ -0,0 +1,148 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace LINGYUN.Abp.MicroService.AIService.Migrations
+{
+ ///
+ public partial class Initial_AI_Service : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "AbpAIConversations",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ TenantId = table.Column(type: "uuid", nullable: true),
+ Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false),
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
+ ExpiredAt = table.Column(type: "timestamp with time zone", nullable: false),
+ UpdateAt = table.Column(type: "timestamp with time zone", nullable: true),
+ CreationTime = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatorId = table.Column(type: "uuid", nullable: true),
+ LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true),
+ LastModifierId = table.Column(type: "uuid", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AbpAIConversations", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AbpAITextChatMessages",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ Content = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false),
+ ExtraProperties = table.Column(type: "text", nullable: false),
+ ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false),
+ CreationTime = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatorId = table.Column(type: "uuid", nullable: true),
+ LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true),
+ LastModifierId = table.Column(type: "uuid", nullable: true),
+ TenantId = table.Column(type: "uuid", nullable: true),
+ Workspace = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ Role = table.Column(type: "character varying(20)", maxLength: 20, nullable: false),
+ CreatedAt = table.Column(type: "timestamp with time zone", nullable: false),
+ UserId = table.Column(type: "uuid", nullable: true),
+ ConversationId = table.Column(type: "uuid", nullable: true),
+ ReplyMessage = table.Column(type: "text", nullable: true),
+ ReplyAt = table.Column(type: "timestamp with time zone", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AbpAITextChatMessages", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AbpAITokenUsages",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ TenantId = table.Column(type: "uuid", nullable: true),
+ MessageId = table.Column(type: "uuid", nullable: true),
+ ConversationId = table.Column(type: "uuid", nullable: true),
+ InputTokenCount = table.Column(type: "bigint", nullable: true),
+ OutputTokenCount = table.Column(type: "bigint", nullable: true),
+ TotalTokenCount = table.Column(type: "bigint", nullable: true),
+ CachedInputTokenCount = table.Column(type: "bigint", nullable: true),
+ ReasoningTokenCount = table.Column(type: "bigint", nullable: true),
+ CreationTime = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatorId = table.Column(type: "uuid", nullable: true),
+ LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true),
+ LastModifierId = table.Column(type: "uuid", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AbpAITokenUsages", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AbpAIWorkspaceDefinitions",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ Name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ Provider = table.Column(type: "character varying(20)", maxLength: 20, nullable: false),
+ ModelName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ DisplayName = table.Column(type: "character varying(128)", maxLength: 128, nullable: false),
+ Description = table.Column(type: "character varying(128)", maxLength: 128, nullable: true),
+ ApiKey = table.Column(type: "character varying(64)", maxLength: 64, nullable: true),
+ ApiBaseUrl = table.Column(type: "character varying(128)", maxLength: 128, nullable: true),
+ SystemPrompt = table.Column(type: "character varying(512)", maxLength: 512, nullable: true),
+ Instructions = table.Column(type: "character varying(512)", maxLength: 512, nullable: true),
+ Temperature = table.Column(type: "real", nullable: true),
+ MaxOutputTokens = table.Column(type: "integer", nullable: true),
+ FrequencyPenalty = table.Column(type: "real", nullable: true),
+ PresencePenalty = table.Column(type: "real", nullable: true),
+ IsEnabled = table.Column(type: "boolean", nullable: false),
+ StateCheckers = table.Column(type: "character varying(256)", maxLength: 256, nullable: true),
+ ExtraProperties = table.Column(type: "text", nullable: false),
+ ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false),
+ CreationTime = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatorId = table.Column(type: "uuid", nullable: true),
+ LastModificationTime = table.Column(type: "timestamp with time zone", nullable: true),
+ LastModifierId = table.Column(type: "uuid", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AbpAIWorkspaceDefinitions", x => x.Id);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AbpAITextChatMessages_TenantId_ConversationId",
+ table: "AbpAITextChatMessages",
+ columns: new[] { "TenantId", "ConversationId" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AbpAITokenUsages_TenantId_ConversationId",
+ table: "AbpAITokenUsages",
+ columns: new[] { "TenantId", "ConversationId" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AbpAIWorkspaceDefinitions_Name",
+ table: "AbpAIWorkspaceDefinitions",
+ column: "Name",
+ unique: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "AbpAIConversations");
+
+ migrationBuilder.DropTable(
+ name: "AbpAITextChatMessages");
+
+ migrationBuilder.DropTable(
+ name: "AbpAITokenUsages");
+
+ migrationBuilder.DropTable(
+ name: "AbpAIWorkspaceDefinitions");
+ }
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs
new file mode 100644
index 000000000..095a0771d
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs
@@ -0,0 +1,300 @@
+//
+using System;
+using LINGYUN.Abp.MicroService.AIService;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Volo.Abp.EntityFrameworkCore;
+
+#nullable disable
+
+namespace LINGYUN.Abp.MicroService.AIService.Migrations
+{
+ [DbContext(typeof(AIServiceMigrationsDbContext))]
+ partial class AIServiceMigrationsDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
+ .HasAnnotation("ProductVersion", "10.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("ExpiredAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("UpdateAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("AbpAIConversations", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)")
+ .HasColumnName("ConcurrencyStamp");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("ConversationId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("ExtraProperties")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("ExtraProperties");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("ReplyAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ReplyMessage")
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Workspace")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId", "ConversationId");
+
+ b.ToTable("AbpAITextChatMessages", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CachedInputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("ConversationId")
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("InputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("MessageId")
+ .HasColumnType("uuid");
+
+ b.Property("OutputTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("ReasoningTokenCount")
+ .HasColumnType("bigint");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("TenantId");
+
+ b.Property("TotalTokenCount")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId", "ConversationId");
+
+ b.ToTable("AbpAITokenUsages", (string)null);
+ });
+
+ modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("ApiBaseUrl")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ApiKey")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)")
+ .HasColumnName("ConcurrencyStamp");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("CreationTime");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid")
+ .HasColumnName("CreatorId");
+
+ b.Property("Description")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ExtraProperties")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("ExtraProperties");
+
+ b.Property("FrequencyPenalty")
+ .HasColumnType("real");
+
+ b.Property("Instructions")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("IsEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("LastModificationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("LastModificationTime");
+
+ b.Property("LastModifierId")
+ .HasColumnType("uuid")
+ .HasColumnName("LastModifierId");
+
+ b.Property("MaxOutputTokens")
+ .HasColumnType("integer");
+
+ b.Property("ModelName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("PresencePenalty")
+ .HasColumnType("real");
+
+ b.Property("Provider")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("StateCheckers")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("SystemPrompt")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Temperature")
+ .HasColumnType("real");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("AbpAIWorkspaceDefinitions", (string)null);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs
new file mode 100644
index 000000000..f654a1bc1
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.Configure.cs
@@ -0,0 +1,423 @@
+using DotNetCore.CAP;
+using LINGYUN.Abp.AIManagement;
+using LINGYUN.Abp.AIManagement.Chats;
+using LINGYUN.Abp.Localization.CultureMap;
+using LINGYUN.Abp.LocalizationManagement;
+using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using LINGYUN.Abp.TextTemplating;
+using LINGYUN.Abp.Wrapper;
+using Medallion.Threading;
+using Medallion.Threading.Redis;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Cors;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.Caching.StackExchangeRedis;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.OpenApi.Models;
+using StackExchange.Redis;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc.AntiForgery;
+using Volo.Abp.Auditing;
+using Volo.Abp.Caching;
+using Volo.Abp.Domain.Entities.Events.Distributed;
+using Volo.Abp.FeatureManagement;
+using Volo.Abp.GlobalFeatures;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Identity.Localization;
+using Volo.Abp.Json;
+using Volo.Abp.Json.SystemTextJson;
+using Volo.Abp.Localization;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.PermissionManagement;
+using Volo.Abp.Security.Claims;
+using Volo.Abp.SettingManagement;
+using Volo.Abp.Threading;
+using Volo.Abp.Timing;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+
+public partial class AIServiceModule
+{
+ private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
+
+ private void PreConfigureFeature()
+ {
+ OneTimeRunner.Run(() =>
+ {
+ GlobalFeatureManager.Instance.Modules.Editions().EnableAll();
+ });
+ }
+
+ private void PreConfigureApp(IConfiguration configuration)
+ {
+ PreConfigure(options =>
+ {
+ // 以开放端口区别,应在0-31之间
+ options.SnowflakeIdOptions.WorkerId = 19;
+ options.SnowflakeIdOptions.WorkerIdBits = 5;
+ options.SnowflakeIdOptions.DatacenterId = 1;
+ });
+
+ if (configuration.GetValue("App:ShowPii"))
+ {
+ IdentityModelEventSource.ShowPII = true;
+ }
+ }
+
+ private void PreConfigureCAP(IConfiguration configuration)
+ {
+ PreConfigure(options =>
+ {
+ options
+ .UsePostgreSql(mySqlOptions =>
+ {
+ configuration.GetSection("CAP:PostgreSql").Bind(mySqlOptions);
+ })
+ .UseRabbitMQ(rabbitMQOptions =>
+ {
+ configuration.GetSection("CAP:RabbitMQ").Bind(rabbitMQOptions);
+ })
+ .UseDashboard();
+ });
+ }
+
+ private void ConfigureTextTemplating()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicTemplateDefinitionStoreEnabled = true;
+ });
+ }
+
+ private void ConfigureFeatureManagement()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicFeatureStoreEnabled = true;
+ });
+ }
+
+ private void ConfigureJsonSerializer(IConfiguration configuration)
+ {
+ // 统一时间日期格式
+ Configure(options =>
+ {
+ var jsonConfiguration = configuration.GetSection("Json");
+ if (jsonConfiguration.Exists())
+ {
+ jsonConfiguration.Bind(options);
+ }
+ });
+ // 中文序列化的编码问题
+ Configure(options =>
+ {
+ options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
+ });
+ }
+
+ private void ConfigureAIManagement()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicWorkspaceStoreEnabled = true;
+ options.SaveStaticWorkspacesToDatabase = true;
+ });
+ }
+
+ private void ConfigurePermissionManagement()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicPermissionStoreEnabled = true;
+ });
+ }
+
+ private void ConfigureSettingManagement()
+ {
+ Configure(options =>
+ {
+ options.IsDynamicSettingStoreEnabled = true;
+ });
+ }
+
+ private void ConfigureTiming(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ configuration.GetSection("Clock").Bind(options);
+ });
+ }
+
+ private void ConfigureCaching(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ configuration.GetSection("DistributedCache").Bind(options);
+ });
+
+ Configure(options =>
+ {
+ options.AutoEventSelectors.AddNamespace("Volo.Abp.TenantManagement");
+ });
+
+ Configure(options =>
+ {
+ var redisConfig = ConfigurationOptions.Parse(options.Configuration!);
+ options.ConfigurationOptions = redisConfig;
+ options.InstanceName = configuration["Redis:InstanceName"];
+ });
+ }
+
+ private void ConfigureDistributedLocking(IServiceCollection services, IConfiguration configuration)
+ {
+ var distributedLockEnabled = configuration["DistributedLock:IsEnabled"];
+ if (distributedLockEnabled.IsNullOrEmpty() || bool.Parse(distributedLockEnabled))
+ {
+ services.AddSingleton(sp =>
+ {
+ var connectionMultiplexer = sp.GetRequiredService();
+ return new RedisDistributedSynchronizationProvider(connectionMultiplexer.GetDatabase());
+ });
+ }
+ }
+
+ private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ options.ExposeIntegrationServices = true;
+ });
+ }
+
+ private void ConfigureVirtualFileSystem()
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded("LINGYUN.Abp.MicroService.AIService");
+ });
+ }
+
+ private void ConfigureMultiTenancy(IConfiguration configuration)
+ {
+ // 多租户
+ Configure(options =>
+ {
+ options.IsEnabled = true;
+ });
+
+ var tenantResolveCfg = configuration.GetSection("App:Domains");
+ if (tenantResolveCfg.Exists())
+ {
+ Configure(options =>
+ {
+ var domains = tenantResolveCfg.Get() ?? [];
+ foreach (var domain in domains)
+ {
+ options.AddDomainTenantResolver(domain);
+ }
+ });
+ }
+ }
+
+ private void ConfigureIdentity(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ options.IsDynamicClaimsEnabled = true;
+ options.RemoteRefreshUrl = configuration["App:RefreshClaimsUrl"] + options.RemoteRefreshUrl;
+ });
+ }
+
+ private void ConfigureAuditing(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ // 是否启用实体变更记录
+ var allEntitiesSelectorIsEnabled = configuration["Auditing:AllEntitiesSelector"];
+ if (allEntitiesSelectorIsEnabled.IsNullOrWhiteSpace() ||
+ (bool.TryParse(allEntitiesSelectorIsEnabled, out var enabled) && enabled))
+ {
+ options.EntityHistorySelectors.AddAllEntities();
+ }
+ });
+ }
+
+ private void ConfigureSwagger(IServiceCollection services, IConfiguration configuration)
+ {
+ // Swagger
+ services.AddAbpSwaggerGenWithOAuth(
+ configuration["AuthServer:Authority"]!,
+ new Dictionary
+ {
+ { "AIService", "AI Service API"}
+ },
+ options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo
+ {
+ Title = "AI Service API", Version = "v1",
+ Contact = new OpenApiContact
+ {
+ Name = "colin",
+ Email = "colin.in@foxmail.com",
+ Url = new Uri("https://github.com/colinin")
+ },
+ License = new OpenApiLicense
+ {
+ Name = "MIT",
+ Url = new Uri("https://github.com/colinin/abp-next-admin/blob/master/LICENSE")
+ }
+ });
+ options.DocInclusionPredicate((docName, description) => true);
+ options.CustomSchemaIds(type => type.FullName);
+ options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
+ Name = "Authorization",
+ In = ParameterLocation.Header,
+ Scheme = "bearer",
+ Type = SecuritySchemeType.Http,
+ BearerFormat = "JWT"
+ });
+ options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
+ },
+ new string[] { }
+ }
+ });
+ options.OperationFilter();
+ });
+ }
+
+ private void ConfigureLocalization()
+ {
+ // 支持本地化语言类型
+ Configure(options =>
+ {
+ options.Languages.Add(new LanguageInfo("en", "en", "English"));
+ options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
+
+ options.Resources
+ .Get()
+ .AddVirtualJson("/Localization/Resources");
+ });
+
+ Configure(options =>
+ {
+ var zhHansCultureMapInfo = new CultureMapInfo
+ {
+ TargetCulture = "zh-Hans",
+ SourceCultures = new string[] { "zh", "zh_CN", "zh-CN" }
+ };
+
+ options.CulturesMaps.Add(zhHansCultureMapInfo);
+ options.UiCulturesMaps.Add(zhHansCultureMapInfo);
+ });
+
+ Configure(options =>
+ {
+ options.SaveStaticLocalizationsToDatabase = true;
+ });
+ }
+
+ private void ConfigureCors(IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddCors(options =>
+ {
+ options.AddDefaultPolicy(builder =>
+ {
+ var corsOrigins = configuration.GetSection("App:CorsOrigins").Get>();
+ if (corsOrigins == null || corsOrigins.Count == 0)
+ {
+ corsOrigins = configuration["App:CorsOrigins"]?
+ .Split(",", StringSplitOptions.RemoveEmptyEntries)
+ .Select(o => o.RemovePostFix("/"))
+ .ToList() ?? new List();
+ }
+ builder
+ .WithOrigins(corsOrigins
+ .Select(o => o.RemovePostFix("/"))
+ .ToArray()
+ )
+ .WithAbpExposedHeaders()
+ .WithAbpWrapExposedHeaders()
+ .SetIsOriginAllowedToAllowWildcardSubdomains()
+ .AllowAnyHeader()
+ .AllowAnyMethod()
+ .AllowCredentials();
+ });
+ });
+ }
+
+ private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false)
+ {
+ Configure(options =>
+ {
+ options.TokenCookie.HttpOnly = false;
+ options.TokenCookie.SameSite = SameSiteMode.Lax;
+ });
+
+ services.AddAlwaysAllowAuthorization();
+ services.AddAlwaysAllowSession();
+
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddAbpJwtBearer(options =>
+ {
+ configuration.GetSection("AuthServer").Bind(options);
+
+ var validIssuers = configuration.GetSection("AuthServer:ValidIssuers").Get>();
+ if (validIssuers?.Count > 0)
+ {
+ options.TokenValidationParameters.ValidIssuers = validIssuers;
+ options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
+ }
+ var validAudiences = configuration.GetSection("AuthServer:ValidAudiences").Get>();
+ if (validAudiences?.Count > 0)
+ {
+ options.TokenValidationParameters.ValidAudiences = validAudiences;
+ }
+ });
+
+ services
+ .AddDataProtection()
+ .SetApplicationName("LINGYUN.Abp.Application")
+ .PersistKeysToStackExchangeRedis(() =>
+ {
+ var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
+
+ return redis.GetDatabase();
+ },
+ "LINGYUN.Abp.Application:DataProtection:Protection-Keys");
+ }
+
+ private void ConfigureWrapper()
+ {
+ Configure(options =>
+ {
+ options.IsEnabled = true;
+
+ options.IgnoreControllers.Add();
+ });
+ }
+
+ private void PreConfigureWrapper()
+ {
+ // 服务间调用不包装
+ PreConfigure(options =>
+ {
+ options.ProxyClientActions.Add(
+ (_, _, client) =>
+ {
+ client.DefaultRequestHeaders.TryAddWithoutValidation(AbpHttpWrapConsts.AbpDontWrapResult, "true");
+ });
+ });
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs
new file mode 100644
index 000000000..e18ca18d8
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs
@@ -0,0 +1,103 @@
+using LINGYUN.Abp.AIManagement;
+using LINGYUN.Abp.AspNetCore.HttpOverrides;
+using LINGYUN.Abp.AspNetCore.Mvc.Localization;
+using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
+using LINGYUN.Abp.AuditLogging.Elasticsearch;
+using LINGYUN.Abp.Claims.Mapping;
+using LINGYUN.Abp.Data.DbMigrator;
+using LINGYUN.Abp.Emailing.Platform;
+using LINGYUN.Abp.EventBus.CAP;
+using LINGYUN.Abp.ExceptionHandling.Emailing;
+using LINGYUN.Abp.Identity.Session.AspNetCore;
+using LINGYUN.Abp.Localization.CultureMap;
+using LINGYUN.Abp.Logging.Serilog.Elasticsearch;
+using LINGYUN.Abp.Serilog.Enrichers.Application;
+using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using LINGYUN.Abp.Sms.Platform;
+using LINGYUN.Abp.TextTemplating.Scriban;
+using Volo.Abp.AspNetCore.Authentication.JwtBearer;
+using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
+using Volo.Abp.AspNetCore.Serilog;
+using Volo.Abp.Autofac;
+using Volo.Abp.Caching.StackExchangeRedis;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+using Volo.Abp.PermissionManagement.Identity;
+using Volo.Abp.PermissionManagement.OpenIddict;
+using Volo.Abp.Swashbuckle;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+
+[DependsOn(
+ typeof(AbpCAPEventBusModule),
+ typeof(AbpSerilogEnrichersApplicationModule),
+ typeof(AbpSerilogEnrichersUniqueIdModule),
+ typeof(AbpAspNetCoreSerilogModule),
+ typeof(AbpLoggingSerilogElasticsearchModule),
+ typeof(AbpAuditLoggingElasticsearchModule),
+ typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
+ typeof(AbpAspNetCoreMvcLocalizationModule),
+
+ typeof(AbpPermissionManagementDomainIdentityModule),
+ typeof(AbpPermissionManagementDomainOpenIddictModule),
+
+ // 重写模板引擎支持外部本地化
+ typeof(AbpTextTemplatingScribanModule),
+
+ typeof(AbpIdentitySessionAspNetCoreModule),
+
+ typeof(AbpAIManagementApplicationModule),
+ typeof(AbpAIManagementHttpApiModule),
+ typeof(AIServiceMigrationsEntityFrameworkCoreModule),
+ typeof(AbpDataDbMigratorModule),
+ typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
+ typeof(AbpEmailingExceptionHandlingModule),
+ typeof(AbpHttpClientModule),
+ typeof(AbpSmsPlatformModule),
+ typeof(AbpEmailingPlatformModule),
+ typeof(AbpCachingStackExchangeRedisModule),
+ typeof(AbpLocalizationCultureMapModule),
+ typeof(AbpAspNetCoreMvcWrapperModule),
+ typeof(AbpAspNetCoreHttpOverridesModule),
+ typeof(AbpClaimsMappingModule),
+ typeof(AbpSwashbuckleModule),
+ typeof(AbpAutofacModule)
+ )]
+public partial class AIServiceModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Services.GetConfiguration();
+
+ PreConfigureWrapper();
+ PreConfigureFeature();
+ PreConfigureApp(configuration);
+ PreConfigureCAP(configuration);
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var hostingEnvironment = context.Services.GetHostingEnvironment();
+ var configuration = context.Services.GetConfiguration();
+
+ ConfigureWrapper();
+ ConfigureLocalization();
+ ConfigureVirtualFileSystem();
+ ConfigureTextTemplating();
+ ConfigureAIManagement();
+ ConfigureSettingManagement();
+ ConfigureFeatureManagement();
+ ConfigurePermissionManagement();
+ ConfigureIdentity(configuration);
+ ConfigureTiming(configuration);
+ ConfigureCaching(configuration);
+ ConfigureAuditing(configuration);
+ ConfigureMultiTenancy(configuration);
+ ConfigureJsonSerializer(configuration);
+ ConfigureMvc(context.Services, configuration);
+ ConfigureCors(context.Services, configuration);
+ ConfigureSwagger(context.Services, configuration);
+ ConfigureDistributedLocking(context.Services, configuration);
+ ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment());
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj
new file mode 100644
index 000000000..dafa6b426
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj
@@ -0,0 +1,71 @@
+
+
+
+ net10.0
+ enable
+ enable
+ LINGYUN.Abp.MicroService.AIService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs
new file mode 100644
index 000000000..85b652951
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Program.cs
@@ -0,0 +1,99 @@
+using LINGYUN.Abp.Identity.Session.AspNetCore;
+using LINGYUN.Abp.MicroService.AIService;
+using LINGYUN.Abp.Serilog.Enrichers.Application;
+using Serilog;
+using Volo.Abp.IO;
+using Volo.Abp.Modularity.PlugIns;
+
+Log.Information("Starting AIService Host...");
+
+try
+{
+ var builder = WebApplication.CreateBuilder(args);
+ builder.Host.AddAppSettingsSecretsJson()
+ .UseAutofac()
+ .ConfigureAppConfiguration((context, config) =>
+ {
+ if (context.Configuration.GetValue("AgileConfig:IsEnabled", false))
+ {
+ config.AddAgileConfig(new AgileConfig.Client.ConfigClient(context.Configuration));
+ }
+ })
+ .UseSerilog((context, provider, config) =>
+ {
+ config.ReadFrom.Configuration(context.Configuration);
+ });
+
+ builder.AddServiceDefaults();
+
+ await builder.AddApplicationAsync(options =>
+ {
+ var applicationName = Environment.GetEnvironmentVariable("APPLICATION_NAME") ?? "AIService";
+ options.ApplicationName = applicationName;
+ AbpSerilogEnrichersConsts.ApplicationName = applicationName;
+
+ var pluginFolder = Path.Combine(Directory.GetCurrentDirectory(), "Modules");
+ DirectoryHelper.CreateIfNotExists(pluginFolder);
+ options.PlugInSources.AddFolder(pluginFolder, SearchOption.AllDirectories);
+ });
+
+ var app = builder.Build();
+
+ await app.InitializeApplicationAsync();
+
+ app.MapDefaultEndpoints();
+
+ app.UseForwardedHeaders();
+ // 本地化
+ app.UseMapRequestLocalization();
+ // http调用链
+ app.UseCorrelationId();
+ // 文件系统
+ app.MapAbpStaticAssets();
+ // 路由
+ app.UseRouting();
+ // 跨域
+ app.UseCors();
+ // 认证
+ app.UseAuthentication();
+ app.UseJwtTokenMiddleware();
+ // 多租户
+ app.UseMultiTenancy();
+ // 会话
+ app.UseAbpSession();
+ // jwt
+ app.UseDynamicClaims();
+ // 授权
+ app.UseAuthorization();
+ // Swagger
+ app.UseSwagger();
+ // Swagger可视化界面
+ app.UseAbpSwaggerUI(options =>
+ {
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support AI Service API");
+
+ var configuration = app.Configuration;
+ options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
+ options.OAuthScopes(configuration["AuthServer:Audience"]);
+ });
+ // 审计日志
+ app.UseAuditing();
+ app.UseAbpSerilogEnrichers();
+ // 路由
+ app.UseConfiguredEndpoints();
+
+ await app.RunAsync();
+}
+catch (Exception ex)
+{
+ if (ex is HostAbortedException)
+ {
+ throw;
+ }
+
+ Log.Fatal(ex, "Host terminated unexpectedly!");
+}
+finally
+{
+ await Log.CloseAndFlushAsync();
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json
new file mode 100644
index 000000000..312571616
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "LINGYUN.Abp.MicroService.AIService": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:49787;http://localhost:49788"
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs
new file mode 100644
index 000000000..f72407569
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/TenantHeaderParamter.cs
@@ -0,0 +1,35 @@
+using Microsoft.Extensions.Options;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+using Volo.Abp.AspNetCore.MultiTenancy;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.MicroService.AIService;
+
+public class TenantHeaderParamter : IOperationFilter
+{
+ private readonly AbpMultiTenancyOptions _multiTenancyOptions;
+ private readonly AbpAspNetCoreMultiTenancyOptions _aspNetCoreMultiTenancyOptions;
+ public TenantHeaderParamter(
+ IOptions multiTenancyOptions,
+ IOptions aspNetCoreMultiTenancyOptions)
+ {
+ _multiTenancyOptions = multiTenancyOptions.Value;
+ _aspNetCoreMultiTenancyOptions = aspNetCoreMultiTenancyOptions.Value;
+ }
+
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ if (_multiTenancyOptions.IsEnabled)
+ {
+ operation.Parameters = operation.Parameters ?? new List();
+ operation.Parameters.Add(new OpenApiParameter
+ {
+ Name = _aspNetCoreMultiTenancyOptions.TenantKey,
+ In = ParameterLocation.Header,
+ Description = "Tenant Id in http header",
+ Required = false
+ });
+ }
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json
new file mode 100644
index 000000000..c9a9d8327
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json
@@ -0,0 +1,116 @@
+{
+ "App": {
+ "ShowPii": true,
+ "CorsOrigins": [ "http://localhost:5666", "http://localhost:30000" ],
+ "RefreshClaimsUrl": "http://localhost:30015"
+ },
+ "Auditing": {
+ "AllEntitiesSelector": true
+ },
+ "DistributedCache": {
+ "HideErrors": true,
+ "KeyPrefix": "LINGYUN.Abp.Application",
+ "GlobalCacheEntryOptions": {
+ "SlidingExpiration": "30:00:00",
+ "AbsoluteExpirationRelativeToNow": "60:00:00"
+ }
+ },
+ "ConnectionStrings": {
+ "Default": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456"
+ },
+ "CAP": {
+ "EventBus": {
+ "DefaultGroupName": "AIService",
+ "Version": "v1",
+ "FailedRetryInterval": 300,
+ "FailedRetryCount": 10,
+ "CollectorCleaningInterval": 3600000
+ },
+ "PostgreSql": {
+ "TableNamePrefix": "admin",
+ "ConnectionString": "Host=127.0.0.1;Database=abp;Username=postgres;Password=123456"
+ },
+ "RabbitMQ": {
+ "HostName": "localhost",
+ "Port": 5672,
+ "UserName": "admin",
+ "Password": "123456",
+ "ExchangeName": "LINGYUN.Abp.Application",
+ "VirtualHost": "/"
+ }
+ },
+ "DistributedLock": {
+ "IsEnabled": true,
+ "Redis": {
+ "Configuration": "localhost,defaultDatabase=13"
+ }
+ },
+ "Redis": {
+ "Configuration": "localhost,defaultDatabase=10",
+ "InstanceName": "LINGYUN.Abp.Application"
+ },
+ "AuthServer": {
+ "Authority": "http://localhost:44385/",
+ "Audience": "admin-service",
+ "ValidAudiences": [ "lingyun-abp-application" ],
+ "MapInboundClaims": false,
+ "RequireHttpsMetadata": false,
+ "SwaggerClientId": "vue-oauth-client"
+ },
+ "RemoteServices": {
+ "Platform": {
+ "BaseUrl": "http://localhost:30025",
+ "UseCurrentAccessToken": false
+ }
+ },
+ "Logging": {
+ "Serilog": {
+ "Elasticsearch": {
+ "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}"
+ }
+ }
+ },
+ "AuditLogging": {
+ "Elasticsearch": {
+ "IndexPrefix": "abp.dev.auditing"
+ }
+ },
+ "Elasticsearch": {
+ "NodeUris": "http://elasticsearch"
+ },
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "System": "Warning",
+ "Microsoft": "Warning",
+ "DotNetCore": "Debug"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Async",
+ "Args": {
+ "configure": [
+ {
+ "Name": "Console",
+ "Args": {
+ "restrictedToMinimumLevel": "Debug",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "Elasticsearch",
+ "Args": {
+ "nodeUris": "http://elasticsearch",
+ "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}",
+ "autoRegisterTemplate": true,
+ "autoRegisterTemplateVersion": "ESv7"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json
new file mode 100644
index 000000000..b8e4da13c
--- /dev/null
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.json
@@ -0,0 +1,91 @@
+{
+ "Clock": {
+ "Kind": "Local"
+ },
+ "Forwarded": {
+ "ForwardedHeaders": "XForwardedFor,XForwardedProto"
+ },
+ "StringEncryption": {
+ "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
+ "InitVectorBytes": "s83ng0abvd02js84",
+ "DefaultSalt": "sf&5)s3#"
+ },
+ "Json": {
+ "InputDateTimeFormats": [
+ "yyyy-MM-dd HH:mm:ss",
+ "yyyy-MM-ddTHH:mm:ss"
+ ]
+ },
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "System": "Warning",
+ "Microsoft": "Warning",
+ "DotNetCore": "Information"
+ }
+ },
+ "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ],
+ "WriteTo": [
+ {
+ "Name": "Async",
+ "Args": {
+ "configure": [
+ {
+ "Name": "Console",
+ "Args": {
+ "restrictedToMinimumLevel": "Debug",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Debug-.log",
+ "restrictedToMinimumLevel": "Debug",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Info-.log",
+ "restrictedToMinimumLevel": "Information",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Warn-.log",
+ "restrictedToMinimumLevel": "Warning",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Error-.log",
+ "restrictedToMinimumLevel": "Error",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Fatal-.log",
+ "restrictedToMinimumLevel": "Fatal",
+ "rollingInterval": "Day",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json
index 61baae0e6..34e4940a0 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.ApiGateway/yarp.json
@@ -1,6 +1,21 @@
{
"ReverseProxy": {
"Routes": {
+ "ai-management-route": {
+ "ClusterId": "ai-management-cluster",
+ "Match": {
+ "Path": "/api/ai-management/{**everything}"
+ },
+ "Transforms": [
+ {
+ "HeaderPrefix": "X-Forwarded-",
+ "X-Forwarded": "Append"
+ },
+ {
+ "ResponseHeadersAllowed": "_AbpWrapResult;_AbpDontWrapResult;_AbpErrorFormat"
+ }
+ ]
+ },
"abp-route": {
"ClusterId": "admin-service-cluster",
"Match": {
@@ -451,6 +466,16 @@
}
},
"Clusters": {
+ "ai-management-cluster": {
+ "Destinations": {
+ "destination1": {
+ "Address": "http://localhost:30070",
+ "Metadata": {
+ "SwaggerEndpoint": "http://localhost:30070"
+ }
+ }
+ }
+ },
"auth-server-cluster": {
"Destinations": {
"destination1": {
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs
index 48a47b899..dadaf8d82 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/AppHost.cs
@@ -10,8 +10,17 @@ var redis = builder.AddRedis("redis")
// Elasticsearch
var elasticsearch = builder.AddElasticsearch("elasticsearch")
.WithContainerName("elasticsearch")
+ .WithImageTag("8.17.3")
.WithDataVolume("elasticsearch-dev")
- .WithEnvironment("ES_JAVA_OPTS", "-Xms2g -Xmx2g");
+ .WithEnvironment("ES_JAVA_OPTS", "-Xms2g -Xmx2g")
+ // see: https://www.funkysi1701.com/posts/2025/adding-elasticsearch-with-aspire/
+ .WithEnvironment("xpack.security.enabled", "false");
+
+// Kibana
+builder.AddContainer("kibana", "kibana", "8.17.3")
+ .WithReference(elasticsearch)
+ .WithEndpoint(5601, 5601)
+ .WaitFor(elasticsearch);
// Postgres
var postgres = builder.AddPostgres("postgres")
@@ -215,9 +224,21 @@ builder.AddProject("WorkflowS
.WaitFor(rabbitmq)
.WaitFor(taskService);
+// AIService
+AddDotNetProject<
+ Projects.LINGYUN_Abp_MicroService_AIService_DbMigrator,
+ Projects.LINGYUN_Abp_MicroService_AIService>(
+ builder: builder,
+ servicePrefix: "AI",
+ serviceSuffix: "Service",
+ migratorSuffix: "Migrator",
+ port: 30070,
+ portName: "ai",
+ waitProject: localizationService);
+
// ApiGateway
var apigateway = builder.AddProject("ApiGateway")
- .WithHttpEndpoint(port: 30000, name: "gateway")
+ // .WithHttpEndpoint(port: 30000, name: "gateway")
.WithExternalHttpEndpoints()
.WithReference(redis, "Redis")
.WithReference(elasticsearch, "Elasticsearch")
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj
index cccaa6637..807b9b7a0 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AppHost/LINGYUN.Abp.MicroService.AppHost.csproj
@@ -17,6 +17,8 @@
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj
index 2a4b28ffb..ba3e066a1 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/LINGYUN.Abp.MicroService.TaskService.csproj
@@ -55,6 +55,7 @@
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs
index 42e80af93..4933dbb3b 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.TaskService/TaskServiceModule.cs
@@ -10,6 +10,7 @@ using LINGYUN.Abp.BackgroundTasks.Notifications;
using LINGYUN.Abp.BackgroundTasks.Quartz;
using LINGYUN.Abp.Claims.Mapping;
using LINGYUN.Abp.Data.DbMigrator;
+using LINGYUN.Abp.Elasticsearch.Jobs;
using LINGYUN.Abp.Emailing.Platform;
using LINGYUN.Abp.EventBus.CAP;
using LINGYUN.Abp.ExceptionHandling.Emailing;
@@ -54,6 +55,7 @@ namespace LINGYUN.Abp.MicroService.TaskService;
typeof(AbpHttpClientIdentityModelWebModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpAspNetCoreMvcLocalizationModule),
+ typeof(AbpElasticsearchJobsModule),
typeof(AbpBackgroundTasksJobsModule),
typeof(AbpBackgroundTasksQuartzModule),
typeof(AbpBackgroundTasksDistributedLockingModule),
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs
new file mode 100644
index 000000000..95b93584f
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs
@@ -0,0 +1,110 @@
+using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Localization;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.AI;
+using Volo.Abp.Authorization;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.SimpleStateChecking;
+
+namespace LINGYUN.Abp.AI.Agent;
+public class AgentFactory : IAgentFactory, IScopedDependency
+{
+ protected IServiceProvider ServiceProvider { get; }
+ protected IChatClientFactory ChatClientFactory { get; }
+ protected IStringLocalizerFactory StringLocalizerFactory { get; }
+ protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; }
+ protected ISimpleStateCheckerManager StateCheckerManager { get; }
+ public AgentFactory(
+ IServiceProvider serviceProvider,
+ IChatClientFactory chatClientFactory,
+ IStringLocalizerFactory stringLocalizerFactory,
+ IWorkspaceDefinitionManager workspaceDefinitionManager,
+ ISimpleStateCheckerManager stateCheckerManager)
+ {
+ ServiceProvider = serviceProvider;
+ ChatClientFactory = chatClientFactory;
+ StringLocalizerFactory = stringLocalizerFactory;
+ WorkspaceDefinitionManager = workspaceDefinitionManager;
+ StateCheckerManager = stateCheckerManager;
+ }
+
+ public async virtual Task CreateAsync()
+ {
+ var workspace = WorkspaceNameAttribute.GetWorkspaceName();
+
+ var chatClient = await ChatClientFactory.CreateAsync();
+
+ var workspaceDefine = await WorkspaceDefinitionManager.GetOrNullAsync(workspace);
+
+ if (workspaceDefine != null)
+ {
+ await CheckWorkspaceStateAsync(workspaceDefine);
+ }
+
+ return await CreateAgentAsync(chatClient, workspaceDefine);
+ }
+
+ public async virtual Task CreateAsync(string workspace)
+ {
+ var workspaceDefine = await WorkspaceDefinitionManager.GetAsync(workspace);
+
+ await CheckWorkspaceStateAsync(workspaceDefine);
+
+ var chatClient = await ChatClientFactory.CreateAsync(workspace);
+
+ return await CreateAgentAsync(chatClient, workspaceDefine);
+ }
+
+ protected async virtual Task