SeemGen/Services/AIService.cs

1240 lines
64 KiB
C#

using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text;
using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.Wordprocessing;
using BLAIzor.Models;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components;
using static Google.Apis.Requests.BatchRequest;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using NuGet.Packaging;
using Microsoft.AspNetCore.Mvc.Formatters;
using Google.Api;
using System.CodeDom;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;
using BLAIzor.Services;
using Microsoft.DotNet.Scaffolding.Shared;
using System.Xml.XPath;
using Azure.Identity;
using BLAIzor.Helpers;
using System.Collections;
using Qdrant.Client.Grpc;
using Microsoft.CodeAnalysis.Elfie.Model.Tree;
using Microsoft.AspNetCore.Components.Web;
namespace BLAIzor.Services
{
public class AIService
{
private readonly HttpClient _httpClient;
private readonly ContentEditorService _contentEditorService;
private readonly ScopedContentService _scopedContentService;
private readonly OpenAIEmbeddingService _openAIEmbeddingService;
private readonly LocalEmbeddingService _localEmbeddingService;
private readonly OpenAIApiService _openAIApiService;
private readonly OpenAiRealtimeService _openAIRealtimeService;
private readonly DeepSeekApiService _deepSeekApiService;
private readonly CerebrasAPIService _cerebrasAPIService;
private readonly KimiApiService _kimiApiService;
private readonly QDrantService _qDrantService;
private readonly NavigationManager _navigationManager;
private readonly LocalVectorSearchService _localVectorSearchService;
private readonly WebsiteContentLoaderService _websiteContentLoaderService;
private readonly ISimpleLogger _logger;
private readonly CacheService _cacheService;
public static IConfiguration? _configuration;
public AIService(HttpClient httpClient,
ContentEditorService contentEditorService,
ScopedContentService scopedContentService,
QDrantService qDrantService,
OpenAIEmbeddingService openAIEmbeddingService,
LocalEmbeddingService localEmbeddingService,
OpenAIApiService openAIApiService,
DeepSeekApiService deepSeekApiService,
OpenAiRealtimeService openAIRealtimeService,
CerebrasAPIService cerebrasAPIService,
KimiApiService kimiApiService,
NavigationManager navigationManager,
IConfiguration? configuration,
LocalVectorSearchService localVectorSearchService,
WebsiteContentLoaderService websiteContentLoaderService,
ISimpleLogger logger,
CacheService cacheService)
{
_httpClient = httpClient;
_contentEditorService = contentEditorService;
_scopedContentService = scopedContentService;
_qDrantService = qDrantService;
_openAIEmbeddingService = openAIEmbeddingService;
_localEmbeddingService = localEmbeddingService;
_openAIApiService = openAIApiService;
_deepSeekApiService = deepSeekApiService;
_openAIRealtimeService = openAIRealtimeService;
_cerebrasAPIService = cerebrasAPIService;
_kimiApiService = kimiApiService;
_navigationManager = navigationManager;
_localVectorSearchService = localVectorSearchService;
_websiteContentLoaderService = websiteContentLoaderService;
_configuration = configuration;
_openAIApiService.RegisterCallback(HandleActionInvoked, HandleFinishedInvoked, HandleErrorInvoked);
_cerebrasAPIService.RegisterCallback(HandleActionInvoked, HandleFinishedInvoked, HandleErrorInvoked);
_openAIRealtimeService.RegisterCallback(HandleActionInvoked);
_logger = logger;
_cacheService = cacheService;
}
private const string OpenAiEndpoint = "https://api.openai.com/v1/chat/completions";
public string _apiKey;
public event Action<string, string, MenuItem?>? OnContentReceived;
public event Action<string>? OnContentReceiveFinished;
public event Action<string, string>? OnContentReceivedError;
public event Action<string, string>? OnStatusChangeReceived;
public event Action<string, string>? OnTextContentAvailable;
public string Mood = "cool, and professional";
private string _workingContent = null;
public bool UseWebsocket = false;
private string AiProvider = "";
private string GetAiSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("Provider") ?? string.Empty;
private string GetAiEmbeddingSettings() =>
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
private void HandleActionInvoked(string sessionId, string streamedHtmlContent)
{
OnContentReceived?.Invoke(sessionId, streamedHtmlContent, null);
}
private void HandleErrorInvoked(string sessionId, string streamedHtmlContent)
{
OnContentReceivedError?.Invoke(sessionId, streamedHtmlContent);
}
private void HandleFinishedInvoked(string sessionId)
{
OnContentReceiveFinished?.Invoke(sessionId);
}
public string GetApiKey()
{
if (_configuration == null)
{
return string.Empty;
}
if (_configuration.GetSection("OpenAI") == null)
{
return string.Empty;
}
return _configuration.GetSection("OpenAI").GetValue<string>("ApiKey")!;
}
public async Task<WebsiteContentModel> InitSite(string sessionId, SiteInfo site, string templateCollectionName, string menuList = "")
{
await _logger.InfoAsync($"InitSite: method called", templateCollectionName);
string currentUri = _navigationManager.Uri;
if (site.DefaultLanguage != null)
{
_scopedContentService.WebsiteDefaultLanguage = site.DefaultLanguage;
}
WebsiteContentModel siteModel = await _cacheService.UpdateContentCache(sessionId, site.Id);
//_scopedContentService.SessionId = sessionId;
_scopedContentService.AvailableTemplateSnippets = await GetSnippetsForDisplay(sessionId, templateCollectionName);
return siteModel;
}
public async Task GetChatGptWelcomeMessage(string sessionId, int SiteId, string templateCollectionName, string menuList = "")
{
await _logger.InfoAsync($"GetChatGptWelcomeMessage: method called", templateCollectionName);
SiteInfo site = await _contentEditorService.GetSiteInfoByIdAsync(SiteId);
var siteModel = await InitSite(sessionId, site, templateCollectionName, menuList);
//_apiKey = GetApiKey();
//List<string> qdrantPoint = await _qDrantService.GetContentAsync(SiteId, _scopedContentService.WebsiteContent.Items.FirstOrDefault());
string extractedText = "";
//nullcheck
if (siteModel != null)
{
await _logger.InfoAsync($"GetChatGptWelcomeMessage: sitemodel not null", siteModel.SiteInfoId.ToString());
if (siteModel.ContentItems != null && siteModel.ContentItems.Count > 0)
{
if (siteModel.ContentItems.OrderBy(sm => sm.ContentItem.Id).FirstOrDefault().VectorPoints != null)
{
foreach (var contentPoint in siteModel.ContentItems.OrderBy(sm => sm.ContentItem.Id).FirstOrDefault().VectorPoints)
{
extractedText += contentPoint.Content;
}
}
else
{
extractedText = "Content not available";
}
}
}
else
{
var siteContent = await _qDrantService.GetPointFromQdrantAsyncByPointId(SiteId, 0);
extractedText += siteContent.FirstOrDefault().Content;
}
//OnContentReceived?.Invoke(sessionId, extractedText);
string siteEntity;
if (!string.IsNullOrWhiteSpace(site.Entity))
{
siteEntity = site.Entity;
}
else
{
siteEntity = "brand or company";
}
string systemMessage = AiPrompts.WelcomeContent.GetSystemMessageForWelcomeMessage(Mood, siteEntity, extractedText, _scopedContentService.SelectedBrandName, _scopedContentService.SelectedLanguage, menuList);
string userMessage = "Hello";
string streamedHtmlContent = string.Empty;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
var result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, null);
}
else if (AiProvider == "chatgpt")
{
await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage);
//var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
//var fixedForColons = TextHelper.FixJsonWithoutAI(result);
//OnContentReceived?.Invoke(sessionId, fixedForColons, null);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(systemMessage, userMessage);
}
if (AiProvider == "kimi")
{
var result = await _kimiApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, null);
}
}
else
{
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage);
}
//_scopedContentService.CurrentDOM = streamedHtmlContent;
////Console.Write("Answer: " + streamedHtmlContent);
//return streamedHtmlContent;
_scopedContentService.AvailableTemplateSnippets = await GetSnippetsForDisplay(sessionId, templateCollectionName);
//Console.WriteLine(_scopedContentService.WebsiteContentModel.ContentItems.Count);
}
public async Task ProcessUserIntent(string sessionId, string userPrompt, int siteId, int templateId, string contentCollectionName, string menuList = "")
{
await _logger.InfoAsync($"ProcessUserIntent: method called", $"sessionId: {sessionId}, userprompt: {userPrompt}, siteId: {siteId}, templateId: {templateId}, contentCollectionName: {contentCollectionName}");
//Console.WriteLine($"SITE ID: {siteId}");
OnStatusChangeReceived?.Invoke(sessionId, "Understanding your request...");
// Get JSON result based on siteId presence
string resultJson = await GetJsonResultFromQuery(sessionId, siteId, userPrompt);
//Console.WriteLine(resultJson);
var baseResult = await ValidateAndFixJson<ChatGPTResultBase>(resultJson, FixJsonWithAI);
//var baseResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTResultBase>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (baseResult == null)
{
//Console.WriteLine("Invalid JSON response.");
return;
}
var fixedResult = System.Text.Json.JsonSerializer.Serialize(baseResult);
OnStatusChangeReceived?.Invoke(sessionId, "Making a decision");
// Process result based on type
switch (baseResult.Type.ToLower())
{
case "methodresult":
await ProcessMethodResult(sessionId, resultJson);
break;
case "textresult":
await ProcessTextResult(sessionId, "Your answer", fixedResult, templateId, contentCollectionName);
break;
case "examinationresult":
await ProcessExaminationResult(sessionId, fixedResult, templateId, contentCollectionName);
break;
case "errorresult":
await ProcessErrorResult(sessionId, fixedResult);
break;
default:
//Console.WriteLine("Unknown result type.");
break;
}
}
/// <summary>
/// No reasoning needed just content retrieved and displayed as html
/// </summary>
/// <param name="sessionId"></param>
/// <param name="userPrompt"></param>
/// <param name="siteId"></param>
/// <param name="templateId"></param>
/// <param name="collectionName"></param>
/// <param name="menuList"></param>
/// <returns></returns>
public async Task ProcessContentRequest(string sessionId, MenuItem requestedMenu, int siteId, int templateId, string contentCollectionName, string menuList = "", bool forceUnmodified = false)
{
//Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
string extractedText = "";
if (requestedMenu != null)
{
//do we have it cached?
var contentId = requestedMenu.ContentItemId;
if (_scopedContentService.WebsiteContentModel != null)
{
if (_scopedContentService.WebsiteContentModel.ContentItems.Any(x => x.ContentItem.Id == contentId))
{
//we have the content cached
//checkversion TODO
var thisContent = _scopedContentService.WebsiteContentModel.ContentItems.Where(x => x.ContentItem.Id == contentId).FirstOrDefault();
//this should be only 1 for now (and we shouldn't use this, but the save content itself...)
//foreach (var chunk in thisContent.VectorPoints)
//{
// extractedText += $"{chunk.Name}: {chunk.Content}";
//}
extractedText = thisContent.ContentItem.Content;
}
}
else
{
//get vector of menuItem
if (requestedMenu.ContentGroupId != null)
{
//here we need something that turns the menuitem into contentitem... oh wait we have that..
var contentItem = await _contentEditorService.GetContentItemByIdAsync((int)requestedMenu.ContentItemId);
var vectorList = contentItem.Chunks.ToList();
//var vectors = await _contentEditorService.GetPointIdsByContentGroupIdAsync((int)requestedMenu.ContentGroupId);
PointId[] points = new PointId[vectorList.Count];
for (int i = 0; i < vectorList.Count; i++)
{
points[i] = Convert.ToUInt64(vectorList[i].ChunkIndex);
}
var site = await _contentEditorService.GetSiteInfoByIdAsync(siteId);
List<WebPageContent> qDrantData = await _qDrantService.GetPointsFromQdrantAsyncByPointIds(site, points); //FIXXXXXXX
foreach (var point in qDrantData)
{
//extractedText += point.result.payload.name + ": " + point.result.payload.content + ", ";
extractedText += $"{point.Name}: {point.Content},";
}
}
else
{
//error, no contentgroup yet
}
//TODO
}
}
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,
forceUnmodified);
await ProcessContent(sessionId, requestedMenu, contentJson, templateId, contentCollectionName);
}
public async Task ProcessContentRequest(string sessionId, string requestedMenu, int siteId, int templateId, string contentCollectionName, string menuList = "", bool forceUnmodified = false)
{
await _logger.InfoAsync($"ProcessContentRequest: method called", $"sessionId: {sessionId}, requestedMenu: {requestedMenu}, siteId: {siteId}, templateId: {templateId}, contentCollectionName: {contentCollectionName}");
//Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
string extractedText = "";
float[] vector = [];
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
var embeddingServiceProvider = GetAiEmbeddingSettings();
if (embeddingServiceProvider == "local")
{
vector = await _localEmbeddingService.GenerateEmbeddingAsync(requestedMenu);
}
else
{
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(requestedMenu);
}
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
//get current version
ContentGroup contentGroup = await _contentEditorService.GetContentGroupBySiteInfoIdAsync(siteId);
int version = 0;
if (contentGroup != null)
{
version = contentGroup.Version;
}
//no cache
//if (_scopedContentService.WebsiteContentModel == null || _scopedContentService.WebsiteContentModel.ContentGroup.Version != version)
if (_scopedContentService.WebsiteContentModel == null)
{
//have to get from qdrant
var pointList = await _qDrantService.QueryContentAsync(contentCollectionName, vector, 3);
//PointId[] pointArray = new PointId[intArray.Length];
if (pointList.Count > 0)
{
for (int i = 0; i < pointList.Count; i++)
{
extractedText += $"{pointList[i].Name}: {pointList[i].Content}";
}
}
else
{
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
}
}
//cached content
else
{
List<WebPageContent> pagePoints = new List<WebPageContent>();
foreach (var valami in _scopedContentService.WebsiteContentModel.ContentItems)
{
pagePoints.AddRange(valami.VectorPoints);
}
var resultContent = _localVectorSearchService.SearchContent(vector, pagePoints, 3);
foreach (var item in resultContent)
{
extractedText += item.Name + ": " + item.Content + ", ";
}
}
Console.WriteLine($"Points from cache or qdrant: {extractedText}");
string contentJson = await GetContentFromQuery(sessionId,
requestedMenu,
extractedText,
forceUnmodified);
Console.WriteLine($"AI representation: {contentJson}");
await ProcessContent(sessionId, requestedMenu, contentJson, templateId, contentCollectionName);
}
// Refactored helper methods
private async Task ProcessMethodResult(string sessionId, string resultJson)
{
await _logger.InfoAsync($"ProcessMethodResult: method called", $"sessionId: {sessionId}, json: {resultJson}");
//var fixedResult = await ValidateAndFixJson<ChatGPTMethodResult>(resultJson, FixJsonWithAI); //ANOTHER fixing is useless, json is from fixed format
var methodResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTMethodResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (methodResult != null)
{
OnStatusChangeReceived?.Invoke(sessionId, "Initiating the task you requested");
await DisplayHtml(sessionId, methodResult.Text, methodResult.MethodToCall, methodResult.Parameter);
}
}
private async Task ProcessTextResult(string sessionId, string pageTitle, string resultJson, int templateId, string collectionName)
{
await _logger.InfoAsync($"ProcessTextResult: method called", $"sessionId: {sessionId}, json: {resultJson}, pageTitle: {pageTitle}, templateId: {templateId}, collectionName: {collectionName}");
//var fixedResult = await ValidateAndFixJson<ChatGPTTextResult>(resultJson, FixJsonWithAI); //ANOTHER fixing is useless, json is from fixed format
var textResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTTextResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (textResult != null)
{
string contentJson = await GetContentFromQuery(sessionId, textResult.Text, _workingContent);
//Console.Write("\r \n ProcessTextResult: Content: " + contentJson + "\r \n");
await ProcessContent(sessionId, pageTitle, contentJson, templateId, collectionName);
}
}
public async Task<T?> ValidateAndFixJson<T>(string json, Func<string, Task<string>> aiFixer)
{
await _logger.InfoAsync($"ValidateAndFixJson: method called", $"json: {json}");
try
{
return System.Text.Json.JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
catch (Exception ex)
{
await _logger.ErrorAsync($"ValidateAndFixJson: failed first!", $"message: {ex.Message}");
var prompt = BuildJsonFixPrompt(json, ex.Message, typeof(T).Name);
var fixedJson = await aiFixer(prompt);
try
{
return System.Text.Json.JsonSerializer.Deserialize<T>(fixedJson, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
catch (Exception ex2)
{
await _logger.ErrorAsync($"ValidateAndFixJson: failed again!", $"❌ AI-fix parse failed: {ex2.Message}");
return default;
}
}
}
public async Task<string> FixJsonWithAI(string prompt)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
return await _cerebrasAPIService.GetSimpleCerebrasResponseNoSession("You are a JSON-fixing assistant.", prompt);
}
else if (AiProvider == "chatgpt")
{
return await _openAIApiService.GetSimpleChatGPTResponseNoSession("You are a JSON-fixing assistant.", prompt);
}
else if (AiProvider == "deepseek")
{
return await _deepSeekApiService.GetSimpleChatGPTResponse("You are a JSON-fixing assistant.", prompt);
}
else { return ""; }
}
private string BuildJsonFixPrompt(string json, string errorMessage, string targetTypeName)
{
return $"""
The following JSON was supposed to be parsed into an object of type {targetTypeName}, but it failed with this error:
{errorMessage}
Please fix the formatting of the JSON so that it becomes valid and deserializable:
--- JSON START ---
{json}
--- JSON END ---
Only return the fixed JSON, with no explanation or formatting like ```json.
""";
}
private async Task ProcessExaminationResult(string sessionId, string resultJson, int templateId, string collectionName)
{
await _logger.InfoAsync($"ProcessExaminationResult: method called", $"sessionId: {sessionId}, json: {resultJson}, templateId: {templateId}, collectionName: {collectionName}");
//var fixedResult = await ValidateAndFixJson<ChatGPTExaminationResult>(resultJson, FixJsonWithAI);
var explanationResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTExaminationResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (explanationResult != null)
{
string contentJson = await GetExplanationFromQuery(sessionId, explanationResult.Text, _scopedContentService.CurrentDOM);
await ProcessContent(sessionId, "Examination", contentJson, templateId, collectionName);
}
}
private async Task ProcessContent(string sessionId, string pageTitle, string contentJson, int templateId, string contentCollectionName)
{
await _logger.InfoAsync($"ProcessContent: method called", $"sessionId: {sessionId}, pageTitle: {pageTitle}, json: {contentJson}, templateId: {templateId}, contentCollectionName: {contentCollectionName}");
try
{
var fixedResult = await ValidateAndFixJson<ChatGPTContentResult>(contentJson, FixJsonWithAI);
//var contentResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTContentResult>(contentJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
//var fixedResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTContentResult>(contentJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (fixedResult != null)
{
Console.WriteLine($"\n\n Actual content: {fixedResult.Text} \n\n");
// Add reaction GIFs
//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
//TODO modify photos handling, move audio generation to the layout area
string removedNumbers = TextHelper.ReplaceNumbersAndSpecialCharacters(fixedResult.Text, _scopedContentService.SelectedLanguage);
Console.WriteLine(removedNumbers);
OnTextContentAvailable?.Invoke(sessionId, removedNumbers);
//List<HtmlSnippet> snippets = await GetSnippetsForDisplay(sessionId, collectionName);
List<HtmlSnippet> snippets = _scopedContentService.AvailableTemplateSnippets;
//await DisplayLayoutPlanFromContent(sessionId, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
var result = await DisplayLayoutPlanFromContent(sessionId, pageTitle, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
if (result == null) result = new LayoutPlan();
await DisplayHtml(sessionId, pageTitle, result, snippets, fixedResult.Topics, fixedResult.Photos);
//OnContentReceived?.Invoke(sessionId, result.Blocks.Count.ToString());
}
}
catch (Exception ex)
{
//Console.WriteLine($"Error processing content: {ex.Message}");
OnContentReceived?.Invoke(sessionId, ex.Message, null);
}
}
//passing menuitem further
private async Task ProcessContent(string sessionId, MenuItem requestedMenu, string contentJson, int templateId, string collectionName)
{
await _logger.InfoAsync($"ProcessContent: method called", $"sessionId: {sessionId}, menuItem: {requestedMenu.Name}, json: {contentJson}, templateId: {templateId}, contentCollectionName: {collectionName}");
try
{
var fixedResult = await ValidateAndFixJson<ChatGPTContentResult>(contentJson, FixJsonWithAI);
//var contentResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTContentResult>(contentJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
//var fixedResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTContentResult>(contentJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (fixedResult != null)
{
Console.WriteLine($"\n\n Actual content: {fixedResult.Text} \n\n");
// Add reaction GIFs
//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
//TODO modify photos handling, move audio generation to the layout area
string removedNumbers = TextHelper.ReplaceNumbersAndSpecialCharacters(fixedResult.Text, _scopedContentService.SelectedLanguage);
Console.WriteLine(removedNumbers);
OnTextContentAvailable?.Invoke(sessionId, removedNumbers);
//List<HtmlSnippet> snippets = await GetSnippetsForDisplay(sessionId, collectionName);
List<HtmlSnippet> snippets = _scopedContentService.AvailableTemplateSnippets;
//await DisplayLayoutPlanFromContent(sessionId, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
var result = await DisplayLayoutPlanFromContent(sessionId, requestedMenu.Name, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
if (result == null) result = new LayoutPlan();
await DisplayHtmlForMenu(sessionId, requestedMenu, result, snippets, fixedResult.Topics, fixedResult.Photos);
//OnContentReceived?.Invoke(sessionId, result.Blocks.Count.ToString());
}
}
catch (Exception ex)
{
//Console.WriteLine($"Error processing content: {ex.Message}");
OnContentReceived?.Invoke(sessionId, ex.Message, requestedMenu);
}
}
private async Task ProcessErrorResult(string sessionId, string resultJson)
{
await _logger.InfoAsync($"ProcessErrorResult: method called", $"sessionId: {sessionId}, json: {resultJson}");
var errorResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTErrorResult>(resultJson);
if (errorResult != null)
{
//Console.WriteLine($"Error Result: {errorResult.Text}");
await DisplayLayoutPlanFromContent(sessionId, errorResult.Text, null, null, null);
}
}
/// <summary>
/// Let's get the actual content
/// </summary>
/// <param name="userPrompt"></param>
/// <returns></returns>
public async Task<string> GetContentFromQuery(string sessionId, string userPrompt, string content = null, bool forceUnmodified = false)
{
await _logger.InfoAsync($"GetContentFromQuery: method called", $"sessionId: {sessionId}, userPrompt: {userPrompt}, content: {content}, forceUnmodified: {forceUnmodified}");
string extractedText;
if (content == null)
{
string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _scopedContentService.SelectedDocument);
extractedText = WordFileReader.ExtractText(rootpath);
}
else extractedText = content;
_apiKey = GetApiKey();
////Console.Write("GetJSONResult called: " +extractedText);
string systemMessage = "";
if (forceUnmodified)
{
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 " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n" +
"Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " +
"- Turn the following content into a nice informative webpage content (DO NOT REMOVE URLS, PHOTO URLS though).\r\n " +
"- Start with the page title.\r\n" +
"- Structure the text nicely without missing any information.\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
{
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. " +
//"**chatGPTContentResult**:\r\n " +
"- `type`: A string with value `contentresult`.\r\n " +
"- `text`: A string with the actual response.\r\n " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n" +
"Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " +
"- Base a detailed response solely on the initial document provided below. " +
"- 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 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." +
//"You can decorate your clarification" +
//"request with this image URL: `https://www.reactiongifs.com/r/martin.gif` added to the photo dictionary.\r\n\r\n" +
//"- For compliments from the user: Express our happiness about it " +
//"and apply this image URL: `https://www.reactiongifs.com/r/review.gif` in the photo dictionary.\r\n\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.";
}
OnStatusChangeReceived?.Invoke(sessionId, "Constructing the answer");
string interMediateResult = string.Empty;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
interMediateResult = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "chatgpt")
{
interMediateResult = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "deepseek")
{
interMediateResult = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
}
else
{
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
//Console.Write("GetContentFromQuery: Result decision: " + interMediateResult);
return interMediateResult;
}
public async Task<string> GetExplanationFromQuery(string sessionId, string userPrompt, string content = null)
{
string extractedText;
if (content == null)
{
string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _scopedContentService.SelectedDocument);
extractedText = WordFileReader.ExtractText(rootpath);
}
else extractedText = content;
_apiKey = GetApiKey();
////Console.Write("GetJSONResult called: " +extractedText);
var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a 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 " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n- Base responses solely on the initial document: {" + extractedText + "}.\r\n" +
"- For missing information: Inform the user and provide a clarification. " +
"- 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. " +
"You may decorate your clarification" +
"request with this image URL: `https://www.reactiongifs.com/r/martin.gif` added to the photo dictionary.\r\n\r\n" +
"- For compliments from the user: Express our happiness about it " +
"and apply this image URL: `https://www.reactiongifs.com/r/review.gif` in the photo dictionary.\r\n\r\n" +
"**Style and Image Handling**:\r\n" +
//"- Copy styles explicitly from the document into the response.\r\n" +
//"- Only use image URLs found in the document for relevant content.\r\n" +
"- 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 or such.";
OnStatusChangeReceived?.Invoke(sessionId, "Constructing the answer");
string interMediateResult = string.Empty;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
interMediateResult = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "chatgpt")
{
interMediateResult = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "deepseek")
{
interMediateResult = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
}
else
{
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
//Console.Write("GetExaminationResult: Result decision: " + interMediateResult);
return interMediateResult;
}
public async Task<string> GetJsonResultFromQuery(string sessionId, int siteId, string userPrompt)
{
await _logger.InfoAsync($"GetJsonResultFromQuery: method called", $"SessionId: {sessionId}, userPrompt: {userPrompt}, siteId: {siteId}");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
//_apiKey = GetApiKey();
//start with embeddings
var site = await _contentEditorService.GetSiteInfoByIdAsync(siteId);
float[] vector = [];
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
var embeddingServiceProvider = GetAiEmbeddingSettings();
if (embeddingServiceProvider == "local")
{
vector = await _localEmbeddingService.GenerateEmbeddingAsync(userPrompt);
}
else
{
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(userPrompt);
}
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
string extractedText = "Sections: ";
if (_scopedContentService.WebsiteContentModel == null)
{
//have to get from qdrant
var pointList = await _qDrantService.QueryContentAsync(site.VectorCollectionName, vector, 3);
if (pointList.Count > 0)
{
for (int i = 0; i < pointList.Count; i++)
{
extractedText += pointList[i].Name + ": " + pointList[i].Content + ", ";
}
}
else
{
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
}
}
//cached content
else
{
List<WebPageContent> pagePoints = new List<WebPageContent>();
//TODO nullcheck... how did we get here,
foreach (var valami in _scopedContentService.WebsiteContentModel.ContentItems)
{
pagePoints.AddRange(valami.VectorPoints);
}
var resultContent = _localVectorSearchService.SearchContent(vector, pagePoints, 3);
foreach (var item in resultContent)
{
extractedText += item.Name + ": " + item.Content + ", ";
}
}
_workingContent = extractedText.Replace("\"", "'");
//Console.Write("\r \n GetJsonResultFromQuery: Working content: " + _workingContent + "\r \n");
//string extractedText = WordFileReader.ExtractText(rootpath);
//Console.Write("GetJSONResult called!");
string systemMessage = AiPrompts.ContentProcessing.GetSystemMessageForJsonResultDecision(_scopedContentService.SelectedLanguage, extractedText, _scopedContentService.CurrentDOM);
string interMediateResult = string.Empty;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
interMediateResult = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "chatgpt")
{
interMediateResult = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
else if (AiProvider == "deepseek")
{
interMediateResult = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userPrompt);
}
}
else
{
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
//Console.Write("\r \n GetJsonResultFromQuery: Result decision: " + interMediateResult + "\r \n");
return interMediateResult;
}
public async Task<List<HtmlSnippet>> GetSnippetsForDisplay(string sessionId, string collectionName)
{
await _logger.InfoAsync($"GetSnippetsForDisplay: method called", $"SessionId: {sessionId}, collectionName: {collectionName}");
_apiKey = GetApiKey();
OnStatusChangeReceived?.Invoke(sessionId, "Looking up the UI template elements for you");
//string availableSnippetList = "";
List<HtmlSnippet> snippets = new List<HtmlSnippet>();
var snippetscount = await _qDrantService.GetCollectionCount(collectionName);
for (int j = 1; j <= snippetscount; j++)
{
var snippet = await _qDrantService.GetSnippetAsync(j, collectionName);
QDrantGetPointResult x = JsonConvert.DeserializeObject<QDrantGetPointResult>(snippet);
snippets.Add(new HtmlSnippet
{
Id = x.result.payload.Id,
Name = x.result.payload.Name,
Description = x.result.payload.Description,
Type = x.result.payload.Type,
Variant = x.result.payload.Variant,
Tags = x.result.payload.Tags,
Slots = x.result.payload.Slots,
Html = x.result.payload.Html,
SampleHtml = x.result.payload.SampleHtml,
Vectors = x.result.GetFloatVector()
});
//availableSnippetList += ("- " + x.result.payload.Name + ": " + x.result.payload.Description + ".\r\n");
}
////Console.Write(availableSnippetList);
OnStatusChangeReceived?.Invoke(sessionId, "Loading UI elements from the design database");
return snippets;
}
//for textresult and errorresult
public async Task DisplayHtml(string sessionId, string pageTitle, LayoutPlan layoutPlan, List<HtmlSnippet> htmlToUse, string[]? topics = null, Dictionary<string, string>? photos = null)
{
await _logger.InfoAsync($"DisplayHtml: method called", $"SessionId: {sessionId}, pageTitle: {pageTitle}, layoutPlan: {layoutPlan.Blocks.Count}, htmlToUse: {htmlToUse.Count}, topics: {topics?.Length}, photos: {photos?.Count}");
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
//Console.WriteLine($"DISPLAYHTML Snippets: {htmlToUse.Count}\n\n");
//Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
string systemMessage = AiPrompts.HtmlRendering.GetHtmlRenderingSystemPromptForTextAndErrorResult(_scopedContentService.SelectedLanguage, pageTitle, htmlToUse, photos, topics);
//string userMessage = AiPrompts.HtmlRendering.HtmlRenderingUserPromptForTextAndErrorResult;
//string assistantMessage = AiPrompts.HtmlRendering.GetHtmlRenderingAssistantMessageForTextAndErrorResult(layoutPlan);
string userMessage = AiPrompts.HtmlRendering.HtmlRenderingUserPromptForTextAndErrorResult + AiPrompts.HtmlRendering.GetHtmlRenderingAssistantMessageForTextAndErrorResult(layoutPlan);
//string assistantMessage = "`Provided layout plan, that contains the text to be displayed as HTML`:";
//foreach (var block in layoutPlan.Blocks)
//{
// assistantMessage += $"Block type: {block.Type}, block content: {block.RawContent}";
//}
//int mode = -1;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
//await _cerebrasAPIService.GetCerebrasStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage, "", -1);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, null);
}
else if (AiProvider == "chatgpt")
{
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage, "", -1);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, null);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else
{
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage, "");
}
}
public async Task DisplayHtmlForMenu(string sessionId, MenuItem requestedMenu, LayoutPlan layoutPlan, List<HtmlSnippet> htmlToUse, string[]? topics = null, Dictionary<string, string>? photos = null)
{
await _logger.InfoAsync($"DisplayHtmlForMenu: method called", $"SessionId: {sessionId}, requestedMenu: {requestedMenu.Name}, layoutPlan: {layoutPlan.Blocks.Count}, htmlToUse: {htmlToUse.Count}, topics: {topics?.Length}, photos: {photos?.Count}");
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
//Console.WriteLine($"DISPLAYHTML Snippets: {htmlToUse.Count}\n\n");
//Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
string systemMessage = AiPrompts.HtmlRendering.GetHtmlRenderingSystemPromptForTextAndErrorResult(_scopedContentService.SelectedLanguage, requestedMenu.Name, htmlToUse, photos, topics);
string userMessage = AiPrompts.HtmlRendering.HtmlRenderingUserPromptForTextAndErrorResult;
string assistantMessage = AiPrompts.HtmlRendering.GetHtmlRenderingAssistantMessageForTextAndErrorResult(layoutPlan);
//string assistantMessage = "`Provided layout plan, that contains the text to be displayed as HTML`:";
//foreach (var block in layoutPlan.Blocks)
//{
// assistantMessage += $"Block type: {block.Type}, block content: {block.RawContent}";
//}
//int mode = -1;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
//await _cerebrasAPIService.GetCerebrasStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, requestedMenu);
}
else if (AiProvider == "chatgpt")
{
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
var fixedForColons = TextHelper.FixJsonWithoutAI(result);
OnContentReceived?.Invoke(sessionId, fixedForColons, requestedMenu);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else
{
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage + assistantMessage);
}
}
public async Task<LayoutPlan?> DisplayLayoutPlanFromContent(string sessionId, string pageTitle, string interMediateResult, List<HtmlSnippet> htmlToUse, string[]? topics = null, Dictionary<string, string>? photos = null)
{
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: method called for methodResult", sessionId.ToString());
OnStatusChangeReceived?.Invoke(sessionId, "Planning layout based on the provided content");
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: starting text", $"{interMediateResult}");
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: starting snippets count", htmlToUse.Count.ToString());
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: starting photos count", photos.Count.ToString());
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: starting topics count", topics.Length.ToString());
string systemMessage = AiPrompts.LayoutPlanning.GetLayoutPlanningSystemPrompt(htmlToUse, photos, topics);
string userMessage = AiPrompts.LayoutPlanning.GetLayoutPlanningUserPrompt(interMediateResult, pageTitle, photos);
string assistantMessage = "";
//Console.WriteLine($"LAYOUTBUILDER PROMPT: {systemMessage}\n\n");
// 🛡 Retry logic
string aiResponse = string.Empty;
LayoutPlan? layoutPlan = null;
int retry = 0;
AiProvider = GetAiSettings();
while (layoutPlan == null && retry < 2)
{
if (AiProvider == "chatgpt")
{
aiResponse = await _openAIApiService.GetSimpleChatGPTResponse(systemMessage, userMessage, assistantMessage);
}
else if (AiProvider == "cerebras")
{
aiResponse = await _cerebrasAPIService.GetSimpleCerebrasResponse(systemMessage, userMessage, assistantMessage);
}
else if (AiProvider == "deepseek")
{
aiResponse = await _deepSeekApiService.GetSimpleChatGPTResponse(systemMessage, userMessage, assistantMessage);
}
else
{
aiResponse = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userMessage, null);
}
if (string.IsNullOrWhiteSpace(aiResponse))
{
//Console.WriteLine("Empty response from AI.");
return null;
}
try
{
//Console.WriteLine("AI Response:");
//Console.WriteLine(aiResponse);
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: ai Response", $"{aiResponse}");
aiResponse = TextHelper.FixJsonWithoutAI(aiResponse);
aiResponse = TextHelper.RemoveTabs(aiResponse);
layoutPlan = System.Text.Json.JsonSerializer.Deserialize<LayoutPlan>(aiResponse, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
//if (layoutPlan?.Blocks == null || layoutPlan.Blocks.Any(b => string.IsNullOrEmpty(b.Type) || string.IsNullOrEmpty(b.RawContent)))
if (layoutPlan?.Blocks == null || layoutPlan.Blocks.Any(b => string.IsNullOrEmpty(b.Type)))
{
//try to fix with AI)
await _logger.WarnAsync($"DisplayLayoutPlanFromContent: trying to fix with AI", $"{aiResponse}");
layoutPlan = await ValidateAndFixJson<LayoutPlan>(aiResponse, FixJsonWithAI);
if (layoutPlan?.Blocks == null || layoutPlan.Blocks.Any(b => string.IsNullOrEmpty(b.Type) || string.IsNullOrEmpty(b.RawContent)))
{
await _logger.ErrorAsync($"DisplayLayoutPlanFromContent: Invalid block structure in response.", $"{layoutPlan.Blocks.Count}");
layoutPlan = null;
}
}
}
catch (Exception ex)
{
await _logger.ErrorAsync($"DisplayLayoutPlanFromContent: ai Response", ex.Message);
layoutPlan = null;
}
if (layoutPlan == null)
{
retry++;
await _logger.WarnAsync($"DisplayLayoutPlanFromContent: Retrying due to invalid format...", $"{aiResponse}");
}
}
foreach (var block in layoutPlan.Blocks)
{
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: reading blocks", $"{block.Order}, {block.Type}, {block.PreferredSnippetId}");
Console.WriteLine($"{block.Order}, {block.Type}, {block.PreferredSnippetId}");
if (block.ContentMap != null && block.ContentMap.Count > 0)
{
foreach (var key in block.ContentMap.Keys)
{
await _logger.InfoAsync($"DisplayLayoutPlanFromContent: reading ContentMap", $"key {key} : value: {block.ContentMap[key]}");
//Console.WriteLine($"key {key} : value: {block.ContentMap[key]}");
}
}
}
return layoutPlan;
}
//for methodResult
public async Task DisplayHtml(string sessionId, string interMediateResult, string methodToCall = "", string methodParameter = "")//, string[]? topics = null)
{
await _logger.InfoAsync($"DisplayHtml: method called for methodResult", sessionId.ToString());
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
////Console.WriteLine($"DISPLAYHTML Text: {interMediateResult}\n\n");
////Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
string systemMessage = AiPrompts.HtmlRendering.GetHtmlRenderingSystemPromptForMethodResult(_scopedContentService.SelectedLanguage, methodToCall, methodParameter);
string userMessage = "Create a perfect, production ready, structured, responsive Bootstrap 5 webpage content from the provided text, please.";
string assistantMessage = "`Provided text to display as HTML`: `" + interMediateResult + "`";
//int mode = -1;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
await _logger.InfoAsync($"DisplayHtml: streamed response", sessionId.ToString());
await _cerebrasAPIService.GetCerebrasStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else if (AiProvider == "chatgpt")
{
await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else
{
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage + assistantMessage);
}
}
}
}