✨ Thinking
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.Design; | ||||
| using NodaTime; | ||||
|  | ||||
| namespace DysonNetwork.Develop; | ||||
|  | ||||
| @@ -29,6 +30,35 @@ public class AppDatabase( | ||||
|  | ||||
|         base.OnConfiguring(optionsBuilder); | ||||
|     } | ||||
|      | ||||
|     public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         var now = SystemClock.Instance.GetCurrentInstant(); | ||||
|  | ||||
|         foreach (var entry in ChangeTracker.Entries<ModelBase>()) | ||||
|         { | ||||
|             switch (entry.State) | ||||
|             { | ||||
|                 case EntityState.Added: | ||||
|                     entry.Entity.CreatedAt = now; | ||||
|                     entry.Entity.UpdatedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Modified: | ||||
|                     entry.Entity.UpdatedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Deleted: | ||||
|                     entry.State = EntityState.Modified; | ||||
|                     entry.Entity.DeletedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Detached: | ||||
|                 case EntityState.Unchanged: | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return await base.SaveChangesAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     protected override void OnModelCreating(ModelBuilder modelBuilder) | ||||
|     { | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="EFCore.NamingConventions" Version="9.0.0" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|         </PackageReference> | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|         <PackageReference Include="FFMpegCore" Version="5.2.0" /> | ||||
|         <PackageReference Include="Grpc.AspNetCore.Server" Version="2.71.0" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|           <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|           <PrivateAssets>all</PrivateAssets> | ||||
|         </PackageReference> | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.Design; | ||||
| using NodaTime; | ||||
|  | ||||
| namespace DysonNetwork.Insight; | ||||
|  | ||||
| @@ -8,6 +10,9 @@ public class AppDatabase( | ||||
|     IConfiguration configuration | ||||
| ) : DbContext(options) | ||||
| { | ||||
|     public DbSet<SnThinkingSequence> ThinkingSequences { get; set; } | ||||
|     public DbSet<SnThinkingThought> ThinkingThoughts { get; set; } | ||||
|      | ||||
|     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | ||||
|     { | ||||
|         optionsBuilder.UseNpgsql( | ||||
| @@ -20,6 +25,35 @@ public class AppDatabase( | ||||
|  | ||||
|         base.OnConfiguring(optionsBuilder); | ||||
|     } | ||||
|      | ||||
|     public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         var now = SystemClock.Instance.GetCurrentInstant(); | ||||
|  | ||||
|         foreach (var entry in ChangeTracker.Entries<ModelBase>()) | ||||
|         { | ||||
|             switch (entry.State) | ||||
|             { | ||||
|                 case EntityState.Added: | ||||
|                     entry.Entity.CreatedAt = now; | ||||
|                     entry.Entity.UpdatedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Modified: | ||||
|                     entry.Entity.UpdatedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Deleted: | ||||
|                     entry.State = EntityState.Modified; | ||||
|                     entry.Entity.DeletedAt = now; | ||||
|                     break; | ||||
|                 case EntityState.Detached: | ||||
|                 case EntityState.Unchanged: | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return await base.SaveChangesAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     protected override void OnModelCreating(ModelBuilder modelBuilder) | ||||
|     { | ||||
|   | ||||
| @@ -9,6 +9,10 @@ | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="EFCore.NamingConventions" Version="9.0.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|       <PrivateAssets>all</PrivateAssets> | ||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|     </PackageReference> | ||||
|     <PackageReference Include="Microsoft.SemanticKernel" Version="1.66.0" /> | ||||
|     <PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" /> | ||||
|     <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> | ||||
|   | ||||
							
								
								
									
										124
									
								
								DysonNetwork.Insight/Migrations/20251025115921_AddThinkingThought.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								DysonNetwork.Insight/Migrations/20251025115921_AddThinkingThought.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| // <auto-generated /> | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using DysonNetwork.Insight; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.Infrastructure; | ||||
| using Microsoft.EntityFrameworkCore.Migrations; | ||||
| using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||||
| using NodaTime; | ||||
| using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; | ||||
|  | ||||
| #nullable disable | ||||
|  | ||||
| namespace DysonNetwork.Insight.Migrations | ||||
| { | ||||
|     [DbContext(typeof(AppDatabase))] | ||||
|     [Migration("20251025115921_AddThinkingThought")] | ||||
|     partial class AddThinkingThought | ||||
|     { | ||||
|         /// <inheritdoc /> | ||||
|         protected override void BuildTargetModel(ModelBuilder modelBuilder) | ||||
|         { | ||||
| #pragma warning disable 612, 618 | ||||
|             modelBuilder | ||||
|                 .HasAnnotation("ProductVersion", "9.0.10") | ||||
|                 .HasAnnotation("Relational:MaxIdentifierLength", 63); | ||||
|  | ||||
|             NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingSequence", b => | ||||
|                 { | ||||
|                     b.Property<Guid>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("id"); | ||||
|  | ||||
|                     b.Property<Guid>("AccountId") | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("account_id"); | ||||
|  | ||||
|                     b.Property<Instant>("CreatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("created_at"); | ||||
|  | ||||
|                     b.Property<Instant?>("DeletedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("deleted_at"); | ||||
|  | ||||
|                     b.Property<string>("Topic") | ||||
|                         .HasMaxLength(4096) | ||||
|                         .HasColumnType("character varying(4096)") | ||||
|                         .HasColumnName("topic"); | ||||
|  | ||||
|                     b.Property<Instant>("UpdatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("updated_at"); | ||||
|  | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_thinking_sequences"); | ||||
|  | ||||
|                     b.ToTable("thinking_sequences", (string)null); | ||||
|                 }); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingThought", b => | ||||
|                 { | ||||
|                     b.Property<Guid>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("id"); | ||||
|  | ||||
|                     b.Property<string>("Content") | ||||
|                         .HasColumnType("text") | ||||
|                         .HasColumnName("content"); | ||||
|  | ||||
|                     b.Property<Instant>("CreatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("created_at"); | ||||
|  | ||||
|                     b.Property<Instant?>("DeletedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("deleted_at"); | ||||
|  | ||||
|                     b.Property<List<SnCloudFileReferenceObject>>("Files") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnType("jsonb") | ||||
|                         .HasColumnName("files"); | ||||
|  | ||||
|                     b.Property<int>("Role") | ||||
|                         .HasColumnType("integer") | ||||
|                         .HasColumnName("role"); | ||||
|  | ||||
|                     b.Property<Guid>("SequenceId") | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("sequence_id"); | ||||
|  | ||||
|                     b.Property<Instant>("UpdatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("updated_at"); | ||||
|  | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_thinking_thoughts"); | ||||
|  | ||||
|                     b.HasIndex("SequenceId") | ||||
|                         .HasDatabaseName("ix_thinking_thoughts_sequence_id"); | ||||
|  | ||||
|                     b.ToTable("thinking_thoughts", (string)null); | ||||
|                 }); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingThought", b => | ||||
|                 { | ||||
|                     b.HasOne("DysonNetwork.Shared.Models.SnThinkingSequence", "Sequence") | ||||
|                         .WithMany() | ||||
|                         .HasForeignKey("SequenceId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_thinking_thoughts_thinking_sequences_sequence_id"); | ||||
|  | ||||
|                     b.Navigation("Sequence"); | ||||
|                 }); | ||||
| #pragma warning restore 612, 618 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,73 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore.Migrations; | ||||
| using NodaTime; | ||||
|  | ||||
| #nullable disable | ||||
|  | ||||
| namespace DysonNetwork.Insight.Migrations | ||||
| { | ||||
|     /// <inheritdoc /> | ||||
|     public partial class AddThinkingThought : Migration | ||||
|     { | ||||
|         /// <inheritdoc /> | ||||
|         protected override void Up(MigrationBuilder migrationBuilder) | ||||
|         { | ||||
|             migrationBuilder.CreateTable( | ||||
|                 name: "thinking_sequences", | ||||
|                 columns: table => new | ||||
|                 { | ||||
|                     id = table.Column<Guid>(type: "uuid", nullable: false), | ||||
|                     topic = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true), | ||||
|                     account_id = table.Column<Guid>(type: "uuid", nullable: false), | ||||
|                     created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||
|                     updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||
|                     deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
|                     table.PrimaryKey("pk_thinking_sequences", x => x.id); | ||||
|                 }); | ||||
|  | ||||
|             migrationBuilder.CreateTable( | ||||
|                 name: "thinking_thoughts", | ||||
|                 columns: table => new | ||||
|                 { | ||||
|                     id = table.Column<Guid>(type: "uuid", nullable: false), | ||||
|                     content = table.Column<string>(type: "text", nullable: true), | ||||
|                     files = table.Column<List<SnCloudFileReferenceObject>>(type: "jsonb", nullable: false), | ||||
|                     role = table.Column<int>(type: "integer", nullable: false), | ||||
|                     sequence_id = table.Column<Guid>(type: "uuid", nullable: false), | ||||
|                     created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||
|                     updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||
|                     deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
|                     table.PrimaryKey("pk_thinking_thoughts", x => x.id); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_thinking_thoughts_thinking_sequences_sequence_id", | ||||
|                         column: x => x.sequence_id, | ||||
|                         principalTable: "thinking_sequences", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                 }); | ||||
|  | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_thinking_thoughts_sequence_id", | ||||
|                 table: "thinking_thoughts", | ||||
|                 column: "sequence_id"); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         protected override void Down(MigrationBuilder migrationBuilder) | ||||
|         { | ||||
|             migrationBuilder.DropTable( | ||||
|                 name: "thinking_thoughts"); | ||||
|  | ||||
|             migrationBuilder.DropTable( | ||||
|                 name: "thinking_sequences"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										121
									
								
								DysonNetwork.Insight/Migrations/AppDatabaseModelSnapshot.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								DysonNetwork.Insight/Migrations/AppDatabaseModelSnapshot.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| // <auto-generated /> | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using DysonNetwork.Insight; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.Infrastructure; | ||||
| using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||||
| using NodaTime; | ||||
| using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; | ||||
|  | ||||
| #nullable disable | ||||
|  | ||||
| namespace DysonNetwork.Insight.Migrations | ||||
| { | ||||
|     [DbContext(typeof(AppDatabase))] | ||||
|     partial class AppDatabaseModelSnapshot : ModelSnapshot | ||||
|     { | ||||
|         protected override void BuildModel(ModelBuilder modelBuilder) | ||||
|         { | ||||
| #pragma warning disable 612, 618 | ||||
|             modelBuilder | ||||
|                 .HasAnnotation("ProductVersion", "9.0.10") | ||||
|                 .HasAnnotation("Relational:MaxIdentifierLength", 63); | ||||
|  | ||||
|             NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingSequence", b => | ||||
|                 { | ||||
|                     b.Property<Guid>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("id"); | ||||
|  | ||||
|                     b.Property<Guid>("AccountId") | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("account_id"); | ||||
|  | ||||
|                     b.Property<Instant>("CreatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("created_at"); | ||||
|  | ||||
|                     b.Property<Instant?>("DeletedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("deleted_at"); | ||||
|  | ||||
|                     b.Property<string>("Topic") | ||||
|                         .HasMaxLength(4096) | ||||
|                         .HasColumnType("character varying(4096)") | ||||
|                         .HasColumnName("topic"); | ||||
|  | ||||
|                     b.Property<Instant>("UpdatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("updated_at"); | ||||
|  | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_thinking_sequences"); | ||||
|  | ||||
|                     b.ToTable("thinking_sequences", (string)null); | ||||
|                 }); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingThought", b => | ||||
|                 { | ||||
|                     b.Property<Guid>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("id"); | ||||
|  | ||||
|                     b.Property<string>("Content") | ||||
|                         .HasColumnType("text") | ||||
|                         .HasColumnName("content"); | ||||
|  | ||||
|                     b.Property<Instant>("CreatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("created_at"); | ||||
|  | ||||
|                     b.Property<Instant?>("DeletedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("deleted_at"); | ||||
|  | ||||
|                     b.Property<List<SnCloudFileReferenceObject>>("Files") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnType("jsonb") | ||||
|                         .HasColumnName("files"); | ||||
|  | ||||
|                     b.Property<int>("Role") | ||||
|                         .HasColumnType("integer") | ||||
|                         .HasColumnName("role"); | ||||
|  | ||||
|                     b.Property<Guid>("SequenceId") | ||||
|                         .HasColumnType("uuid") | ||||
|                         .HasColumnName("sequence_id"); | ||||
|  | ||||
|                     b.Property<Instant>("UpdatedAt") | ||||
|                         .HasColumnType("timestamp with time zone") | ||||
|                         .HasColumnName("updated_at"); | ||||
|  | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_thinking_thoughts"); | ||||
|  | ||||
|                     b.HasIndex("SequenceId") | ||||
|                         .HasDatabaseName("ix_thinking_thoughts_sequence_id"); | ||||
|  | ||||
|                     b.ToTable("thinking_thoughts", (string)null); | ||||
|                 }); | ||||
|  | ||||
|             modelBuilder.Entity("DysonNetwork.Shared.Models.SnThinkingThought", b => | ||||
|                 { | ||||
|                     b.HasOne("DysonNetwork.Shared.Models.SnThinkingSequence", "Sequence") | ||||
|                         .WithMany() | ||||
|                         .HasForeignKey("SequenceId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_thinking_thoughts_thinking_sequences_sequence_id"); | ||||
|  | ||||
|                     b.Navigation("Sequence"); | ||||
|                 }); | ||||
| #pragma warning restore 612, 618 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| using DysonNetwork.Insight; | ||||
| using DysonNetwork.Insight.Startup; | ||||
| using DysonNetwork.Shared.Auth; | ||||
| using DysonNetwork.Shared.Http; | ||||
| using DysonNetwork.Shared.Registry; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| @@ -16,6 +17,7 @@ builder.Services.AddAppAuthentication(); | ||||
| builder.Services.AddAppFlushHandlers(); | ||||
| builder.Services.AddAppBusinessServices(); | ||||
|  | ||||
| builder.Services.AddDysonAuth(); | ||||
| builder.Services.AddAccountService(); | ||||
| builder.Services.AddSphereService(); | ||||
| builder.Services.AddThinkingServices(builder.Configuration); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using DysonNetwork.Insight.Thinking; | ||||
| using DysonNetwork.Insight.Thought; | ||||
| using DysonNetwork.Shared.Cache; | ||||
| using Microsoft.SemanticKernel; | ||||
| using NodaTime; | ||||
| @@ -65,7 +65,8 @@ public static class ServiceCollectionExtensions | ||||
|  | ||||
|     public static IServiceCollection AddThinkingServices(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSingleton<ThinkingProvider>(); | ||||
|         services.AddSingleton<ThoughtProvider>(); | ||||
|         services.AddScoped<ThoughtService>(); | ||||
|  | ||||
|         return services; | ||||
|     } | ||||
|   | ||||
| @@ -1,68 +0,0 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.SemanticKernel.ChatCompletion; | ||||
| using System.Text; | ||||
| using Microsoft.SemanticKernel; | ||||
| using Microsoft.SemanticKernel.Connectors.Ollama; | ||||
|  | ||||
| namespace DysonNetwork.Insight.Thinking; | ||||
|  | ||||
| [ApiController] | ||||
| [Route("/api/thinking")] | ||||
| public class ThinkingController(ThinkingProvider provider) : ControllerBase | ||||
| { | ||||
|     public class StreamThinkingRequest | ||||
|     { | ||||
|         [Required] public string UserMessage { get; set; } = null!; | ||||
|     } | ||||
|  | ||||
|     [HttpPost("stream")] | ||||
|     public async Task ChatStream([FromBody] StreamThinkingRequest request) | ||||
|     { | ||||
|         // Set response for streaming | ||||
|         Response.Headers.Append("Content-Type", "text/event-stream"); | ||||
|         Response.StatusCode = 200; | ||||
|  | ||||
|         var kernel = provider.Kernel; | ||||
|  | ||||
|         var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); | ||||
|  | ||||
|         var chatHistory = new ChatHistory( | ||||
|             "You're a helpful assistant on the Solar Network, a social network.\n" + | ||||
|             "Your name is Sn-chan, a cute sweet heart with passion for almost everything.\n" + | ||||
|             "\n" + | ||||
|             "Your aim is to helping solving questions for the users on the Solar Network.\n" + | ||||
|             "And the Solar Network is the social network platform you live on.\n" + | ||||
|             "When the user ask questions about the Solar Network (also known as SN and Solian), try use the tools you have to get latest and accurate data." | ||||
|         ); | ||||
|         chatHistory.AddUserMessage(request.UserMessage); | ||||
|  | ||||
|         // Kick off streaming generation | ||||
|         var accumulatedContent = new StringBuilder(); | ||||
|         await foreach (var chunk in chatCompletionService.GetStreamingChatMessageContentsAsync( | ||||
|                            chatHistory, | ||||
|                            new OllamaPromptExecutionSettings | ||||
|                            { | ||||
|                                FunctionChoiceBehavior = FunctionChoiceBehavior.Auto( | ||||
|                                    options: new FunctionChoiceBehaviorOptions() | ||||
|                                    { | ||||
|                                        AllowParallelCalls = true, | ||||
|                                        AllowConcurrentInvocation = true | ||||
|                                    }) | ||||
|                            }, | ||||
|                            kernel: kernel | ||||
|                        )) | ||||
|         { | ||||
|             // Write each chunk to the HTTP response as SSE | ||||
|             var data = chunk.Content ?? ""; | ||||
|             accumulatedContent.Append(data); | ||||
|             if (string.IsNullOrEmpty(data)) continue; | ||||
|  | ||||
|             var bytes = Encoding.UTF8.GetBytes(data); | ||||
|             await Response.Body.WriteAsync(bytes); | ||||
|             await Response.Body.FlushAsync(); | ||||
|         } | ||||
|  | ||||
|         // Optionally: after finishing streaming, you can save the assistant message to history. | ||||
|     } | ||||
| } | ||||
							
								
								
									
										137
									
								
								DysonNetwork.Insight/Thought/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								DysonNetwork.Insight/Thought/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| # DysonNetwork Insight Thought API | ||||
|  | ||||
| The Thought API provides conversational AI capabilities for users of the Solar Network. It allows users to engage in chat-like conversations with an AI assistant powered by semantic kernel and connected to various tools. | ||||
|  | ||||
| This service is handled by the Insight, when using with the Gateway, the `/api` should be replaced with `/insight` | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| - Streaming chat responses using Server-Sent Events (SSE) | ||||
| - Conversation context management with sequences | ||||
| - Caching for improved performance | ||||
| - Authentication required for all operations | ||||
|  | ||||
| ## Endpoints | ||||
|  | ||||
| ### POST /api/thought | ||||
|  | ||||
| Initiates or continues a chat conversation. | ||||
|  | ||||
| #### Parameters | ||||
| - `UserMessage` (string, required): The message from the user | ||||
| - `SequenceId` (Guid, optional): ID of existing conversation sequence. If not provided, a new sequence is created. | ||||
|  | ||||
| #### Response | ||||
| - Content-Type: `text/event-stream` | ||||
| - Streaming response with assistant messages | ||||
| - Status: 401 if not authenticated | ||||
| - Status: 403 if sequence doesn't belong to user | ||||
|  | ||||
| #### Example Usage | ||||
| ```bash | ||||
| curl -X POST "http://localhost:5000/api/thought" \ | ||||
|   -H "Content-Type: application/json" \ | ||||
|   -d '{ | ||||
|     "UserMessage": "Hello, how can I help with the Solar Network?", | ||||
|     "SequenceId": null | ||||
|   }' | ||||
| ``` | ||||
|  | ||||
| ### GET /api/thought/sequences | ||||
|  | ||||
| Lists all thinking sequences for the authenticated user. | ||||
|  | ||||
| #### Parameters | ||||
| - `offset` (int, default 0): Number of sequences to skip for pagination | ||||
| - `take` (int, default 20): Maximum number of sequences to return | ||||
|  | ||||
| #### Response | ||||
| - `200 OK`: Array of `SnThinkingSequence` | ||||
| - `401 Unauthorized`: If not authenticated | ||||
| - Headers: | ||||
|   - `X-Total`: Total number of sequences before pagination | ||||
|  | ||||
| #### Example Usage | ||||
| ```bash | ||||
| curl -X GET "http://localhost:5000/api/thought/sequences?take=10" | ||||
| ``` | ||||
|  | ||||
| ### GET /api/thought/sequences/{sequenceId} | ||||
|  | ||||
| Retrieves all thoughts (messages) in a specific conversation sequence. | ||||
|  | ||||
| #### Parameters | ||||
| - `sequenceId` (Guid, path): ID of the sequence to retrieve | ||||
|  | ||||
| #### Response | ||||
| - `200 OK`: Array of `SnThinkingThought` ordered by creation date | ||||
| - `401 Unauthorized`: If not authenticated | ||||
| - `404 Not Found`: If sequence doesn't exist or doesn't belong to user | ||||
|  | ||||
| #### Example Usage | ||||
| ```bash | ||||
| curl -X GET "http://localhost:5000/api/thought/sequences/12345678-1234-1234-1234-123456789abc" | ||||
| ``` | ||||
|  | ||||
| ## Data Models | ||||
|  | ||||
| ### StreamThinkingRequest | ||||
| ```csharp | ||||
| { | ||||
|   string UserMessage, // Required | ||||
|   Guid? SequenceId    // Optional | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### SnThinkingSequence | ||||
| ```csharp | ||||
| { | ||||
|   Guid Id, | ||||
|   string? Topic, | ||||
|   Guid AccountId | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### SnThinkingThought | ||||
| ```csharp | ||||
| { | ||||
|   Guid Id, | ||||
|   string? Content, | ||||
|   List<SnCloudFileReferenceObject> Files, | ||||
|   ThinkingThoughtRole Role, | ||||
|   Guid SequenceId, | ||||
|   SnThinkingSequence Sequence | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### ThinkingThoughtRole (enum) | ||||
| - `Assistant` | ||||
| - `User` | ||||
|  | ||||
| ## Caching | ||||
|  | ||||
| The API uses Redis-based caching for conversation thoughts: | ||||
| - Thoughts are cached for 10 minutes with group-based invalidation | ||||
| - Cache is invalidated when new thoughts are added to a sequence | ||||
| - Improves performance for accessing conversation history | ||||
|  | ||||
| ## Authentication | ||||
|  | ||||
| All endpoints require authentication through the current user session. Sequence access is validated against the authenticated user's account ID. | ||||
|  | ||||
| ## Error Responses | ||||
|  | ||||
| - `401 Unauthorized`: Authentication required | ||||
| - `403 Forbidden`: Access denied (sequence ownership) | ||||
| - `404 Not Found`: Resource not found | ||||
|  | ||||
| ## Streaming Details | ||||
|  | ||||
| The POST endpoint returns a stream of assistant responses using Server-Sent Events format. Clients should handle the streaming response and display messages incrementally. | ||||
|  | ||||
| ## Implementation Notes | ||||
|  | ||||
| - Built with ASP.NET Core and Semantic Kernel | ||||
| - Uses PostgreSQL via Entity Framework Core | ||||
| - Integrated with Ollama for AI completion | ||||
| - Caching via Redis | ||||
							
								
								
									
										185
									
								
								DysonNetwork.Insight/Thought/ThoughtController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								DysonNetwork.Insight/Thought/ThoughtController.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using DysonNetwork.Shared.Proto; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.SemanticKernel; | ||||
| using Microsoft.SemanticKernel.ChatCompletion; | ||||
| using Microsoft.SemanticKernel.Connectors.Ollama; | ||||
|  | ||||
| namespace DysonNetwork.Insight.Thought; | ||||
|  | ||||
| [ApiController] | ||||
| [Route("/api/thought")] | ||||
| public class ThoughtController(ThoughtProvider provider, ThoughtService service) : ControllerBase | ||||
| { | ||||
|     public class StreamThinkingRequest | ||||
|     { | ||||
|         [Required] public string UserMessage { get; set; } = null!; | ||||
|         public Guid? SequenceId { get; set; } | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     public async Task<ActionResult> Think([FromBody] StreamThinkingRequest request) | ||||
|     { | ||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||
|         var accountId = Guid.Parse(currentUser.Id); | ||||
|  | ||||
|         // Generate topic if creating new sequence | ||||
|         string? topic = null; | ||||
|         if (!request.SequenceId.HasValue) | ||||
|         { | ||||
|             // Use AI to summarize topic from user message | ||||
|             var summaryHistory = new ChatHistory( | ||||
|                 "You are a helpful assistant. Summarize the following user message into a concise topic title (max 100 characters)." | ||||
|             ); | ||||
|             summaryHistory.AddUserMessage(request.UserMessage); | ||||
|  | ||||
|             var summaryResult = await provider.Kernel.GetRequiredService<IChatCompletionService>() | ||||
|                 .GetChatMessageContentAsync(summaryHistory); | ||||
|  | ||||
|             topic = summaryResult.Content?.Substring(0, Math.Min(summaryResult.Content.Length, 4096)); | ||||
|         } | ||||
|  | ||||
|         // Handle sequence | ||||
|         var sequence = await service.GetOrCreateSequenceAsync(accountId, request.SequenceId, topic); | ||||
|         if (sequence == null) return Forbid(); // or NotFound | ||||
|  | ||||
|         // Save user thought | ||||
|         await service.SaveThoughtAsync(sequence, request.UserMessage, ThinkingThoughtRole.User); | ||||
|  | ||||
|         // Build chat history | ||||
|         var chatHistory = new ChatHistory( | ||||
|             "You're a helpful assistant on the Solar Network, a social network.\n" + | ||||
|             "Your name is Sn-chan, a cute sweet heart with passion for almost everything.\n" + | ||||
|             "When you talk to user, you can add some modal particles and emoticons to your response to be cute, but prevent use a lot of emojis." + | ||||
|             "\n" + | ||||
|             "Your aim is to helping solving questions for the users on the Solar Network.\n" + | ||||
|             "And the Solar Network is the social network platform you live on.\n" + | ||||
|             "When the user asks questions about the Solar Network (also known as SN and Solian), try use the tools you have to get latest and accurate data." | ||||
|         ); | ||||
|  | ||||
|         // Add previous thoughts (excluding the current user thought, which is the last one) | ||||
|         var previousThoughts = await service.GetPreviousThoughtsAsync(sequence); | ||||
|         var count = previousThoughts.Count; | ||||
|         for (var i = 0; i < count - 1; i++) | ||||
|         { | ||||
|             var thought = previousThoughts[i]; | ||||
|             switch (thought.Role) | ||||
|             { | ||||
|                 case ThinkingThoughtRole.User: | ||||
|                     chatHistory.AddUserMessage(thought.Content ?? ""); | ||||
|                     break; | ||||
|                 case ThinkingThoughtRole.Assistant: | ||||
|                     chatHistory.AddAssistantMessage(thought.Content ?? ""); | ||||
|                     break; | ||||
|                 default: | ||||
|                     throw new ArgumentOutOfRangeException(); | ||||
|             } | ||||
|         } | ||||
|         chatHistory.AddUserMessage(request.UserMessage); | ||||
|  | ||||
|         // Set response for streaming | ||||
|         Response.Headers.Append("Content-Type", "text/event-stream"); | ||||
|         Response.StatusCode = 200; | ||||
|  | ||||
|         var kernel = provider.Kernel; | ||||
|         var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); | ||||
|  | ||||
|         // Kick off streaming generation | ||||
|         var accumulatedContent = new StringBuilder(); | ||||
|         await foreach (var chunk in chatCompletionService.GetStreamingChatMessageContentsAsync( | ||||
|                            chatHistory, | ||||
|                            new OllamaPromptExecutionSettings | ||||
|                            { | ||||
|                                FunctionChoiceBehavior = FunctionChoiceBehavior.Auto( | ||||
|                                    options: new FunctionChoiceBehaviorOptions() | ||||
|                                    { | ||||
|                                        AllowParallelCalls = true, | ||||
|                                        AllowConcurrentInvocation = true | ||||
|                                    }) | ||||
|                            }, | ||||
|                            kernel: kernel | ||||
|                        )) | ||||
|         { | ||||
|             // Write each chunk to the HTTP response as SSE | ||||
|             var data = chunk.Content ?? ""; | ||||
|             accumulatedContent.Append(data); | ||||
|             if (string.IsNullOrEmpty(data)) continue; | ||||
|  | ||||
|             var bytes = Encoding.UTF8.GetBytes(data); | ||||
|             await Response.Body.WriteAsync(bytes); | ||||
|             await Response.Body.FlushAsync(); | ||||
|         } | ||||
|  | ||||
|         // Save assistant thought | ||||
|         var savedThought = await service.SaveThoughtAsync(sequence, accumulatedContent.ToString(), ThinkingThoughtRole.Assistant); | ||||
|  | ||||
|         // Write the topic if it was newly set, then the thought object as JSON to the stream | ||||
|         using (var streamBuilder = new MemoryStream()) | ||||
|         { | ||||
|             await streamBuilder.WriteAsync("\n"u8.ToArray()); | ||||
|             if (topic != null) | ||||
|             { | ||||
|                 await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"<topic>{sequence.Topic ?? ""}</topic>\n")); | ||||
|             } | ||||
|             await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(savedThought, GrpcTypeHelper.SerializerOptions))); | ||||
|             var outputBytes = streamBuilder.ToArray(); | ||||
|             await Response.Body.WriteAsync(outputBytes); | ||||
|             await Response.Body.FlushAsync(); | ||||
|         } | ||||
|  | ||||
|         // Return empty result since we're streaming | ||||
|         return new EmptyResult(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Retrieves a paginated list of thinking sequences for the authenticated user. | ||||
|     /// </summary> | ||||
|     /// <param name="offset">The number of sequences to skip for pagination.</param> | ||||
|     /// <param name="take">The maximum number of sequences to return (default: 20).</param> | ||||
|     /// <returns> | ||||
|     /// Returns an ActionResult containing a list of thinking sequences. | ||||
|     /// Includes an X-Total header with the total count of sequences before pagination. | ||||
|     /// </returns> | ||||
|     [HttpGet("sequences")] | ||||
|     [ProducesResponseType(StatusCodes.Status200OK)] | ||||
|     public async Task<ActionResult<List<SnThinkingSequence>>> ListSequences( | ||||
|         [FromQuery] int offset = 0, | ||||
|         [FromQuery] int take = 20 | ||||
|     ) | ||||
|     { | ||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||
|         var accountId = Guid.Parse(currentUser.Id); | ||||
|  | ||||
|         var (totalCount, sequences) = await service.ListSequencesAsync(accountId, offset, take); | ||||
|  | ||||
|         Response.Headers["X-Total"] = totalCount.ToString(); | ||||
|  | ||||
|         return Ok(sequences); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Retrieves the thoughts in a specific thinking sequence. | ||||
|     /// </summary> | ||||
|     /// <param name="sequenceId">The ID of the sequence to retrieve thoughts from.</param> | ||||
|     /// <returns> | ||||
|     /// Returns an ActionResult containing a list of thoughts in the sequence, ordered by creation date. | ||||
|     /// </returns> | ||||
|     [HttpGet("sequences/{sequenceId:guid}")] | ||||
|     [ProducesResponseType(StatusCodes.Status200OK)] | ||||
|     [ProducesResponseType(StatusCodes.Status404NotFound)] | ||||
|     public async Task<ActionResult<List<SnThinkingThought>>> GetSequenceThoughts(Guid sequenceId) | ||||
|     { | ||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||
|         var accountId = Guid.Parse(currentUser.Id); | ||||
|  | ||||
|         var sequence = await service.GetOrCreateSequenceAsync(accountId, sequenceId); | ||||
|         if (sequence == null) return NotFound(); | ||||
|  | ||||
|         var thoughts = await service.GetPreviousThoughtsAsync(sequence); | ||||
|  | ||||
|         return Ok(thoughts); | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,10 @@ | ||||
| using System.Text.Json; | ||||
| using DysonNetwork.Shared.Proto; | ||||
| using Microsoft.SemanticKernel; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using System.Text.Json; | ||||
| 
 | ||||
| namespace DysonNetwork.Insight.Thinking; | ||||
| namespace DysonNetwork.Insight.Thought; | ||||
| 
 | ||||
| public class ThinkingProvider | ||||
| public class ThoughtProvider | ||||
| { | ||||
|     private readonly Kernel _kernel; | ||||
|     private readonly PostService.PostServiceClient _postClient; | ||||
| @@ -15,7 +14,7 @@ public class ThinkingProvider | ||||
|     public string? ModelProviderType { get; private set; } | ||||
|     public string? ModelDefault { get; private set; } | ||||
| 
 | ||||
|     public ThinkingProvider( | ||||
|     public ThoughtProvider( | ||||
|         IConfiguration configuration, | ||||
|         PostService.PostServiceClient postClient, | ||||
|         AccountService.AccountServiceClient accountClient | ||||
							
								
								
									
										75
									
								
								DysonNetwork.Insight/Thought/ThoughtService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								DysonNetwork.Insight/Thought/ThoughtService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| using DysonNetwork.Shared.Cache; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
|  | ||||
| namespace DysonNetwork.Insight.Thought; | ||||
|  | ||||
| public class ThoughtService(AppDatabase db, ICacheService cache) | ||||
| { | ||||
|     public async Task<SnThinkingSequence?> GetOrCreateSequenceAsync(Guid accountId, Guid? sequenceId, string? topic = null) | ||||
|     { | ||||
|         if (sequenceId.HasValue) | ||||
|         { | ||||
|             var seq = await db.ThinkingSequences.FindAsync(sequenceId.Value); | ||||
|             if (seq == null || seq.AccountId != accountId) return null; | ||||
|             return seq; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             var seq = new SnThinkingSequence { AccountId = accountId, Topic = topic }; | ||||
|             db.ThinkingSequences.Add(seq); | ||||
|             await db.SaveChangesAsync(); | ||||
|             return seq; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<SnThinkingThought> SaveThoughtAsync(SnThinkingSequence sequence, string content, ThinkingThoughtRole role) | ||||
|     { | ||||
|         var thought = new SnThinkingThought | ||||
|         { | ||||
|             SequenceId = sequence.Id, | ||||
|             Content = content, | ||||
|             Role = role | ||||
|         }; | ||||
|         db.ThinkingThoughts.Add(thought); | ||||
|         await db.SaveChangesAsync(); | ||||
|  | ||||
|         // Invalidate cache for this sequence's thoughts | ||||
|         await cache.RemoveGroupAsync($"sequence:{sequence.Id}"); | ||||
|  | ||||
|         return thought; | ||||
|     } | ||||
|  | ||||
|     public async Task<List<SnThinkingThought>> GetPreviousThoughtsAsync(SnThinkingSequence sequence) | ||||
|     { | ||||
|         var cacheKey = $"thoughts:{sequence.Id}"; | ||||
|         var (found, cachedThoughts) = await cache.GetAsyncWithStatus<List<SnThinkingThought>>(cacheKey); | ||||
|         if (found && cachedThoughts != null) | ||||
|         { | ||||
|             return cachedThoughts; | ||||
|         } | ||||
|  | ||||
|         var thoughts = await db.ThinkingThoughts | ||||
|             .Where(t => t.SequenceId == sequence.Id) | ||||
|             .OrderBy(t => t.CreatedAt) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         // Cache for 10 minutes | ||||
|         await cache.SetWithGroupsAsync(cacheKey, thoughts, new[] { $"sequence:{sequence.Id}" }, TimeSpan.FromMinutes(10)); | ||||
|  | ||||
|         return thoughts; | ||||
|     } | ||||
|  | ||||
|     public async Task<(int total, List<SnThinkingSequence> sequences)> ListSequencesAsync(Guid accountId, int offset, int take) | ||||
|     { | ||||
|         var query = db.ThinkingSequences.Where(s => s.AccountId == accountId); | ||||
|         var totalCount = await query.CountAsync(); | ||||
|         var sequences = await query | ||||
|             .OrderByDescending(s => s.CreatedAt) | ||||
|             .Skip(offset) | ||||
|             .Take(take) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         return (totalCount, sequences); | ||||
|     } | ||||
| } | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="Grpc.AspNetCore.Server" Version="2.71.0"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|         </PackageReference> | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|         <PackageReference Include="Grpc.AspNetCore.Server" Version="2.71.0" /> | ||||
|         <PackageReference Include="MailKit" Version="4.13.0" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|           <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|           <PrivateAssets>all</PrivateAssets> | ||||
|         </PackageReference> | ||||
|   | ||||
							
								
								
									
										32
									
								
								DysonNetwork.Shared/Models/ThinkingSequence.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								DysonNetwork.Shared/Models/ThinkingSequence.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.ComponentModel.DataAnnotations.Schema; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace DysonNetwork.Shared.Models; | ||||
|  | ||||
| public class SnThinkingSequence : ModelBase | ||||
| { | ||||
|     public Guid Id { get; set; } = Guid.NewGuid(); | ||||
|     [MaxLength(4096)] public string? Topic { get; set; } | ||||
|  | ||||
|     public Guid AccountId { get; set; } | ||||
| } | ||||
|  | ||||
| public enum ThinkingThoughtRole | ||||
| { | ||||
|     Assistant, | ||||
|     User | ||||
| } | ||||
|  | ||||
| public class SnThinkingThought : ModelBase | ||||
| { | ||||
|     public Guid Id { get; set; } = Guid.NewGuid(); | ||||
|     public string? Content { get; set; } | ||||
|  | ||||
|     [Column(TypeName = "jsonb")] public List<SnCloudFileReferenceObject> Files { get; set; } = []; | ||||
|  | ||||
|     public ThinkingThoughtRole Role { get; set; } | ||||
|  | ||||
|     public Guid SequenceId { get; set; } | ||||
|     [JsonIgnore] public SnThinkingSequence Sequence { get; set; } = null!; | ||||
| } | ||||
| @@ -21,7 +21,7 @@ | ||||
|         <PackageReference Include="Markdig" Version="0.41.3"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" /> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10"> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|         </PackageReference> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user