SeemGen/Services/ContentEditorAIService.cs

309 lines
15 KiB
C#

using BLAIzor.Data;
using BLAIzor.Models;
using Microsoft.DotNet.Scaffolding.Shared;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Client;
namespace BLAIzor.Services
{
public class ContentEditorAIService
{
//private readonly AIService _aiService;
private readonly OpenAIApiService _openAIApiService;
private readonly OpenAiRealtimeService _openAIRealtimeService;
private readonly DeepSeekApiService _deepSeekApiService;
private readonly CerebrasAPIService _cerebrasAPIService;
private readonly KimiApiService _kimiApiService;
//private readonly ApplicationDbContext _context;
private readonly QDrantService _qDrantService;
private readonly HtmlSnippetProcessor _htmlSnippetProcessor;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ScopedContentService _scopedContentService;
private readonly ContentEditorService _contentEditorService;
private readonly ISimpleLogger _logger;
public static IConfiguration? _configuration;
public bool UseWebsocket = false;
private string AiProvider = "";
public ContentEditorAIService(
OpenAIApiService openAIApiService,
DeepSeekApiService deepSeekApiService,
OpenAiRealtimeService openAIRealtimeService,
CerebrasAPIService cerebrasAPIService,
KimiApiService kimiApiService,
/*ApplicationDbContext context,*/
QDrantService qDrantService,
HtmlSnippetProcessor htmlSnippetProcessor,
IServiceScopeFactory serviceScopeFactory,
ScopedContentService scopedContentService,
ISimpleLogger logger,
IConfiguration? configuration,
ContentEditorService contentEditorService)
{
//_aiService = aiService;
_openAIApiService = openAIApiService;
_deepSeekApiService = deepSeekApiService;
_openAIRealtimeService = openAIRealtimeService;
_cerebrasAPIService = cerebrasAPIService;
_kimiApiService = kimiApiService;
//_context = context;
_qDrantService = qDrantService;
_htmlSnippetProcessor = htmlSnippetProcessor;
_serviceScopeFactory = serviceScopeFactory;
_scopedContentService = scopedContentService;
_logger = logger;
_configuration = configuration;
_contentEditorService = contentEditorService;
}
private string GetAiEmbeddingSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
private string GetAiSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("Provider") ?? string.Empty;
// Existing methods
public async Task<string> GetMenuSuggestionsAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that helps the user in creating a website.";
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
//return result.Split('\n', StringSplitOptions.RemoveEmptyEntries)
// .Select(line => line.Trim('-').Trim())
// .ToList() ?? new List<string>();
return result;
}
public async Task<string> GetFacebookContentAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that helps the user in creating a website content from facebook posts.";
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
await _logger.InfoAsync("Facebook content: " + result);
//return result.Split('\n', StringSplitOptions.RemoveEmptyEntries)
// .Select(line => line.Trim('-').Trim())
// .ToList() ?? new List<string>();
return result;
}
public async Task<string> GetGeneratedContentAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that helps the user plan the content of a website. Do not generate html, just plain text.";
//var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
string result = "";
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, prompt);
}
else if (AiProvider == "chatgpt")
{
result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(systemMessage, userMessage);
result = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
if (AiProvider == "kimi")
{
result = await _kimiApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
return result;
}
public async Task<string> GetPhotoPromptAsync(string sessionId, string prompt)
{
string systemMessage = "You are a helpful assistant that writes image prompts. Respond only with the image prompt, no explanation, or information added.";
//var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
string result = "";
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, prompt);
}
else if (AiProvider == "chatgpt")
{
result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(systemMessage, userMessage);
result = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
if (AiProvider == "kimi")
{
result = await _kimiApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
}
return result;
}
public async Task<string> ProcessMenuItems(int SiteId, bool hasCollection, List<MenuItemModel> ExtractedMenuItems, string subject, bool menuItemsSaved, bool updateVectorDatabase)
{
try
{
if (!hasCollection)
{
//create colection
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
hasCollection = true;
}
else
{
//check if Collection is really created already
var collection = await _qDrantService.GetCollectionCount("Site" + SiteId.ToString());
if (collection != null)
{
var siteInfo = await _contentEditorService.GetSiteInfoByIdAsync(SiteId);
if (siteInfo.EmbeddingService != null)
{
var currentEmbeddingServiceName = GetAiEmbeddingSettings();
if (siteInfo.EmbeddingService == currentEmbeddingServiceName)
{
//drop existing collection
await _qDrantService.DeleteCollectionAsync("Site" + SiteId.ToString());
//create will automatically handle the new vector size
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
}
}
}
}
var updateQdrantList = ExtractedMenuItems.OrderBy(mi => mi.MenuItem.ContentGroup.Items.FirstOrDefault().Id).ToList(); //FIXXXXXXX
for (int i = 0; i < updateQdrantList.Count(); i++)
{
MenuItem menuItem = new MenuItem();
menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
menuItem.SortOrder = i;
menuItem.SiteInfoId = SiteId;
menuItem.ShowInMainMenu = true;
if (!string.IsNullOrEmpty(ExtractedMenuItems[i].Content)) //if we have content in the menuItem, let's update the vector DB
{
if (updateVectorDatabase)
{
Guid guid = Guid.NewGuid();
WebPageContent content = new WebPageContent();
content.Id = guid;
content.UId = guid.ToString();
content.Type = "page";
content.SiteId = SiteId;
content.Name = ExtractedMenuItems[i].MenuItem.Name;
if (ExtractedMenuItems[i].ContentDescription == null)
{
content.Description = $"A section called {ExtractedMenuItems[i].MenuItem.Name} of a website of {subject}";
}
else
{
content.Description = ExtractedMenuItems[i].ContentDescription;
}
content.Content = ExtractedMenuItems[i].Content;
content.LastUpdated = DateTime.UtcNow;
//menuItem.QdrantPointId = guid; //FIXXXXXXX
//menuItem.PointId = i; //FIXXXXXXX
await _htmlSnippetProcessor.ProcessAndStoreWebContentAsync(guid, content, SiteId);
}
else
{
//menuItem.PointId = i; //FIXXXXXXX
//menuItem.QdrantPointId = ExtractedMenuItems[i].MenuItem.QdrantPointId; //FIXXXXXXX
}
}
if (!menuItemsSaved) //menuItems just extracted, no content yet because that can be generated only after menuItems have been saved first
{
//menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
//menuItem.SortOrder = i;
//menuItem.SiteInfoId = SiteId;
//menuItem.ShowInMainMenu = true;
await _contentEditorService.AddMenuItemAsync(menuItem);
}
else //menuItems available already
{
var menuItems = await _contentEditorService.GetMenuItemsBySiteIdAsync(SiteId);
var thisItem = menuItems.FirstOrDefault(x => x.SiteInfoId == SiteId && x.Id == ExtractedMenuItems[i].MenuItem.Id);
if (thisItem == null)
{
//new menu item?
await _contentEditorService.AddMenuItemAsync(menuItem);
}
else
{
//thisItem.PointId = menuItem.PointId; //FIXXXXXXX
//thisItem.QdrantPointId = menuItem.QdrantPointId; //FIXXXXXXX
thisItem.SortOrder = ExtractedMenuItems[i].MenuItem.SortOrder;
thisItem.ShowInMainMenu = ExtractedMenuItems[i].MenuItem.ShowInMainMenu;
await _contentEditorService.UpdateMenuItemAsync(thisItem);
}
}
}
//await ContentEditorService.SaveMenuItemsAsync(menuItems);
//menuItemsSaved = true;
return "OK";
}
catch
{
return "Error";
}
}
//public async Task<string> AnalyzeFormFieldsFromText(string text)
//{
// string systemMessage = "You are a helpful assistant that extracts structured form field data from plain text.";
// string userMessage =
// "This is the plain text extracted from a form document. " +
// "Please extract all input fields a user is expected to fill in and return a JSON array of objects.\n\n" +
// "Each object should have the following structure:\n" +
// "- `label`: The label or prompt for the input\n" +
// "- `type`: One of `text`, `number`, `date`, `checkbox`, `radio`, or `textarea`\n" +
// "- `options`: If type is `radio` or `checkbox`, include the possible options as a list of strings; otherwise, return an empty list\n" +
// "- `section`: The section title this field belongs to (if any)\n" +
// "- `repeatable`: true if this field (or set of fields) is expected to be repeated for multiple entries (like multiple employers or qualifications), otherwise false\n\n" +
// "Return only the JSON array, without any explanation or formatting like ```json.\n\n" +
// "Here is the form text:\n\n" +
// $"---\n{text}\n---";
// return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
//}
public async Task<string> AnalyzeGroupedFormFieldsFromText(string text)
{
string systemMessage = "You are a helpful assistant that analyzes plain form text and extracts structured, grouped form field definitions.";
string userMessage =
"This is a plain text version of a printed form. Your task is to extract the **input fields** that a user must fill in. " +
"Group related fields under sections where applicable, and return them in a JSON structure.\n\n" +
"Each group should follow this structure:\n" +
"- `groupName`: Name of the logical section or group (e.g. 'Work Experience')\n" +
"- `repeatable`: true if this section can appear multiple times (e.g. multiple employers, qualifications)\n" +
"- `fields`: an array of fields within the group\n\n" +
"Each field must have:\n" +
"- `label`: The field label/question\n" +
"- `type`: one of `text`, `number`, `date`, `textarea`, `checkbox`, or `radio`\n" +
"- `options`: list of string values if type is `radio` or `checkbox`, or an empty list otherwise\n" +
"- `section`: optional section name (can match group name or be more specific)\n\n" +
"Return only the JSON array. Do not include any explanation or formatting like ```json.\n\n" +
$"Here is the form text:\n\n---\n{text}\n---";
return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
}
}
}