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("EmbeddingService") ?? string.Empty; private string GetAiSettings() => _configuration?.GetSection("AiSettings")?.GetValue("Provider") ?? string.Empty; // Existing methods public async Task 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(); return result; } public async Task 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(); return result; } public async Task 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 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 ProcessMenuItems(int SiteId, bool hasCollection, List 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 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 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); } } }