418 lines
19 KiB
C#
418 lines
19 KiB
C#
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using System.Threading.Tasks;
|
|
using BLAIzor.Models;
|
|
using DocumentFormat.OpenXml.Office2010.Excel;
|
|
using Google.Protobuf.Collections;
|
|
using Google.Type;
|
|
using Microsoft.CodeAnalysis.Completion;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Qdrant.Client.Grpc;
|
|
|
|
namespace BLAIzor.Services
|
|
{
|
|
|
|
|
|
public class HtmlSnippetProcessor
|
|
{
|
|
private readonly OpenAIEmbeddingService _openAIEmbeddingService;
|
|
private readonly LocalEmbeddingService _localEmbeddingService;
|
|
private readonly HttpClient _httpClient;
|
|
private readonly QDrantService _drantService;
|
|
public static IConfiguration? _configuration;
|
|
private string _qdrantApiKey;
|
|
|
|
public HtmlSnippetProcessor(QDrantService drantService, OpenAIEmbeddingService openAIEmbeddingService, LocalEmbeddingService localEmbeddingService, IConfiguration? configuration)
|
|
{
|
|
_drantService = drantService;
|
|
_openAIEmbeddingService = openAIEmbeddingService;
|
|
_localEmbeddingService = localEmbeddingService;
|
|
_httpClient = new HttpClient();
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public string GetApiKey()
|
|
{
|
|
return _configuration?.GetSection("QDrant")?.GetValue<string>("ApiKey") ?? string.Empty;
|
|
}
|
|
|
|
private string GetAiEmbeddingSettings() =>
|
|
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
|
|
|
|
//public async Task ProcessAndStoreSnippetsAsync()
|
|
//{
|
|
// _qdrantApiKey = GetApiKey();
|
|
|
|
// var snippets = GetHtmlSnippets();
|
|
|
|
// var ids = new List<int>();
|
|
// var vectors = new List<float[]>();
|
|
// var payloads = new List<MapField<string, Value>>();
|
|
|
|
// foreach (var snippet in snippets)
|
|
// {
|
|
// try
|
|
// {
|
|
// // Combine details to generate the embedding
|
|
// var combinedText = $"{snippet.Name}: {snippet.Description}";
|
|
// var embedding = await _embeddingService.GenerateEmbeddingAsync(combinedText);
|
|
|
|
// // Add data for batch insertion
|
|
// ids.Add(snippet.Id);
|
|
// vectors.Add(embedding);
|
|
// payloads.Add(new MapField<string, Value>
|
|
// {
|
|
// ["type"] = snippet.Type,
|
|
// ["name"] = snippet.Name,
|
|
// ["description"] = snippet.Description,
|
|
// ["html"] = snippet.Html
|
|
// });
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// Console.WriteLine($"Error processing snippet {snippet.Name}: {ex.Message}");
|
|
// }
|
|
// }
|
|
|
|
// if (ids.Count > 0)
|
|
// {
|
|
// await _drantService.QDrantInsertTest(ids, vectors, payloads);
|
|
// }
|
|
// else
|
|
// {
|
|
// Console.WriteLine("No points were processed successfully.");
|
|
// }
|
|
//}
|
|
|
|
public async Task ProcessAndStoreTemplateSnippetAsync(string TemplateName, List<HtmlSnippet> snippets)
|
|
{
|
|
_qdrantApiKey = GetApiKey();
|
|
|
|
//var snippets = GetHtmlSnippets();
|
|
|
|
var ids = new List<int>();
|
|
var vectors = new List<float[]>();
|
|
var payloads = new List<MapField<string, Value>>();
|
|
|
|
foreach (var snippet in snippets)
|
|
{
|
|
try
|
|
{
|
|
|
|
// Combine details to generate the embedding
|
|
var combinedText = $"{snippet.Name}: {snippet.Description}. " +
|
|
$"Type: {snippet.Type}. " +
|
|
$"Variant: {snippet.Variant ?? "default"}. " +
|
|
$"Tags: {snippet.Tags}. " +
|
|
$"HTML: {snippet.Html}";
|
|
|
|
float[] embedding = [];
|
|
|
|
var embeddingServiceProvider = GetAiEmbeddingSettings();
|
|
//if (embeddingServiceProvider == "local")
|
|
//{
|
|
// embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
//}
|
|
//else
|
|
//{
|
|
// embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
//}
|
|
embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
|
|
|
|
// Add data for batch insertion
|
|
ids.Add(snippet.Id);
|
|
vectors.Add(embedding);
|
|
payloads.Add(new MapField<string, Value>
|
|
{
|
|
["type"] = snippet.Type,
|
|
["name"] = snippet.Name,
|
|
["variant"] = string.IsNullOrWhiteSpace(snippet.Variant) ? "" : snippet.Variant,
|
|
["tags"] = snippet.Tags,
|
|
["description"] = snippet.Description,
|
|
["html"] = snippet.Html,
|
|
["sampleHtml"] = snippet.SampleHtml
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error processing snippet {snippet.Name}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
if (ids.Count > 0)
|
|
{
|
|
await _drantService.QDrantInsertManyAsync(ids, vectors, payloads, TemplateName);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("No points were processed successfully.");
|
|
}
|
|
}
|
|
|
|
//public async Task ProcessAndStoreWebContentsAsync(List<WebPageContent> pageContentList, int siteId)
|
|
//{
|
|
// _qdrantApiKey = GetApiKey();
|
|
|
|
// var ids = new List<int>();
|
|
// var vectors = new List<float[]>();
|
|
// var payloads = new List<MapField<string, Value>>();
|
|
|
|
// foreach (var content in pageContentList)
|
|
// {
|
|
// try
|
|
// {
|
|
// // Combine details to generate the embedding
|
|
// var combinedText = $"{content.Name}: Description: {content.Description}, complete text: {content.Content}";
|
|
|
|
// float[] embedding = [];
|
|
|
|
// var embeddingServiceProvider = GetAiEmbeddingSettings();
|
|
// if (embeddingServiceProvider == "local")
|
|
// {
|
|
// embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
// }
|
|
// else
|
|
// {
|
|
// embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
// }
|
|
|
|
// // Add data for batch insertion
|
|
// ids.Add(content.Id);
|
|
// vectors.Add(embedding);
|
|
|
|
|
|
// payloads.Add(new MapField<string, Value>
|
|
// {
|
|
// ["uid"] = content.UId,
|
|
// ["type"] = content.Type,
|
|
// ["siteId"] = content.SiteId,
|
|
// //["menuItemId"] = content.MenuItemId,
|
|
// ["name"] = content.Name,
|
|
// ["description"] = content.Description,
|
|
// ["content"] = content.Content,
|
|
// ["lastUpdated"] = content.LastUpdated.ToString()
|
|
// });
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// Console.WriteLine($"Error processing content {content.Name}: {ex.Message}");
|
|
// }
|
|
// }
|
|
|
|
// if (ids.Count > 0)
|
|
// {
|
|
// await _drantService.QDrantInsertManyAsync(ids, vectors, payloads, "Site" + siteId);
|
|
// }
|
|
// else
|
|
// {
|
|
// Console.WriteLine("No points were processed successfully.");
|
|
// }
|
|
//}
|
|
|
|
public async Task ProcessAndStoreWebContentAsync(PointId id, WebPageContent pageContent, int siteId)
|
|
{
|
|
_qdrantApiKey = GetApiKey();
|
|
|
|
float[] vectors = [];
|
|
var payload = new MapField<string, Value>();
|
|
|
|
try
|
|
{
|
|
// Combine details to generate the embedding
|
|
var combinedText = $"{pageContent.Name}: {pageContent.Description} - {pageContent.Content}";
|
|
float[] embedding = [];
|
|
|
|
var embeddingServiceProvider = GetAiEmbeddingSettings();
|
|
if (embeddingServiceProvider == "local")
|
|
{
|
|
embedding = await _localEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
}
|
|
else
|
|
{
|
|
embedding = await _openAIEmbeddingService.GenerateEmbeddingAsync(combinedText);
|
|
}
|
|
|
|
// Add data for batch insertion
|
|
|
|
vectors = embedding;
|
|
|
|
|
|
payload = new MapField<string, Value>
|
|
{
|
|
["uid"] = pageContent.UId,
|
|
["type"] = pageContent.Type,
|
|
["siteId"] = pageContent.SiteId,
|
|
//["menuItemId"] = pageContent.MenuItemId,
|
|
["name"] = pageContent.Name,
|
|
["description"] = pageContent.Description,
|
|
["content"] = pageContent.Content,
|
|
["lastUpdated"] = pageContent.LastUpdated.ToString()
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error processing content {pageContent.Name}: {ex.Message}");
|
|
}
|
|
|
|
|
|
await _drantService.QDrantInsertPointAsync(id, vectors, payload, "Site" + siteId);
|
|
|
|
}
|
|
|
|
private List<HtmlSnippet> GetHtmlSnippets()
|
|
{
|
|
return new List<HtmlSnippet>
|
|
{
|
|
new HtmlSnippet
|
|
{
|
|
Id = 1,
|
|
Type = "hero",
|
|
Name = "Hero Layout",
|
|
Description = "A Bootstrap hero section with an image and text.",
|
|
Html = "<div class=\"row flex-lg-row-reverse align-items-center g-5 py-5\">" +
|
|
"<div class=\"col-10 col-sm-8 col-lg-6\">" +
|
|
"<img src=\"{photo url}\" class=\"d-block mx-lg-auto img-fluid\" alt=\"{photo file name}\" width=\"700\" height=\"500\" loading=\"lazy\">" +
|
|
"</div><div class=\"col-lg-6\">" +
|
|
"<h1 class=\"display-5 fw-bold lh-1 mb-3\">{Title}</h1>" +
|
|
"<p class=\"lead\">{lead}</p>" +
|
|
"<div class=\"d-grid gap-2 d-md-flex justify-content-md-start\">" +
|
|
"<p>{paragraph}</p></div></div></div>"
|
|
},
|
|
new HtmlSnippet
|
|
{
|
|
Id = 2,
|
|
Type = "product_card",
|
|
Name = "Product Card",
|
|
Description = "A responsive product card layout using Bootstrap.",
|
|
Html = "<div class=\"animate__animated animate__bounce card\">" +
|
|
"<div class=\"container mt-3\">" +
|
|
"<img src=\"{image url}\" class=\"card-img-top\" alt=\"{image file name}\"></div>" +
|
|
"<div class=\"card-body\">" +
|
|
"<h5 class=\"card-title ms-1\">{Product title}</h5>" +
|
|
"<p class=\"card-text mb-5 ms-1\">{description}</p>" +
|
|
"<a href=\"{url}\" target=\"_blank\" class=\"btn btn-primary mb-1 mt-1\">{button text}</a>" +
|
|
"</div></div>"
|
|
},
|
|
new HtmlSnippet
|
|
{
|
|
Id = 3,
|
|
Type = "table_general",
|
|
Name = "General Table",
|
|
Description = "A responsive table to be used for displaying information in columns and rows",
|
|
Html = "<table class=\"table\">" +
|
|
"<thead><tr><th scope=\"col\">#</th><th scope=\"col\">First</th><th scope=\"col\">Last</th><th scope=\"col\">Handle</th>" +
|
|
"</tr>" +
|
|
"</thead><tbody><tr><th scope=\"row\">{cell content 1}</th><td>{cell content 2}</td><td>{cell content 3}</td><td>{cell content 3}</td></tr>" +
|
|
"</tbody>" +
|
|
"</table>"
|
|
|
|
|
|
},
|
|
new HtmlSnippet
|
|
{
|
|
Id = 4,
|
|
Type = "team_member",
|
|
Name = "Team member",
|
|
Description = "A responsive card to display employees, leaders, other team members",
|
|
Html = "<div class=\"animate__animated animate__bounce card py-3\">" +
|
|
"<div class=\"container mt-3\">" +
|
|
"<img src=\"{image url}\" class=\"card-img-top img-fluid p-3\" alt=\"{image file name}\"></div>" +
|
|
"<div class=\"card-body\">" +
|
|
"<h5 class=\"card-title ms-1\">{Person's name}</h5>" +
|
|
"<p class=\"card-text mb-5 ms-1\">{description}</p>" +
|
|
"</div></div>"
|
|
|
|
},
|
|
new HtmlSnippet
|
|
{
|
|
Id = 5,
|
|
Type = "music_player",
|
|
Name = "Music player",
|
|
Description = "A responsive music player to play a list of mp3 songs",
|
|
Html = "<div class=\"container mt-5\">" +
|
|
"<div class=\"card\">" +
|
|
"<div class=\"card-body\">" +
|
|
"<h5 class=\"card-title\">{'Now playing' in current language}</h5>" +
|
|
"<p class=\"card-text\">{Audio file name}</p>" +
|
|
"<div class=\"progress mb-3\">" +
|
|
"<div class=\"progress-bar\" role=\"progressbar\" " +
|
|
"style=\"width: 0%;\" aria-valuenow=\"0\" aria-valuemin=\"0\" " +
|
|
"aria-valuemax=\"100\"></div></div>" +
|
|
"<div class=\"d-flex justify-content-between\"><span id=\"currentTime\">0:00</span>" +
|
|
"<span id=\"duration\">0:00</span>" +
|
|
"</div><div class=\"mt-3 text-center\">" +
|
|
"<button id=\"prevBtn\" class=\"btn btn-secondary\">" +
|
|
"<i class=\"fas fa-step-backward\"></i></button>" +
|
|
"<button id=\"playPauseBtn\" class=\"btn btn-primary\"><i class=\"fas fa-play\"></i></button>" +
|
|
"<button id=\"nextBtn\" class=\"btn btn-secondary\"><i class=\"fas fa-step-forward\">" +
|
|
"</i></button></div></div></div></div><script>" +
|
|
"var playPauseBtn = document.getElementById('playPauseBtn');const progressBar = document.querySelector('.progress-bar'); " +
|
|
"var currentTimeSpan = document.getElementById('currentTime');" +
|
|
"var durationSpan = document.getElementById('duration');" +
|
|
"var audio = new Audio('{AUDIOURL}');" +
|
|
"let isPlaying = false;function togglePlayPause() {if (isPlaying) {audio.pause();" +
|
|
"playPauseBtn.innerHTML = '<i class=\"fas fa-play\"></i>';} else {audio.play();" +
|
|
"playPauseBtn.innerHTML = '<i class=\"fas fa-pause\"></i>';}isPlaying = !isPlaying;" +
|
|
"function updateProgress() {const progress = (audio.currentTime / audio.duration) * 100;" +
|
|
"progressBar.style.width = `${progress}%`;progressBar.setAttribute('aria-valuenow', progress);" +
|
|
"currentTimeSpan.textContent = formatTime(audio.currentTime);" +
|
|
"durationSpan.textContent = formatTime(audio.duration);}function formatTime(seconds) " +
|
|
"{const minutes = Math.floor(seconds / 60);const remainingSeconds = Math.floor(seconds % 60);" +
|
|
"return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;}" +
|
|
"playPauseBtn.addEventListener('click', togglePlayPause);audio.addEventListener('timeupdate', updateProgress);" +
|
|
"</script>"
|
|
|
|
},
|
|
|
|
new HtmlSnippet
|
|
{
|
|
Id = 6,
|
|
Type = "photo_carousel",
|
|
Name = "Photo carousel",
|
|
Description = "A responsive photo carousel to display photos up not more than 5",
|
|
Html = "<div id=\"carouselExampleControls\" class=\"carousel slide\" data-bs-ride=\"carousel\">" +
|
|
"<div class=\"carousel-inner\">" +
|
|
"..."+
|
|
"<div class=\"carousel-item active\">" +
|
|
"<img src=\"{photo url}\" class=\"d-block w-100\" alt=\"{photo filename}\"></div>" +
|
|
"<div class=\"carousel-item\">" +
|
|
"<img src=\"{photo url}\" class=\"d-block w-100\" alt=\"{photo filename}\"></div>" +
|
|
"<div class=\"carousel-item\">" +
|
|
"<img src=\"{photo url}\" class=\"d-block w-100\" alt=\"{photo filename}\"></div>" +
|
|
"..."+
|
|
"</div>" +
|
|
"<button class=\"carousel-control-prev\" type=\"button\" data-bs-target=\"#carouselExampleControls\" data-bs-slide=\"prev\">" +
|
|
"<span class=\"carousel-control-prev-icon\" aria-hidden=\"true\"></span><span class=\"visually-hidden\">Previous</span></button>" +
|
|
"<button class=\"carousel-control-next\" type=\"button\" data-bs-target=\"#carouselExampleControls\" data-bs-slide=\"next\">" +
|
|
"<span class=\"carousel-control-next-icon\" aria-hidden=\"true\"></span><span class=\"visually-hidden\">Next</span></button></div>"
|
|
|
|
},
|
|
new HtmlSnippet
|
|
{
|
|
Id = 7,
|
|
Type = "event_list",
|
|
Name = "Event list",
|
|
Description = "A responsive layout for displaying events.",
|
|
Html = "<div class=\"col-md-10 mx-auto my-5\">" +
|
|
"<div class=\"event d-flex align-items-center bg-light p-4 row my-5\">" +
|
|
"<div class=\"text-center col-md-4\">" +
|
|
"<h2>{Time}</h2>" +
|
|
"<span class=\"d-block\" style=\"line-height:0;color:#9b5de5\">{Date}</span>" +
|
|
"</div><div class=\"col-md-8\">" +
|
|
"<div class=\"d-flex justify-content-between my-3\">" +
|
|
"<h4 class=\"text-capitalize\">{event name}</h4><p>" +
|
|
"<i class=\"fa-solid fa-location-dot\"></i>" +
|
|
"<span class=\"d-inline-block ml-2\" style=\"color:#9b5de5\">{Location}</span>" +
|
|
"</p></div><div>" +
|
|
"</div></div>"
|
|
}
|
|
// Add more snippets as needed
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|