1082 lines
57 KiB
C#
1082 lines
57 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;
|
||
|
||
namespace BLAIzor.Services
|
||
{
|
||
public class AIService
|
||
{
|
||
private readonly HttpClient _httpClient;
|
||
private readonly ContentService _contentService;
|
||
private readonly ScopedContentService _scopedContentService;
|
||
private readonly OpenAIEmbeddingService _openAIEmbeddingService;
|
||
private readonly OpenAIApiService _openAIApiService;
|
||
private readonly OpenAiRealtimeService _openAIRealtimeService;
|
||
private readonly DeepSeekApiService _deepSeekApiService;
|
||
private readonly CerebrasAPIService _cerebrasAPIService;
|
||
private readonly QDrantService _qDrantService;
|
||
private readonly NavigationManager _navigationManager;
|
||
|
||
|
||
public static IConfiguration? _configuration;
|
||
|
||
|
||
public AIService(HttpClient httpClient, ContentService contentService, ScopedContentService scopedContentService, QDrantService qDrantService, OpenAIEmbeddingService openAIEmbeddingService, OpenAIApiService openAIApiService, DeepSeekApiService deepSeekApiService, OpenAiRealtimeService openAIRealtimeService, CerebrasAPIService cerebrasAPIService, NavigationManager navigationManager, IConfiguration? configuration)
|
||
{
|
||
_httpClient = httpClient;
|
||
_contentService = contentService;
|
||
_scopedContentService = scopedContentService;
|
||
_qDrantService = qDrantService;
|
||
_openAIEmbeddingService = openAIEmbeddingService;
|
||
_openAIApiService = openAIApiService;
|
||
_deepSeekApiService = deepSeekApiService;
|
||
_openAIRealtimeService = openAIRealtimeService;
|
||
_cerebrasAPIService = cerebrasAPIService;
|
||
_navigationManager = navigationManager;
|
||
_configuration = configuration;
|
||
|
||
_openAIApiService.RegisterCallback(HandleActionInvoked, HandleFinishedInvoked, HandleErrorInvoked);
|
||
_cerebrasAPIService.RegisterCallback(HandleActionInvoked, HandleFinishedInvoked, HandleErrorInvoked);
|
||
_openAIRealtimeService.RegisterCallback(HandleActionInvoked);
|
||
}
|
||
|
||
private const string OpenAiEndpoint = "https://api.openai.com/v1/chat/completions";
|
||
public string _apiKey;
|
||
public static event Action<string, string>? OnContentReceived;
|
||
public static event Action<string>? OnContentReceiveFinished;
|
||
public static event Action<string, string>? OnContentReceivedError;
|
||
public static event Action<string, string>? OnStatusChangeReceived;
|
||
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 void HandleActionInvoked(string sessionId, string streamedHtmlContent)
|
||
{
|
||
OnContentReceived?.Invoke(sessionId, streamedHtmlContent);
|
||
}
|
||
|
||
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 GetChatGptWelcomeMessage(string sessionId, int SiteId, string menuList = "")
|
||
{
|
||
string currentUri = _navigationManager.Uri;
|
||
Console.Write($"\n\n SessionId: {sessionId}\n\n");
|
||
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
|
||
|
||
_apiKey = GetApiKey();
|
||
string qdrantPoint = await _qDrantService.GetContentAsync(SiteId, 0);
|
||
string extractedText = "";
|
||
//TODO: this is the full object, should get the text from it, it sends the vectors too at the moment
|
||
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(qdrantPoint)!;
|
||
if (selectedPoint != null)
|
||
{
|
||
extractedText = selectedPoint.result.payload.content;
|
||
//Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.payload.content} \n");
|
||
Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.id} \n");
|
||
}
|
||
|
||
|
||
string systemMessage = "You are a helpful, " + Mood + " assistant that welcomes the user in the name of the brand or person described by the content, on a website of " + _scopedContentService.SelectedBrandName + " in " + _scopedContentService.SelectedLanguage + ". Use the following content: `" +
|
||
extractedText + "` " +
|
||
//"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 <h1> 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 <h1> 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." +
|
||
"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. " +
|
||
"Do not include anything else than the html title and text elements, no css, no scripts, no head or other tags." +
|
||
"Do not mark your answer with ```html or any other mark.";
|
||
string userMessage = "Hello";
|
||
string streamedHtmlContent = string.Empty;
|
||
|
||
if (!UseWebsocket)
|
||
{
|
||
|
||
AiProvider = GetAiSettings();
|
||
if (AiProvider == "cerebras")
|
||
{
|
||
await _cerebrasAPIService.GetCerebrasStreamedResponse(sessionId, systemMessage, userMessage);
|
||
}
|
||
else if (AiProvider == "chatgpt")
|
||
{
|
||
await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage);
|
||
|
||
}
|
||
else if (AiProvider == "deepseek")
|
||
{
|
||
//await _deepSeekApiService.GetChatGPTStreamedResponse(systemMessage, userMessage);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage);
|
||
|
||
}
|
||
|
||
//_scopedContentService.CurrentDOM = streamedHtmlContent;
|
||
//Console.Write("Answer: " + streamedHtmlContent);
|
||
//return streamedHtmlContent;
|
||
|
||
}
|
||
|
||
public async Task ProcessUserIntent(string sessionId, string userPrompt, int siteId, int templateId, string collectionName, string menuList = "")
|
||
{
|
||
Console.WriteLine($"SITE ID: {siteId}");
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Understanding your request...");
|
||
|
||
// Get JSON result based on siteId presence
|
||
string resultJson = siteId >= 0
|
||
? await GetJsonResultFromQuery(sessionId, siteId, userPrompt)
|
||
: await GetJsonResultFromQuery(sessionId, 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;
|
||
}
|
||
|
||
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, resultJson, templateId, collectionName);
|
||
break;
|
||
|
||
case "examinationresult":
|
||
await ProcessExaminationResult(sessionId, resultJson, templateId, collectionName);
|
||
break;
|
||
|
||
case "errorresult":
|
||
await ProcessErrorResult(sessionId, resultJson);
|
||
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, string requestedMenuName, int siteId, int templateId, string collectionName, 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);
|
||
|
||
float[] vector = [];
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
|
||
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(requestedMenuName);
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
|
||
var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 1);
|
||
|
||
string extractedText = "";
|
||
if (pointId.Length > 0)
|
||
{
|
||
foreach (var item in pointId)
|
||
{
|
||
string qDrantData = await _qDrantService.GetContentAsync(siteId, item);
|
||
QDrantGetContentPointResult _selectedPoint = new QDrantGetContentPointResult();
|
||
|
||
if (qDrantData != null)
|
||
{
|
||
_selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(qDrantData)!;
|
||
}
|
||
extractedText += _selectedPoint.result.payload.name + ": " + _selectedPoint.result.payload.content + ", ";
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
|
||
}
|
||
|
||
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, contentJson, templateId, collectionName);
|
||
}
|
||
|
||
|
||
|
||
// Refactored helper methods
|
||
|
||
private async Task ProcessMethodResult(string sessionId, string resultJson)
|
||
{
|
||
var fixedResult = await ValidateAndFixJson<ChatGPTMethodResult>(resultJson, FixJsonWithAI);
|
||
//var methodResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTMethodResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||
if (fixedResult != null)
|
||
{
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Initiating the task you requested");
|
||
await DisplayHtml(sessionId, fixedResult.Text, fixedResult.MethodToCall, fixedResult.Parameter, "");
|
||
}
|
||
}
|
||
|
||
private async Task ProcessTextResult(string sessionId, string resultJson, int templateId, string collectionName)
|
||
{
|
||
var fixedResult = await ValidateAndFixJson<ChatGPTTextResult>(resultJson, FixJsonWithAI);
|
||
//var textResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTTextResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||
if (fixedResult != null)
|
||
{
|
||
string contentJson = await GetContentFromQuery(sessionId, fixedResult.Text, _workingContent);
|
||
Console.Write("\r \n ProcessTextResult: Content: " + contentJson + "\r \n");
|
||
await ProcessContent(sessionId, contentJson, templateId, collectionName);
|
||
}
|
||
}
|
||
|
||
public async Task<T?> ValidateAndFixJson<T>(string json, Func<string, Task<string>> aiFixer)
|
||
{
|
||
try
|
||
{
|
||
return System.Text.Json.JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions
|
||
{
|
||
PropertyNameCaseInsensitive = true
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"❌ JSON parse failed: {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)
|
||
{
|
||
Console.WriteLine($"❌ 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 ""; }
|
||
|
||
//return await _openAIApiService.GetSimpleChatGPTResponseNoSession("You are a JSON-fixing assistant.", prompt);
|
||
}
|
||
|
||
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)
|
||
{
|
||
var fixedResult = await ValidateAndFixJson<ChatGPTExaminationResult>(resultJson, FixJsonWithAI);
|
||
//var explanationResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTExaminationResult>(resultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||
if (fixedResult != null)
|
||
{
|
||
string contentJson = await GetExplanationFromQuery(sessionId, fixedResult.Text, _scopedContentService.CurrentDOM);
|
||
await ProcessContent(sessionId, contentJson, templateId, collectionName);
|
||
}
|
||
}
|
||
|
||
private async Task ProcessContent(string sessionId, string contentJson, int templateId, string collectionName)
|
||
{
|
||
try
|
||
{
|
||
var fixedResult = await ValidateAndFixJson<ChatGPTContentResult>(contentJson, FixJsonWithAI);
|
||
//var contentResult = 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");
|
||
|
||
string snippets = await GetSnippetsForDisplay(sessionId, templateId, fixedResult.Text, collectionName);
|
||
await DisplayHtml(sessionId, fixedResult.Text, "", "", snippets, fixedResult.Topics, fixedResult.Photos);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Error processing content: {ex.Message}");
|
||
OnContentReceived?.Invoke(sessionId, ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task ProcessErrorResult(string sessionId, string resultJson)
|
||
{
|
||
var errorResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTErrorResult>(resultJson);
|
||
if (errorResult != null)
|
||
{
|
||
Console.WriteLine($"Error Result: {errorResult.Text}");
|
||
await DisplayHtml(sessionId, errorResult.Text, "", "", "");
|
||
}
|
||
}
|
||
|
||
|
||
/// <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)
|
||
{
|
||
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. 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: " +
|
||
"- Turn the following content into a nice informative webpage content. " +
|
||
"- Structure it nicely without leaving out any information. " +
|
||
"*** 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.";
|
||
}
|
||
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, 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." +
|
||
"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
|
||
"- For missing information: Inform the user and provide a clarification. " +
|
||
//"- 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// What does the user want? Answer or action?
|
||
/// </summary>
|
||
/// <param name="userPrompt"></param>
|
||
/// <returns></returns>
|
||
public async Task<string> GetJsonResultFromQuery(string sessionId, string userPrompt)
|
||
{
|
||
string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _scopedContentService.SelectedDocument);
|
||
_apiKey = GetApiKey();
|
||
string extractedText = WordFileReader.ExtractText(rootpath);
|
||
|
||
Console.Write("GetJSONResult called!");
|
||
|
||
var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a JSON object in the following formats:\r\n\r\n1. " +
|
||
"1. MethodResult:\r\n " +
|
||
"- `type`: A string with value `methodresult`.\r\n " +
|
||
"- `text`: A string explaining the result.\r\n " +
|
||
"- `methodToCall`: One of these values: " +
|
||
"[openContactForm, openCalendar, openApplicationForm].\r\n" +
|
||
"- `parameter`: One of these: \r\n" +
|
||
"[email address for openContactForm, calendlyUserName for openCalendar, empty string for openApplicationForm]" +
|
||
"2. TextResult:\r\n " +
|
||
"- `type`: A string with value `textresult`.\r\n " +
|
||
"- `text`: Contains the user query without any modification.\r\n " +
|
||
"3. ExaminationResult:\r\n " +
|
||
"- `type`: A string with value `examinationresult`.\r\n " +
|
||
"- `text`: Contains the user query without any modification.\r\n " +
|
||
"4. ErrorResult:\r\n " +
|
||
"- `type`: A string with value `errorresult`. \r\n " +
|
||
"- `text`: The description of the problem you found. " +
|
||
"**Document-Specific Instructions**:\r\n- Base responses solely on the following initial document: {" + extractedText + "}.\r\n" +
|
||
"**Rules for Decision Making**:\r\n" +
|
||
"- If the user’s input indicates a method invocation, and you find the relevant parameter in the initial document, generate a `methodresult`.\r\n" +
|
||
" In the explanation, put a short sentence about what the user has requested by your understanding. \r\n" +
|
||
"- If the user asks about the current content displayed for them, generate an examinationResult. \r\n" +
|
||
"- If you don't find the relevant parameter in the initial document, generate an errorResult. \r\n" +
|
||
//"- If the user asks for contact form but the initial document doesn't contain contact email, generate an errorResult. \r\n"+
|
||
|
||
"- Otherwise, create a `textresult` with the unmoddified user query.\r\n\r\n" +
|
||
|
||
"Do NOT mark your answer with anything like `````json or such.";
|
||
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("Result decision: " + interMediateResult);
|
||
return interMediateResult;
|
||
}
|
||
|
||
public async Task<string> GetJsonResultFromQuery(string sessionId, int siteId, string userPrompt)
|
||
{
|
||
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
|
||
//_apiKey = GetApiKey();
|
||
//start with embeddings
|
||
float[] vector = [];
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
|
||
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(userPrompt);
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
|
||
var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 3);
|
||
|
||
string extractedText = "Sections: ";
|
||
if (pointId.Length > 0)
|
||
{
|
||
foreach (var item in pointId)
|
||
{
|
||
string qDrantData = await _qDrantService.GetContentAsync(siteId, item);
|
||
QDrantGetContentPointResult selectedPoint = new QDrantGetContentPointResult();
|
||
|
||
if (qDrantData != null)
|
||
{
|
||
selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(qDrantData)!;
|
||
}
|
||
extractedText += selectedPoint.result.payload.name + ": " + selectedPoint.result.payload.content + ", ";
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
|
||
}
|
||
|
||
_workingContent = extractedText;
|
||
Console.Write("\r \n GetJsonResultFromQuery: Working content: " + _workingContent + "\r \n");
|
||
//string extractedText = WordFileReader.ExtractText(rootpath);
|
||
|
||
Console.Write("GetJSONResult called!");
|
||
|
||
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" +
|
||
"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" +
|
||
|
||
"**Response format:**\r\n" +
|
||
"Strictly respond in " + _scopedContentService.SelectedLanguage + " as a JSON object, using one of the following formats:\r\n" +
|
||
|
||
"1. **chatGPTMethodResult** (for initiating actions):\r\n" +
|
||
" - `type`: \"methodresult\"\r\n" +
|
||
" - `text`: A short explanation of what the user wants to do.\r\n" +
|
||
" - `methodToCall`: One of: [openContactForm, openCalendar, openApplicationForm]\r\n" +
|
||
" - `parameter`: [email address for openContactForm, calendlyUserName for openCalendar, empty string for openApplicationForm]\r\n\r\n" +
|
||
|
||
"2. **chatGPTTextResult** (for general website content searches):\r\n" +
|
||
" - `type`: \"textresult\"\r\n" +
|
||
" - `text`: The user’s unmodified query.\r\n\r\n" +
|
||
|
||
"3. **chatGPTExaminationResult** (for analyzing the currently displayed page only):\r\n" +
|
||
" - `type`: \"examinationresult\"\r\n" +
|
||
" - `text`: The user’s unmodified query.\r\n\r\n" +
|
||
|
||
"4. **chatGPTErrorResult** (for errors):\r\n" +
|
||
" - `type`: \"errorresult\"\r\n" +
|
||
" - `text`: A description of the issue encountered.\r\n\r\n" +
|
||
|
||
"**Decision Rules:**\r\n" +
|
||
"- If the user is **searching for website content** beyond what is currently displayed (e.g., 'Find information about our services'), return a `textresult`.\r\n" +
|
||
"- If the user is **asking about the currently visible content** (e.g., 'What is shown on the page?'), return an `examinationresult`.\r\n" +
|
||
"- If the user wants to **perform an action**, return a `methodresult`.\r\n" +
|
||
"- If the required parameter is missing, return an `errorresult`.\r\n\r\n" +
|
||
|
||
"**Examples:**\r\n" +
|
||
"- User asks: 'Show me information about pricing' → `textresult`\r\n" +
|
||
"- User asks: 'What is displayed right now?' → `examinationresult`\r\n" +
|
||
"- User asks: 'Open the contact form' → `methodresult`\r\n" +
|
||
"- User asks: 'Contact support' but no email is found → `errorresult`\r\n\r\n" +
|
||
|
||
"**Context:**\r\n" +
|
||
"- Base responses on this initial document: {" + extractedText + "}\r\n" +
|
||
"- Current displayed HTML: {" + _scopedContentService.CurrentDOM + "}\r\n" +
|
||
|
||
"**IMPORTANT:**\r\n" +
|
||
"- If the request is about general content, **DO NOT use 'examinationresult'**.\r\n" +
|
||
"- If the request is about the currently displayed page, **DO NOT use 'textresult'**.\r\n" +
|
||
"- Do NOT format the response with markdown, code blocks, or `json` tags, do not add any title, or explanation besides the plain json object";
|
||
|
||
|
||
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<string> GetSnippetsForDisplay(string sessionId, int templateId, string interMediateResult, string collectionName)
|
||
{
|
||
_apiKey = GetApiKey();
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Looking up the UI template elements for you");
|
||
string availableSnippetList = "";
|
||
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);
|
||
|
||
|
||
availableSnippetList += ("- " + x.result.payload.Name + ": " + x.result.payload.Description + ".\r\n");
|
||
}
|
||
Console.Write(availableSnippetList);
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Fitting content to available UI elements");
|
||
var systemMessage = "You are a helpful assistant for generating responses using HTML templates. Analyze the user query and choose the most suitable snippet type(s) for rendering the response." +
|
||
"Respond with only the snippet name(s), comma-separated. No explanation. Identify the most suitable " +
|
||
"HTML code snippet type to use for rendering your response based on user queries. The available snippet types are: " +
|
||
availableSnippetList +
|
||
//"The content to answer from from is here: " + extractedText + ". " +
|
||
"If multiple snippets apply, list them in order of priority. ";
|
||
|
||
var userMessage = "How would you render the following text in html: " + interMediateResult + " ? ";
|
||
|
||
string result = string.Empty;
|
||
if (!UseWebsocket)
|
||
{
|
||
|
||
AiProvider = GetAiSettings();
|
||
if (AiProvider == "cerebras")
|
||
{
|
||
result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage);
|
||
}
|
||
else if (AiProvider == "chatgpt")
|
||
{
|
||
result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
|
||
|
||
}
|
||
else if (AiProvider == "deepseek")
|
||
{
|
||
result = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
result = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userMessage);
|
||
}
|
||
|
||
//Console.Write(result);
|
||
List<string> snippetList = result.Split(new[] { ',' }).ToList();
|
||
|
||
int i = 0;
|
||
|
||
OnStatusChangeReceived?.Invoke(sessionId, "Oooh got it! Loading UI elements from the design database");
|
||
//Console.Write("ChatGPT decided!!!! ");
|
||
//let's send result to embeddingservice
|
||
|
||
List<float[]> vectorsList = new List<float[]>();
|
||
|
||
foreach (var snippet in snippetList)
|
||
{
|
||
var vectors = await _openAIEmbeddingService.GenerateEmbeddingAsync(result);
|
||
vectorsList.Add(vectors);
|
||
}
|
||
|
||
List<int> pointIds = new List<int>();
|
||
var collectionCount = await _qDrantService.GetCollectionCount(collectionName);
|
||
|
||
if (collectionCount > 0)
|
||
{
|
||
foreach (var vector in vectorsList)
|
||
{
|
||
var qDrantresult = await _qDrantService.QuerySnippetAsync(vector, 1, collectionName);
|
||
pointIds.Add(qDrantresult);
|
||
}
|
||
|
||
List<string> qDrantDataList = new List<string>();
|
||
|
||
foreach (var pointId in pointIds)
|
||
{
|
||
var qDrantData = await _qDrantService.GetSnippetAsync(pointId, collectionName);
|
||
qDrantDataList.Add(qDrantData);
|
||
}
|
||
|
||
List<QDrantGetPointResult> selectedPointList = new List<QDrantGetPointResult>();
|
||
if (qDrantDataList != null)
|
||
{
|
||
foreach (var x in qDrantDataList)
|
||
{
|
||
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetPointResult>(x)!;
|
||
selectedPointList.Add(selectedPoint);
|
||
}
|
||
}
|
||
|
||
string htmlToUse = "Your snippets to use: ";
|
||
foreach (var selectedPoint in selectedPointList)
|
||
{
|
||
htmlToUse += selectedPoint.result.payload.Name + ": " + selectedPoint.result.payload.Html + ", ";
|
||
|
||
}
|
||
//Console.Write(htmlToUse);
|
||
return htmlToUse;
|
||
}
|
||
|
||
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<string, string>? 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 `<h1>` 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., `<head>`, `<body>`, or `<script>` tags). ");
|
||
|
||
// if (!string.IsNullOrEmpty(methodToCall))
|
||
// {
|
||
// lst.AppendLine("Create a single clickable Bootstrap button (`btn btn-primary`) that calls the JavaScript function `" + methodToCall + "` with '" + methodParameter + "' on click. " +
|
||
// "Place this button at the end of the generated HTML. " +
|
||
// "Do NOT mark your answer with anything like `````html or such.");
|
||
// }
|
||
// else
|
||
// {
|
||
// if (!string.IsNullOrEmpty(htmlToUse))
|
||
// {
|
||
// lst.AppendLine("Use the following snippets when applicable: \n \n " + htmlToUse + ". \n \n For other content, use appropriate Bootstrap elements. " +
|
||
// "Structure the content using Bootstrap layouts: " +
|
||
|
||
// //"If there is a `styles to be applied` section in the content, include it as a `<style>` tag at the beginning of the HTML. " +
|
||
// " - Use 'row justify-content-center' for layout, 'col-xs-12 col-sm-6 col-md-3 text-center' for product cards, and 'img-fluid' for images. \r\n" +
|
||
// " - Separate logical sections using Bootstrap utilities like `clearfix`. Avoid unrelated elements and the use of the `d-grid` class inside `col` elements. \r\n" +
|
||
// " - DO NOT use d-grid class. Ever. \r\n" +
|
||
// "If you render a button you have two options: \r\n" +
|
||
// "1. Render a button for a url you found in the text\r\n" +
|
||
// "2. Any other button, which is not based on a url found in the content should call the JavaScript function `callAI({topic})` on click. \r\n" +
|
||
// "Do NOT generate fake image urls if there is no image url provided. \r\n");
|
||
// }
|
||
// if (photos != null)
|
||
// {
|
||
// lst.AppendLine("- Include related photo URLs from the following list where applicable without modifying the urls. " +
|
||
// "Photo urls: " + string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + ". \r\n" +
|
||
// "DO NOT modifiy the photo urls in any way.");
|
||
// }
|
||
|
||
// lst.AppendLine("- NEVER render image elements if you didn't find any image url in the `photo list`" +
|
||
// "- Maintain the original text content; do not moddify names, or mix information, only convert it into Bootstrap-styled HTML. \r\n");
|
||
|
||
|
||
// if (topics != null && topics.Any())
|
||
// {
|
||
// lst.AppendLine("- Here are the topics: " + string.Join(", ", topics) + ". Create a button for each topic using the `btn btn-primary` class." +
|
||
// "Each button should call the JavaScript function `callAI({topicName})` on click, and these buttons should appear at the end of the generated HTML. \r\n");
|
||
// }
|
||
// else
|
||
// {
|
||
// lst.AppendLine("- No topics are provided. Do not generate navigational topic buttons.");
|
||
// }
|
||
|
||
// }
|
||
// lst.AppendLine("- Do not add explanations and do not mark your response with ```html or similar syntax.");
|
||
// string systemMesage = lst.ToString();
|
||
// string userMessage = "Make a nice bootstrap 5 page content from the provided text, please.";
|
||
// string assistantMessage = "`Provided text to display as html`: `" + interMediateResult + "`";
|
||
// if (!UseWebsocket)
|
||
// {
|
||
// await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMesage, userMessage, assistantMessage);
|
||
// }
|
||
// else
|
||
// {
|
||
// await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMesage, userMessage + assistantMessage);
|
||
// }
|
||
|
||
// //return response;
|
||
//}
|
||
|
||
public async Task DisplayHtml(string sessionId, string interMediateResult, string methodToCall = "", string methodParameter = "", string htmlToUse = "", string[]? topics = null, Dictionary<string, string>? photos = null)
|
||
{
|
||
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 Snippets: {htmlToUse}\n\n");
|
||
Console.WriteLine($"DISPLAYHTML Photos: {photos} \n\n");
|
||
Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
|
||
|
||
StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML content in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" +
|
||
"### Rules to Follow: \n" +
|
||
"- Please generate clean and structured HTML that fits inside a Bootstrap container.\n" +
|
||
"- DO NOT include `<head>`, `<body>`, or `<script>` tags—only content inside the Bootstrap container.\n" +
|
||
"- Use `<h1 class='p-3'>` for the title, based on the user's query.\n" +
|
||
"- Structure content using **separate `row` elements** for different sections.\n" +
|
||
"- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n");
|
||
|
||
if (!string.IsNullOrEmpty(methodToCall))
|
||
{
|
||
lst.AppendLine("- At the END of the content, include a **single** Bootstrap button (`btn btn-primary`) that calls `" + methodToCall + "` with `" + methodParameter + "` on click.\n");
|
||
}
|
||
else
|
||
{
|
||
if (!string.IsNullOrEmpty(htmlToUse))
|
||
{
|
||
lst.AppendLine(
|
||
"### Using Provided Snippets:\n" +
|
||
"- You have been given **multiple HTML snippets**:\n \n " + htmlToUse + ". \n \n **DO NOT merge them into one**.\n" +
|
||
"- Use each snippet **as a separate section** inside its own `div.row`.\n" +
|
||
"- Maintain their order and **DO NOT omit any snippet**.\n" +
|
||
"- Example structure:\n\n" +
|
||
" ```html\n" +
|
||
" <div class='container'>\n" +
|
||
" <div class='row'>\n" +
|
||
" <!-- First snippet -->\n" +
|
||
|
||
" </div>\n" +
|
||
" <div class='row'>\n" +
|
||
" <!-- Second snippet -->\n" +
|
||
" </div>\n" +
|
||
" </div>\n" +
|
||
" ```\n\n" +
|
||
"- Use Bootstrap classes to ensure proper spacing and alignment.\n" +
|
||
" - Use 'row justify-content-center' for layout, 'col-xx-x' classes for columns (IF multiple columns are needed), and always use 'img-fluid' class for images. \r\n" +
|
||
"- If a snippet contains a button, ensure it is placed inside a `div.text-center` for proper alignment.\n");
|
||
}
|
||
|
||
if (photos != null && photos.Any())
|
||
{
|
||
lst.AppendLine(
|
||
"### Handling Photos:\n" +
|
||
"- ONLY use the provided photo URLs **as they are**, without modification.\n" +
|
||
"- Do **NOT** generate or assume image URLs.\n" +
|
||
"- Use these photos where appropriate:\n" +
|
||
" " + string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + "\n\n" +
|
||
"- Example usage:\n" +
|
||
//" ```html\n" +
|
||
" <img src='" + photos.First().Value + "' class='img-fluid' alt='" + photos.First().Key + "' />\n" +
|
||
//" ```\n\n" +
|
||
"DO NOT modifiy the photo urls in any way."
|
||
);
|
||
}
|
||
|
||
if (topics != null && topics.Any())
|
||
{
|
||
lst.AppendLine(
|
||
"### Generating Topic Buttons:\n" +
|
||
"Start this section with a title `Related` " +
|
||
"- Create a **separate button** for each topic.\n" +
|
||
$"- Make sure the topics are in {_scopedContentService.SelectedLanguage}, if not, translate them." +
|
||
"- Each button should use `btn btn-primary` and call `callAI('{topicName}')` on click.\n" +
|
||
"- List of topics:\n" +
|
||
" " + string.Join(", ", topics) + "\n\n" +
|
||
"- Example:\n" +
|
||
" <button class='btn btn-primary' onclick='callAI(\"" + topics.FirstOrDefault() + "\")'>" + topics.FirstOrDefault() + "</button>\n" +
|
||
"Put this section always as last, on the bottom of the page.\n"
|
||
);
|
||
|
||
}
|
||
else
|
||
{
|
||
lst.AppendLine("- **No topics provided** → **Do NOT generate topic buttons.**");
|
||
}
|
||
}
|
||
|
||
lst.AppendLine(
|
||
"- DO **NOT** merge different content sections.\n" +
|
||
"- DO **NOT** wrap the entire content in a single `div`—use separate `row` divs.\n" +
|
||
"- DO **NOT** modify the photo urls in any way." +
|
||
"- Do **NOT** generate or assume new image URLs.\n" +
|
||
"- If the snippet contains an image, but there is no photo url available, skip the image element." +
|
||
"- **Never** add explanations or start with ```html syntax markers.\n");
|
||
|
||
string systemMessage = lst.ToString();
|
||
string userMessage = "Create a perfect, production ready, structured Bootstrap 5 page 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 _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);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
}
|
||
|