diff --git a/BLAIzor.csproj b/BLAIzor.csproj
index 2a947ee..35ed0f9 100644
--- a/BLAIzor.csproj
+++ b/BLAIzor.csproj
@@ -63,6 +63,9 @@
Always
+
+ Always
+
Always
diff --git a/Components/Pages/AgentTest.razor b/Components/Pages/AgentTest.razor
new file mode 100644
index 0000000..130cff1
--- /dev/null
+++ b/Components/Pages/AgentTest.razor
@@ -0,0 +1,33 @@
+@page "/agent"
+@using System.Text.Json
+@using System.Text
+@inject HttpClient Http
+@inject IJSRuntime JS
+@inject IConfiguration _configuration
+
+
+
@@ -119,48 +136,7 @@
.catch(error => console.error(error));
}
- var mediaRecorder;
- var audioChunks = [];
- var recButton = document.getElementById("recButton");
- var stopButton = document.getElementById("stopButton");
- var recText = document.getElementById("recordingText");
- async function startRecording() {
- if (recButton) {
- recButton.hidden = true;
- stopButton.hidden = false;
- recText.textContent = "recording...";
- }
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
- mediaRecorder = new MediaRecorder(stream);
-
- mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
- mediaRecorder.onstop = () => {
- const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
- audioChunks = [];
- console.log("media stopped");
- const reader = new FileReader();
- reader.onload = () => {
- const base64Audio = reader.result.split(',')[1];
- DotNet.invokeMethodAsync('BLAIzor', 'ProcessAudio3', base64Audio, sessionId);
- };
- reader.readAsDataURL(audioBlob);
- };
-
- mediaRecorder.start();
- }
-
- function stopRecording() {
- mediaRecorder.stop();
- var recButton = document.getElementById("recButton");
- var stopButton = document.getElementById("stopButton");
- var recText = document.getElementById("recordingText");
- mediaRecorder.stream.getTracks().forEach(track => track.stop()); // Free memory
- if (stopButton) {
- stopButton.hidden = true;
- recButton.hidden = false;
- recText.textContent = "";
- }
- }
+
@code {
[Parameter]
- public string SelectedBrandName { get; set; }
+ public string SelectedBrandName { get; set; } = "default";
}
diff --git a/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.Designer.cs b/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.Designer.cs
new file mode 100644
index 0000000..9acb553
--- /dev/null
+++ b/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.Designer.cs
@@ -0,0 +1,628 @@
+//
+using System;
+using BLAIzor.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace BLAIzor.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20250523215717_SiteDescriptionAndSpeechManagement")]
+ partial class SiteDescriptionAndSpeechManagement
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.3")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
+ {
+ b.Property
("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CssContent")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DesignTemplateId")
+ .HasColumnType("int");
+
+ b.Property("LastUpdated")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DesignTemplateId")
+ .IsUnique();
+
+ b.ToTable("CssTemplates");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsDeprecated")
+ .HasColumnType("bit");
+
+ b.Property("IsPrivate")
+ .HasColumnType("bit");
+
+ b.Property("IsPublished")
+ .HasColumnType("bit");
+
+ b.Property("QDrandCollectionName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Tags")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TemplateName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TemplatePhotoUrl")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Version")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("DesignTemplates");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
+ Description = "The default template",
+ IsDeprecated = false,
+ IsPrivate = false,
+ IsPublished = false,
+ QDrandCollectionName = "html_snippets",
+ Status = "Draft",
+ Tags = "system",
+ TemplateName = "Default Site",
+ TemplatePhotoUrl = "/images/default-logo.png",
+ UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
+ Version = 1
+ });
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("JsonDefinition")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SiteInfoId")
+ .HasColumnType("int");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("Version")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SiteInfoId", "Slug")
+ .IsUnique();
+
+ b.ToTable("FormDefinitions");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PointId")
+ .HasColumnType("int");
+
+ b.Property("QdrantPointId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ShowInMainMenu")
+ .HasColumnType("bit");
+
+ b.Property("SiteInfoId")
+ .HasColumnType("int");
+
+ b.Property("SortOrder")
+ .HasColumnType("int");
+
+ b.Property("StoredHtml")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SiteInfoId");
+
+ b.ToTable("MenuItems");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("BrandLogoUrl")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DefaultColor")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DefaultUrl")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DomainUrl")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsPublished")
+ .HasColumnType("bit");
+
+ b.Property("STTActive")
+ .HasColumnType("bit");
+
+ b.Property("SiteDescription")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SiteName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TTSActive")
+ .HasColumnType("bit");
+
+ b.Property("TemplateId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TemplateId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("SiteInfos");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ BrandLogoUrl = "/images/default-logo.png",
+ DefaultColor = "#FFFFFF",
+ DefaultUrl = "https://ai.poppixel.cloud",
+ DomainUrl = "poppixel.cloud",
+ IsPublished = false,
+ STTActive = false,
+ SiteName = "Default Site",
+ TTSActive = false,
+ TemplateId = 1,
+ UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
+ });
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex")
+ .HasFilter("[NormalizedName] IS NOT NULL");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("bit");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("PasswordHash")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("bit");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex")
+ .HasFilter("[NormalizedUserName] IS NOT NULL");
+
+ b.ToTable("AspNetUsers", (string)null);
+
+ b.HasData(
+ new
+ {
+ Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
+ AccessFailedCount = 0,
+ ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
+ Email = "adam.g@aycode.com",
+ EmailConfirmed = true,
+ LockoutEnabled = false,
+ NormalizedEmail = "ADAM.G@AYCODE.COM",
+ NormalizedUserName = "ADAM.G@AYCODE.COM",
+ PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
+ PhoneNumberConfirmed = false,
+ SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
+ TwoFactorEnabled = false,
+ UserName = "adam.g@aycode.com"
+ });
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("nvarchar(128)");
+
+ b.Property("ProviderKey")
+ .HasMaxLength(128)
+ .HasColumnType("nvarchar(128)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("RoleId")
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("nvarchar(128)");
+
+ b.Property("Name")
+ .HasMaxLength(128)
+ .HasColumnType("nvarchar(128)");
+
+ b.Property("Value")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
+ {
+ b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
+ .WithOne("CssTemplate")
+ .HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("DesignTemplate");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
+ {
+ b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
+ .WithMany("FormDefinitions")
+ .HasForeignKey("SiteInfoId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("SiteInfo");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
+ {
+ b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
+ .WithMany("MenuItems")
+ .HasForeignKey("SiteInfoId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("SiteInfo");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
+ {
+ b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
+ .WithMany("Sites")
+ .HasForeignKey("TemplateId")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Template");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
+ {
+ b.Navigation("CssTemplate")
+ .IsRequired();
+
+ b.Navigation("Sites");
+ });
+
+ modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
+ {
+ b.Navigation("FormDefinitions");
+
+ b.Navigation("MenuItems");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.cs b/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.cs
new file mode 100644
index 0000000..1bad8d2
--- /dev/null
+++ b/Migrations/20250523215717_SiteDescriptionAndSpeechManagement.cs
@@ -0,0 +1,68 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace BLAIzor.Migrations
+{
+ ///
+ public partial class SiteDescriptionAndSpeechManagement : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "STTActive",
+ table: "SiteInfos",
+ type: "bit",
+ nullable: false,
+ defaultValue: false);
+
+ migrationBuilder.AddColumn(
+ name: "SiteDescription",
+ table: "SiteInfos",
+ type: "nvarchar(max)",
+ nullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "TTSActive",
+ table: "SiteInfos",
+ type: "bit",
+ nullable: false,
+ defaultValue: false);
+
+ migrationBuilder.AddColumn(
+ name: "StoredHtml",
+ table: "MenuItems",
+ type: "nvarchar(max)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.UpdateData(
+ table: "SiteInfos",
+ keyColumn: "Id",
+ keyValue: 1,
+ columns: new[] { "STTActive", "SiteDescription", "TTSActive" },
+ values: new object[] { false, null, false });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "STTActive",
+ table: "SiteInfos");
+
+ migrationBuilder.DropColumn(
+ name: "SiteDescription",
+ table: "SiteInfos");
+
+ migrationBuilder.DropColumn(
+ name: "TTSActive",
+ table: "SiteInfos");
+
+ migrationBuilder.DropColumn(
+ name: "StoredHtml",
+ table: "MenuItems");
+ }
+ }
+}
diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs
index 13e170e..65800f6 100644
--- a/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -192,6 +192,10 @@ namespace BLAIzor.Migrations
b.Property("SortOrder")
.HasColumnType("int");
+ b.Property("StoredHtml")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
b.HasKey("Id");
b.HasIndex("SiteInfoId");
@@ -224,9 +228,18 @@ namespace BLAIzor.Migrations
b.Property("IsPublished")
.HasColumnType("bit");
+ b.Property("STTActive")
+ .HasColumnType("bit");
+
+ b.Property("SiteDescription")
+ .HasColumnType("nvarchar(max)");
+
b.Property("SiteName")
.HasColumnType("nvarchar(max)");
+ b.Property("TTSActive")
+ .HasColumnType("bit");
+
b.Property("TemplateId")
.HasColumnType("int");
@@ -251,7 +264,9 @@ namespace BLAIzor.Migrations
DefaultUrl = "https://ai.poppixel.cloud",
DomainUrl = "poppixel.cloud",
IsPublished = false,
+ STTActive = false,
SiteName = "Default Site",
+ TTSActive = false,
TemplateId = 1,
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
});
diff --git a/Models/MenuItem.cs b/Models/MenuItem.cs
index 53e03ed..f2cc33e 100644
--- a/Models/MenuItem.cs
+++ b/Models/MenuItem.cs
@@ -17,6 +17,8 @@ namespace BLAIzor.Models
public bool ShowInMainMenu { get; set; } = false;
+ public string StoredHtml { get; set; }
+
public Guid? QdrantPointId { get; set; }
[ForeignKey("SiteInfo")]
diff --git a/Models/SiteInfo.cs b/Models/SiteInfo.cs
index 8bb7f2a..ac5ff6a 100644
--- a/Models/SiteInfo.cs
+++ b/Models/SiteInfo.cs
@@ -9,6 +9,7 @@ namespace BLAIzor.Models
public int Id { get; set; }
public string? SiteName { get; set; }
+ public string? SiteDescription { get; set; }
public string? BrandLogoUrl { get; set; }
@@ -24,6 +25,8 @@ namespace BLAIzor.Models
public string DefaultUrl { get; set; } // For generated subdomains
public bool IsPublished { get; set; } // For generated subdomains
public int? TemplateId { get; set; }
+ public bool TTSActive { get; set; } = false;
+ public bool STTActive { get; set; } = false;
// Navigation property for IdentityUser
public IdentityUser User { get; set; }
diff --git a/Services/AIService.cs b/Services/AIService.cs
index d08fec6..ccaaa36 100644
--- a/Services/AIService.cs
+++ b/Services/AIService.cs
@@ -62,6 +62,7 @@ namespace BLAIzor.Services
public static event Action? OnContentReceiveFinished;
public static event Action? OnContentReceivedError;
public static event Action? OnStatusChangeReceived;
+ public static event Action? OnTextContentAvailable;
public string Mood = "cool, and professional";
private string _workingContent = null;
public bool UseWebsocket = false;
@@ -124,6 +125,7 @@ namespace BLAIzor.Services
//"and generate a short" +Mood+ "but kind marketing-oriented welcome message and introduction of the brand for the user, constructed as simple Bootstrap HTML codeblock with a tagged title and a paragraph." +
"and generate a" + Mood + " marketing-oriented welcome message and a summary of the content and introduction of the brand for the user, aiming to explain clearly, what does the company/person offer, constructed as simple Bootstrap HTML codeblock with a tagged title and a paragraph." +
"If there is any logo, or not logo but main brand image in the document use that url, and add that as a bootstrap responsive ('img-fluid py-3') image, with the maximum height of 30vh." +
+ //"In the end of your answer, always add in a new row: 'If you have any questions, you can simply ask either by typing in the message box, or by clicking the microphone icon on the top of the page. '"+
"Here is a list of topics " + menuList +
", make a new bootstrap clearfix and after that make a clickable bootstrap styled (btn btn-primary) button from each of the determined topics, " +
"that calls the javascript function 'callAI({the name of the topic})' on click. " +
@@ -177,6 +179,7 @@ namespace BLAIzor.Services
var baseResult = await ValidateAndFixJson(resultJson, FixJsonWithAI);
//var baseResult = System.Text.Json.JsonSerializer.Deserialize(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var fixedResult = System.Text.Json.JsonSerializer.Serialize(baseResult);
if (baseResult == null)
{
@@ -194,15 +197,15 @@ namespace BLAIzor.Services
break;
case "textresult":
- await ProcessTextResult(sessionId, resultJson, templateId, collectionName);
+ await ProcessTextResult(sessionId, fixedResult, templateId, collectionName);
break;
case "examinationresult":
- await ProcessExaminationResult(sessionId, resultJson, templateId, collectionName);
+ await ProcessExaminationResult(sessionId, fixedResult, templateId, collectionName);
break;
case "errorresult":
- await ProcessErrorResult(sessionId, resultJson);
+ await ProcessErrorResult(sessionId, fixedResult);
break;
default:
@@ -243,6 +246,7 @@ namespace BLAIzor.Services
extractedText = _selectedPoint.result.payload.name + ": " + _selectedPoint.result.payload.content + ", ";
}
+
string contentJson = await GetContentFromQuery(sessionId,
"Enhance this text if needed, making its style and grammar suitable to be displayed as the content of a webpage",
extractedText,
@@ -263,7 +267,7 @@ namespace BLAIzor.Services
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(requestedMenu);
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
- var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 1);
+ var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 3);
if (pointId.Length > 0)
@@ -417,6 +421,9 @@ namespace BLAIzor.Services
//contentResult.Photos.Add("Clarification request", "https://www.reactiongifs.com/r/martin.gif");
//contentResult.Photos.Add("Compliment response", "https://www.reactiongifs.com/r/review.gif");
+ //We have the text all available now, let's pass it to the voice generator
+ OnTextContentAvailable?.Invoke(sessionId, fixedResult.Text);
+
string snippets = await GetSnippetsForDisplay(sessionId, templateId, fixedResult.Text, collectionName);
await DisplayHtml(sessionId, fixedResult.Text, "", "", snippets, fixedResult.Topics, fixedResult.Photos);
}
@@ -460,7 +467,7 @@ namespace BLAIzor.Services
string systemMessage = "";
if(forceUnmodified)
{
- systemMessage = "You are a helpful assistant of a website. Respond in the name of the brand or person in the content, strictly in " + _scopedContentService.SelectedLanguage + " with a plain JSON object in the following format:\r\n\r\n1. " +
+ systemMessage = "You are a helpful assistant of a website. Display the Content strictly in " + _scopedContentService.SelectedLanguage + " with a plain JSON object in the following format:\r\n\r\n1. " +
//"**chatGPTContentResult**:\r\n " +
"- `type`: A string with value `contentresult`.\r\n " +
"- `text`: A string with the actual response.\r\n " +
@@ -474,13 +481,14 @@ namespace BLAIzor.Services
"- Turn the following content into a nice informative webpage content.\r\n " +
"- Start with the page title.\r\n" +
"- Structure it nicely without leaving out any information.\r\n " +
- "*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
+ //"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
"**Style and Image Handling**:\r\n" +
"- Make sure the json is valid json." +
"- Do NOT include extraneous text outside the JSON structure.\r\n\r\n" +
"When you understand the input, follow these rules strictly. Otherwise, seek clarification.\r\n" +
"Do not include linebreaks or any formatting, just the plain json string. Make sure it is valid json, and every objects is closed properly" +
"Do NOT mark your answer with anything like `````json, and do not add any explanation.";
+ userPrompt = "Give me a formatted json from this content, without modifying the text: " + extractedText;
}
else
{
@@ -496,9 +504,9 @@ namespace BLAIzor.Services
"Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " +
"- Base a detailed, but not lengthy response solely on the initial document provided below. " +
- "- In your response, summarize all relevant information in the document, that is connected to the question." +
+ "- In your response, summarize ALL relevant information in the document, that is connected to the question." +
"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
- "- For missing information: Inform the user and provide a clarification. " +
+ "- For missing information: Inform the user and ask if you can help with something else. " +
//"- Do not generate lengthy answers." +
"- If the user prompt is clear and they ask specific, well defined question, do not add other infromation or welcome message." +
"- If the user prompt is unclear, or makes no sense, ask for clarification." +
@@ -721,7 +729,7 @@ namespace BLAIzor.Services
string systemMessage = $"You are a helpful assistant built in a website, trying to figure out what the User wants to do or know about.\r\n" +
"Your job is to classify the user's request into one of the following categories:\r\n" +
- "1. **Browse the website’s content** (Return a 'Text result')\r\n" +
+ "1. **Ask about or search infromation in the website’s content** (Return a 'Text result')\r\n" +
"2. **Analyze the currently displayed HTML content** (Return an 'Examination result')\r\n" +
"3. **Initiate an action** (Return a 'Method result')\r\n" +
"If none of the above applies, return an 'Error result'.\r\n\r\n" +
@@ -905,79 +913,7 @@ namespace BLAIzor.Services
else return "No snippets found, use bootstrap html components";
- }
-
- //public async Task DisplayHtmlOld(string sessionId, string interMediateResult, string methodToCall = "", string methodParameter = "", string htmlToUse = "", string[]? topics = null, Dictionary? photos = null)
- //{
- // Console.Write($"\n SessionId: {sessionId} \n");
- // OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
- // _apiKey = GetApiKey();
- // StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML responses in " + _scopedContentService.SelectedLanguage + " using Bootstrap. " +
- // "Rules to follow:" +
- // "- Based on the user's query and retrieved content, generate HTML that fits into a Bootstrap container. \r\n" +
- // "- Begin with an `` tag styled with the 'p-3' class, and include a title based on the user's query. \r\n" +
- // "- Use separate div element with `row` class for different logical sections." +
- // "- Do not include elements outside the container (e.g., ``, ``, or `