Compare commits
2 Commits
00417c4cf8
...
f1aedcaa1e
| Author | SHA1 | Date |
|---|---|---|
|
|
f1aedcaa1e | |
|
|
e0325bb4bb |
|
|
@ -197,8 +197,8 @@
|
||||||
<script>
|
<script>
|
||||||
function openContactForm(emailAddress) {
|
function openContactForm(emailAddress) {
|
||||||
console.log(emailAddress);
|
console.log(emailAddress);
|
||||||
if (emailAddress) {
|
if (emailAddress && sessionId) {
|
||||||
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress)
|
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress, sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -337,11 +337,6 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Index()
|
|
||||||
{
|
|
||||||
myHome = this; // Set the static reference to the current instance
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
await _logger.InfoAsync("Index component initialized.", $"{SiteId}");
|
await _logger.InfoAsync("Index component initialized.", $"{SiteId}");
|
||||||
|
|
@ -359,42 +354,25 @@
|
||||||
|
|
||||||
private async void UpdateContent(string receivedSessionId, string content, MenuItem? menuItem)
|
private async void UpdateContent(string receivedSessionId, string content, MenuItem? menuItem)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
HtmlContent.Clear();
|
HtmlContent.Clear();
|
||||||
HtmlContent.Append(content);
|
HtmlContent.Append(content);
|
||||||
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
|
await InvokeAsync(StateHasChanged);
|
||||||
await InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
StateHasChanged();
|
|
||||||
});
|
|
||||||
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateContent failed", ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// private async void UpdateTextContentForVoice(string receivedSessionId, string content)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine("UPDATETEXTCONTENT called");
|
|
||||||
// if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
|
||||||
// {
|
|
||||||
|
|
||||||
// TextContent = content;
|
|
||||||
// await ConvertTextToSpeech(content);
|
|
||||||
// //_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private async void UpdateFinished(string receivedSessionId)
|
private async void UpdateFinished(string receivedSessionId)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine("Content update finished");
|
|
||||||
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
||||||
//await ConvertTextToSpeech();
|
|
||||||
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
||||||
Console.Write(_scopedContentService.CurrentDOM);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateFinished failed", ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ContentChangedInForm()
|
private async Task ContentChangedInForm()
|
||||||
|
|
@ -406,17 +384,16 @@
|
||||||
|
|
||||||
private async void UpdateStatus(string receivedSessionId, string content)
|
private async void UpdateStatus(string receivedSessionId, string content)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
StatusContent = content;
|
StatusContent = content;
|
||||||
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
|
await InvokeAsync(StateHasChanged);
|
||||||
await InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
StateHasChanged();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateStatus failed", ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task Enter(KeyboardEventArgs e)
|
public async Task Enter(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using Radzen;
|
using Radzen;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
|
|
@ -24,10 +25,9 @@ namespace BLAIzor.Components.Pages
|
||||||
[Inject] protected NotificationService NotificationService { get; set; }
|
[Inject] protected NotificationService NotificationService { get; set; }
|
||||||
[Inject] protected CacheService CacheService { get; set; }
|
[Inject] protected CacheService CacheService { get; set; }
|
||||||
|
|
||||||
public static readonly Dictionary<string, MainPageBase> _instances = new();
|
public static readonly ConcurrentDictionary<string, MainPageBase> _instances = new();
|
||||||
|
|
||||||
public string SessionId;
|
public string SessionId;
|
||||||
public static MainPageBase myHome;
|
|
||||||
|
|
||||||
public int SiteId;
|
public int SiteId;
|
||||||
public SiteInfo SiteInfo;
|
public SiteInfo SiteInfo;
|
||||||
|
|
@ -65,29 +65,17 @@ namespace BLAIzor.Components.Pages
|
||||||
{
|
{
|
||||||
// Logic here
|
// Logic here
|
||||||
}
|
}
|
||||||
public async void HandleBrandNameChanged()
|
public void HandleBrandNameChanged()
|
||||||
{
|
{
|
||||||
SelectedBrandName = _scopedContentService.SelectedBrandName;
|
SelectedBrandName = _scopedContentService.SelectedBrandName;
|
||||||
//await InvokeAsync(() =>
|
|
||||||
// {
|
|
||||||
// StateHasChanged();
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
//await Task.Run(() =>
|
|
||||||
//{
|
|
||||||
// StateHasChanged();
|
|
||||||
//}).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex);
|
_ = _logger.ErrorAsync("HandleBrandNameChanged failed", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetMenuList(int siteId)
|
public async Task<string> GetMenuList(int siteId)
|
||||||
|
|
@ -216,7 +204,7 @@ namespace BLAIzor.Components.Pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisplayMenuContent(string input, bool forceUnmodified)
|
public virtual async Task DisplayMenuContent(string input, bool forceUnmodified)
|
||||||
{
|
{
|
||||||
welcomeStage = false;
|
welcomeStage = false;
|
||||||
if (!string.IsNullOrEmpty(UserInput))
|
if (!string.IsNullOrEmpty(UserInput))
|
||||||
|
|
@ -260,13 +248,15 @@ namespace BLAIzor.Components.Pages
|
||||||
|
|
||||||
protected async void UpdateTextContentForVoice(string receivedSessionId, string content)
|
protected async void UpdateTextContentForVoice(string receivedSessionId, string content)
|
||||||
{
|
{
|
||||||
Console.WriteLine("UPDATETEXTCONTENT called");
|
if (receivedSessionId != SessionId) return;
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
|
||||||
{
|
|
||||||
|
|
||||||
TextContent = content;
|
TextContent = content;
|
||||||
|
try
|
||||||
|
{
|
||||||
await ConvertTextToSpeech(content);
|
await ConvertTextToSpeech(content);
|
||||||
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await _logger.ErrorAsync("UpdateTextContentForVoice failed", ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,64 +272,47 @@ namespace BLAIzor.Components.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
[JSInvokable("ProcessAudio2")]
|
[JSInvokable("ProcessAudio2")]
|
||||||
public static async Task ProcessAudio2(string base64Audio, string SessionId)
|
public static async Task ProcessAudio2(string base64Audio, string sessionId)
|
||||||
{
|
{
|
||||||
|
if (!_instances.TryGetValue(sessionId, out var instance)) return;
|
||||||
|
if (!instance.STTEnabled) return;
|
||||||
|
|
||||||
Console.Write("audio incoming");
|
var languageCode = instance._scopedContentService.SelectedLanguage switch
|
||||||
if (myHome != null)
|
|
||||||
{
|
{
|
||||||
if (myHome.STTEnabled == false) return;
|
"English" => "en-US",
|
||||||
Console.WriteLine("STT ENABLED -------------------------------------------------------------------------------");
|
"German" => "de-DE",
|
||||||
var languageCode = "hu-HU";
|
_ => "hu-HU"
|
||||||
if (myHome._scopedContentService.SelectedLanguage == "Hungarian")
|
|
||||||
{
|
|
||||||
languageCode = "hu-HU";
|
|
||||||
}
|
|
||||||
else if (myHome._scopedContentService.SelectedLanguage == "English")
|
|
||||||
{
|
|
||||||
languageCode = "en-US";
|
|
||||||
}
|
|
||||||
else if (myHome._scopedContentService.SelectedLanguage == "German")
|
|
||||||
{
|
|
||||||
languageCode = "de-DE";
|
|
||||||
}
|
|
||||||
var credentialsPath = myHome.configuration.GetSection("GoogleAPI").GetValue<string>("CredentialsPath");
|
|
||||||
Console.Write(credentialsPath);
|
|
||||||
var builder = new SpeechClientBuilder
|
|
||||||
{
|
|
||||||
CredentialsPath = credentialsPath
|
|
||||||
};
|
};
|
||||||
var speech = builder.Build();
|
|
||||||
|
var credentialsPath = instance.configuration.GetSection("GoogleAPI").GetValue<string>("CredentialsPath");
|
||||||
|
var speech = new SpeechClientBuilder { CredentialsPath = credentialsPath }.Build();
|
||||||
|
|
||||||
byte[] audioBytes = Convert.FromBase64String(base64Audio);
|
byte[] audioBytes = Convert.FromBase64String(base64Audio);
|
||||||
myHome.HtmlContent.Clear();
|
instance.HtmlContent.Clear();
|
||||||
|
|
||||||
var response = await speech.RecognizeAsync(new RecognitionConfig
|
var response = await speech.RecognizeAsync(new RecognitionConfig
|
||||||
{
|
{
|
||||||
Encoding = RecognitionConfig.Types.AudioEncoding.Mp3,
|
Encoding = RecognitionConfig.Types.AudioEncoding.Mp3,
|
||||||
SampleRateHertz = 48000, // Match the actual sample rate
|
SampleRateHertz = 48000,
|
||||||
LanguageCode = languageCode
|
LanguageCode = languageCode
|
||||||
}, RecognitionAudio.FromBytes(audioBytes));
|
}, RecognitionAudio.FromBytes(audioBytes));
|
||||||
Console.Write("BILLED: " + response.TotalBilledTime);
|
|
||||||
foreach (var result in response.Results)
|
foreach (var result in response.Results)
|
||||||
{
|
{
|
||||||
//Console.Write("RESULT: " + result.Alternatives.Count);
|
|
||||||
foreach (var alternative in result.Alternatives)
|
foreach (var alternative in result.Alternatives)
|
||||||
{
|
{
|
||||||
//Console.WriteLine($"Transcription: {alternative.Transcript}");
|
await instance.HandleVoiceCommand(alternative.Transcript, sessionId);
|
||||||
await myHome.HandleVoiceCommand(alternative.Transcript, SessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JSInvokable("OpenEmailForm2")]
|
[JSInvokable("OpenEmailForm2")]
|
||||||
public static async void OpenEmailForm2(string emailAddress)
|
public static async Task OpenEmailForm2(string emailAddress, string sessionId)
|
||||||
{
|
{
|
||||||
if (myHome != null)
|
if (_instances.TryGetValue(sessionId, out var instance))
|
||||||
{
|
{
|
||||||
await myHome.DisplayEmailForm(emailAddress);
|
await instance.DisplayEmailForm(emailAddress);
|
||||||
}
|
}
|
||||||
Console.Write("openEmail with: " + emailAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendMessage()
|
public async Task SendMessage()
|
||||||
|
|
@ -377,11 +350,8 @@ namespace BLAIzor.Components.Pages
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
//await CssTemplateService.DeleteSessionCssFile(SessionId);
|
_instances.TryRemove(SessionId, out _);
|
||||||
_scopedContentService.OnBrandNameChanged -= HandleBrandNameChanged;
|
_scopedContentService.OnBrandNameChanged -= HandleBrandNameChanged;
|
||||||
//AIService.OnContentReceived -= UpdateContent;
|
|
||||||
//AIService.OnContentReceiveFinished -= UpdateFinished;
|
|
||||||
//AIService.OnStatusChangeReceived -= UpdateStatus;
|
|
||||||
ChatGptService.OnTextContentAvailable -= UpdateTextContentForVoice;
|
ChatGptService.OnTextContentAvailable -= UpdateTextContentForVoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,10 +373,8 @@
|
||||||
<script>
|
<script>
|
||||||
function openContactForm(emailAddress) {
|
function openContactForm(emailAddress) {
|
||||||
console.log(emailAddress);
|
console.log(emailAddress);
|
||||||
|
if (emailAddress && sessionId) {
|
||||||
if (emailAddress) {
|
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress, sessionId)
|
||||||
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -481,12 +479,6 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Preview()
|
|
||||||
{
|
|
||||||
myHome = this; // Set the static reference to the current instance
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
SessionId = _scopedContentService.SessionId;
|
SessionId = _scopedContentService.SessionId;
|
||||||
|
|
@ -557,33 +549,67 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// UpdateContent(SessionId, HtmlContent.ToString(), currentMenuItem);
|
||||||
|
// }
|
||||||
UserInput = string.Empty;
|
UserInput = string.Empty;
|
||||||
_initVoicePending = true;
|
_initVoicePending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UpdateContent(string receivedSessionId, string content, MenuItem menuItem)
|
private async void UpdateContent(string receivedSessionId, string content, MenuItem menuItem)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
HtmlContent.Clear();
|
HtmlContent.Clear();
|
||||||
HtmlContent.Append(content);
|
HtmlContent.Append(content);
|
||||||
//TODO SAVE TO DB
|
await InvokeAsync(StateHasChanged);
|
||||||
if (menuItem != null)
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateContent failed", ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async override Task DisplayMenuContent(string input, bool forceUnmodified)
|
||||||
|
{
|
||||||
|
welcomeStage = false;
|
||||||
|
if (!string.IsNullOrEmpty(UserInput))
|
||||||
|
{
|
||||||
|
HtmlContent.Clear();
|
||||||
|
var menu = await GetMenuList(SiteId);
|
||||||
|
var menuList = await GetMenuItems(SiteId);
|
||||||
|
|
||||||
|
var menuItem = CompareMenuItemNames(input, menuList);
|
||||||
|
if (menuItem == null)
|
||||||
|
{
|
||||||
|
await ChatGptService.ProcessContentRequest(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu, forceUnmodified);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
currentMenuItem = menuItem;
|
currentMenuItem = menuItem;
|
||||||
// IsContentSaved = string.IsNullOrEmpty(currentMenuItem.StoredHtml) ? false : true;
|
if (!string.IsNullOrEmpty(menuItem.StoredHtml))
|
||||||
// _logger.InfoAsync($"Preview - UpdateContent: {IsContentSaved}");
|
|
||||||
displayOptions = true;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
|
|
||||||
await InvokeAsync(() =>
|
|
||||||
{
|
{
|
||||||
StateHasChanged();
|
|
||||||
});
|
HtmlContent.Clear();
|
||||||
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
HtmlContent.Append(menuItem.StoredHtml);
|
||||||
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
if (currentMenuItem.ContentItemId != null)
|
||||||
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
|
{
|
||||||
|
var content = await _contentEditorService.GetContentItemByIdAsync((int)currentMenuItem.ContentItemId);
|
||||||
|
if (content != null)
|
||||||
|
{
|
||||||
|
string removedNumbers = TextHelper.ReplaceNumbersAndSpecialCharacters(content.Content, _scopedContentService.SelectedLanguage);
|
||||||
|
Console.WriteLine(removedNumbers);
|
||||||
|
UpdateTextContentForVoice(SessionId, removedNumbers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
displayOptions = true;
|
||||||
|
IsContentSaved = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ChatGptService.ProcessContentRequest(SessionId, menuItem, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu, forceUnmodified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UserInput = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,13 +626,13 @@
|
||||||
|
|
||||||
private async void UpdateFinished(string receivedSessionId)
|
private async void UpdateFinished(string receivedSessionId)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine("Content update finished");
|
|
||||||
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
||||||
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
||||||
Console.Write(_scopedContentService.CurrentDOM);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateFinished failed", ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ContentChangedInForm()
|
private async Task ContentChangedInForm()
|
||||||
|
|
@ -618,17 +644,16 @@
|
||||||
|
|
||||||
private async void UpdateStatus(string receivedSessionId, string content)
|
private async void UpdateStatus(string receivedSessionId, string content)
|
||||||
{
|
{
|
||||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
if (receivedSessionId != SessionId) return;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
StatusContent = content;
|
StatusContent = content;
|
||||||
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
|
await InvokeAsync(StateHasChanged);
|
||||||
await InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
StateHasChanged();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { await _logger.ErrorAsync("UpdateStatus failed", ex.Message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task Enter(KeyboardEventArgs e)
|
public async Task Enter(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||||
|
|
|
||||||
|
|
@ -237,9 +237,13 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
_instance = this;
|
if (firstRender)
|
||||||
|
{
|
||||||
|
_dotNetRef = DotNetObjectReference.Create(this);
|
||||||
|
await JS.InvokeVoidAsync("initWizardAudio", _dotNetRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NextStep()
|
private void NextStep()
|
||||||
|
|
@ -483,19 +487,22 @@
|
||||||
|
|
||||||
|
|
||||||
// STT Hook
|
// STT Hook
|
||||||
private static CreateSiteWizard? _instance;
|
private DotNetObjectReference<CreateSiteWizard>? _dotNetRef;
|
||||||
|
|
||||||
[JSInvokable]
|
[JSInvokable]
|
||||||
public static async Task SendAudioToServer(List<byte> audioData)
|
public async Task SendAudioToServer(List<byte> audioData)
|
||||||
{
|
{
|
||||||
if (_instance is null) return;
|
var result = await WhisperService.TranscribeAsync(audioData.ToArray());
|
||||||
|
|
||||||
var result = await _instance.WhisperService.TranscribeAsync(audioData.ToArray());
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(result))
|
if (!string.IsNullOrWhiteSpace(result))
|
||||||
{
|
{
|
||||||
_instance.Steps[_instance.CurrentStep].Answer = result;
|
Steps[CurrentStep].Answer = result;
|
||||||
_instance.StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_dotNetRef?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
Program.cs
36
Program.cs
|
|
@ -21,30 +21,26 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
var configuration = builder.Configuration;
|
var configuration = builder.Configuration;
|
||||||
builder.WebHost.UseWebRoot("wwwroot");
|
builder.WebHost.UseWebRoot("wwwroot");
|
||||||
|
|
||||||
// Add services to the container.
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
|
|
||||||
|
// Add DbContext (scoped, for pages and services)
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
options.UseSqlServer(connectionString));
|
||||||
|
|
||||||
|
// Add DbContextFactory (for background services that need their own scope)
|
||||||
|
builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
|
||||||
|
options.UseSqlServer(connectionString), ServiceLifetime.Scoped);
|
||||||
|
|
||||||
// Add Identity services
|
// Add Identity services
|
||||||
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||||
|
|
||||||
//builder.Services.AddBlazorServerOptions(options =>
|
|
||||||
//{
|
|
||||||
// options.MaxBufferSize = 50 * 1024 * 1024; // Set to 50 MB
|
|
||||||
//});
|
|
||||||
|
|
||||||
builder.Services.Configure<KestrelServerOptions>(options =>
|
builder.Services.Configure<KestrelServerOptions>(options =>
|
||||||
{
|
{
|
||||||
options.Limits.MaxRequestBodySize = 50 * 1024 * 1024;
|
options.Limits.MaxRequestBodySize = 50 * 1024 * 1024;
|
||||||
});
|
});
|
||||||
|
|
||||||
var env = builder.Environment; // This is IWebHostEnvironment
|
var env = builder.Environment;
|
||||||
|
|
||||||
if (env.IsProduction())
|
|
||||||
{
|
|
||||||
// do production-specific setup
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorComponents()
|
builder.Services.AddRazorComponents()
|
||||||
|
|
@ -52,16 +48,6 @@ builder.Services.AddRazorComponents()
|
||||||
|
|
||||||
builder.Services.AddRadzenComponents();
|
builder.Services.AddRadzenComponents();
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
|
||||||
|
|
||||||
// Required for scoped injection (used in pages/services)
|
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
||||||
options.UseSqlServer(connectionString));
|
|
||||||
|
|
||||||
// Required for factory-based logging or background usage
|
|
||||||
builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
|
|
||||||
options.UseSqlServer(connectionString), ServiceLifetime.Scoped);
|
|
||||||
|
|
||||||
builder.Services.AddHttpClient();
|
builder.Services.AddHttpClient();
|
||||||
builder.Services.AddScoped<ISimpleLogger, SimpleLogger>();
|
builder.Services.AddScoped<ISimpleLogger, SimpleLogger>();
|
||||||
builder.Services.AddScoped<AIService>();
|
builder.Services.AddScoped<AIService>();
|
||||||
|
|
@ -91,14 +77,14 @@ builder.Services.AddScoped<CssInjectorService>();
|
||||||
builder.Services.AddScoped<LocalVectorSearchService>();
|
builder.Services.AddScoped<LocalVectorSearchService>();
|
||||||
builder.Services.AddScoped<WebsiteContentLoaderService>();
|
builder.Services.AddScoped<WebsiteContentLoaderService>();
|
||||||
builder.Services.AddScoped<CacheService>();
|
builder.Services.AddScoped<CacheService>();
|
||||||
builder.Services.AddSingleton<CreateSiteWizard>();
|
builder.Services.AddScoped<CreateSiteWizard>();
|
||||||
builder.Services.AddScoped<WhisperTranscriptionService>();
|
builder.Services.AddScoped<WhisperTranscriptionService>();
|
||||||
builder.Services.AddScoped<IBrightDataService, BrightDataService>();
|
builder.Services.AddScoped<IBrightDataService, BrightDataService>();
|
||||||
|
|
||||||
builder.Services.AddHttpClient<ReplicateService>(client =>
|
builder.Services.AddHttpClient<ReplicateService>(client =>
|
||||||
{
|
{
|
||||||
client.DefaultRequestHeaders.Authorization =
|
client.DefaultRequestHeaders.Authorization =
|
||||||
new AuthenticationHeaderValue("Bearer", "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh");
|
new AuthenticationHeaderValue("Bearer", configuration["Replicate:ApiKey"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddHostedService<TempFileCleanupService>();
|
builder.Services.AddHostedService<TempFileCleanupService>();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
public class OpenAIEmbeddingService
|
public class OpenAIEmbeddingService
|
||||||
{
|
{
|
||||||
private readonly string _apiKey = "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";
|
private readonly string _apiKey = "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";
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
public OpenAIEmbeddingService()
|
public OpenAIEmbeddingService()
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ namespace BLAIzor.Services
|
||||||
public class QDrantService
|
public class QDrantService
|
||||||
{
|
{
|
||||||
public static IConfiguration? _configuration;
|
public static IConfiguration? _configuration;
|
||||||
private string qdrantUrl = "https://fe7d5c9e-8cd1-4ad9-af5a-af2bf3b93219.europe-west3-0.gcp.cloud.qdrant.io:6333";
|
//private string qdrantUrl = "https://fe7d5c9e-8cd1-4ad9-af5a-af2bf3b93219.europe-west3-0.gcp.cloud.qdrant.io:6333";
|
||||||
private readonly string _qdrantHost = "fe7d5c9e-8cd1-4ad9-af5a-af2bf3b93219.europe-west3-0.gcp.cloud.qdrant.io";
|
private string qdrantUrl = "http://100.117.141.100:6333";
|
||||||
|
//private readonly string _qdrantHost = "fe7d5c9e-8cd1-4ad9-af5a-af2bf3b93219.europe-west3-0.gcp.cloud.qdrant.io";
|
||||||
|
private readonly string _qdrantHost = "100.117.141.100";
|
||||||
|
|
||||||
private string _apiKey = "";
|
private string _apiKey = "";
|
||||||
|
|
||||||
|
|
@ -44,7 +46,7 @@ namespace BLAIzor.Services
|
||||||
public async Task<int> GetCollectionCount(string collectionName)
|
public async Task<int> GetCollectionCount(string collectionName)
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
var result = await client.CountAsync(
|
var result = await client.CountAsync(
|
||||||
collectionName: collectionName,
|
collectionName: collectionName,
|
||||||
exact: true
|
exact: true
|
||||||
|
|
@ -68,7 +70,7 @@ namespace BLAIzor.Services
|
||||||
public async Task<bool> CollectionExistsAsync(string collectionName)
|
public async Task<bool> CollectionExistsAsync(string collectionName)
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
return await client.CollectionExistsAsync(collectionName);
|
return await client.CollectionExistsAsync(collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +78,7 @@ namespace BLAIzor.Services
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
bool doesExist = await CollectionExistsAsync(collectionName);
|
bool doesExist = await CollectionExistsAsync(collectionName);
|
||||||
if (doesExist)
|
if (doesExist)
|
||||||
{
|
{
|
||||||
|
|
@ -168,7 +170,7 @@ namespace BLAIzor.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
bool doesExist = await client.CollectionExistsAsync(site.VectorCollectionName);
|
bool doesExist = await client.CollectionExistsAsync(site.VectorCollectionName);
|
||||||
if (!doesExist)
|
if (!doesExist)
|
||||||
{
|
{
|
||||||
|
|
@ -249,7 +251,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
List<WebPageContent> pageContent = new();
|
List<WebPageContent> pageContent = new();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
bool doesExist = await client.CollectionExistsAsync(collectionName);
|
bool doesExist = await client.CollectionExistsAsync(collectionName);
|
||||||
|
|
||||||
var result = await client.RetrieveAsync(
|
var result = await client.RetrieveAsync(
|
||||||
|
|
@ -299,7 +301,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
List<WebPageContent> contentList = new();
|
List<WebPageContent> contentList = new();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
var result = await client.RetrieveAsync(
|
var result = await client.RetrieveAsync(
|
||||||
collectionName: $"Site{siteId.ToString()}",
|
collectionName: $"Site{siteId.ToString()}",
|
||||||
id: Convert.ToUInt64(pointId),
|
id: Convert.ToUInt64(pointId),
|
||||||
|
|
@ -367,7 +369,7 @@ namespace BLAIzor.Services
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
|
|
||||||
var doesCollectionExist = await client.CollectionExistsAsync(collectionName);
|
var doesCollectionExist = await client.CollectionExistsAsync(collectionName);
|
||||||
if (doesCollectionExist)
|
if (doesCollectionExist)
|
||||||
|
|
@ -475,7 +477,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
var pointStruct = new PointStruct();
|
var pointStruct = new PointStruct();
|
||||||
|
|
||||||
pointStruct = new PointStruct
|
pointStruct = new PointStruct
|
||||||
|
|
@ -505,7 +507,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
var pointStructList = new List<PointStruct>();
|
var pointStructList = new List<PointStruct>();
|
||||||
for (int i = 0; i < ids.Count; i++)
|
for (int i = 0; i < ids.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -535,7 +537,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
var pointStructList = new List<PointStruct>();
|
var pointStructList = new List<PointStruct>();
|
||||||
for (int i = 0; i < chunks.Count; i++)
|
for (int i = 0; i < chunks.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -576,7 +578,7 @@ namespace BLAIzor.Services
|
||||||
public async Task DeletePointAsync(int pointId, string collectionName)
|
public async Task DeletePointAsync(int pointId, string collectionName)
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
|
|
||||||
var result = await client.DeleteAsync(collectionName: "{collection_name}", ids: [(ulong)pointId]);
|
var result = await client.DeleteAsync(collectionName: "{collection_name}", ids: [(ulong)pointId]);
|
||||||
Console.WriteLine(result.Status);
|
Console.WriteLine(result.Status);
|
||||||
|
|
@ -585,7 +587,7 @@ namespace BLAIzor.Services
|
||||||
public async Task DeletePointsAsync(ulong[] pointIds, string collectionName)
|
public async Task DeletePointsAsync(ulong[] pointIds, string collectionName)
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
|
|
||||||
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
||||||
Console.WriteLine(result.Status);
|
Console.WriteLine(result.Status);
|
||||||
|
|
@ -593,7 +595,7 @@ namespace BLAIzor.Services
|
||||||
public async Task DeletePointsAsync(Guid[] pointIds, string collectionName)
|
public async Task DeletePointsAsync(Guid[] pointIds, string collectionName)
|
||||||
{
|
{
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
|
|
||||||
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
||||||
Console.WriteLine(result.Status);
|
Console.WriteLine(result.Status);
|
||||||
|
|
@ -604,7 +606,7 @@ namespace BLAIzor.Services
|
||||||
|
|
||||||
_apiKey = GetApiKey();
|
_apiKey = GetApiKey();
|
||||||
|
|
||||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
|
||||||
await client.DeleteCollectionAsync(collectionName);
|
await client.DeleteCollectionAsync(collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace BLAIzor.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action OnBrandNameChanged;
|
public event Action? OnBrandNameChanged;
|
||||||
public int SelectedSiteId { get; set; } = 1;
|
public int SelectedSiteId { get; set; } = 1;
|
||||||
|
|
||||||
public WebsiteContentModel WebsiteContentModel { get; set; }
|
public WebsiteContentModel WebsiteContentModel { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,8 @@
|
||||||
"Microsoft.AspNetCore": "Information",
|
"Microsoft.AspNetCore": "Information",
|
||||||
"BLAIzor": "Information"
|
"BLAIzor": "Information"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Replicate": {
|
||||||
|
"ApiKey": "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
},
|
},
|
||||||
"OpenAI": {
|
"OpenAI": {
|
||||||
//"CredentialsPath": "D:\\GOOGLECREDENTIALS\\client_secret_359861037120-m3mjvr3kg51i2c2qb38dav62uuqoqs5k.apps.googleusercontent.com.json"
|
//"CredentialsPath": "D:\\GOOGLECREDENTIALS\\client_secret_359861037120-m3mjvr3kg51i2c2qb38dav62uuqoqs5k.apps.googleusercontent.com.json"
|
||||||
"ApiKey": "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",
|
"ApiKey": "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",
|
||||||
//"ApiKey": "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" //VG3Law
|
//"ApiKey": "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" //VG3Law
|
||||||
//"Model": "gpt-4.1-mini"
|
//"Model": "gpt-4.1-mini"
|
||||||
//"Model": "gpt-4o-mini"
|
//"Model": "gpt-4o-mini"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
let mediaRecorder;
|
let mediaRecorder;
|
||||||
let recordedChunks = [];
|
let recordedChunks = [];
|
||||||
|
let wizardDotNetRef = null;
|
||||||
|
|
||||||
|
window.initWizardAudio = (dotnetRef) => {
|
||||||
|
wizardDotNetRef = dotnetRef;
|
||||||
|
};
|
||||||
|
|
||||||
window.startRecording = async () => {
|
window.startRecording = async () => {
|
||||||
recordedChunks = [];
|
recordedChunks = [];
|
||||||
|
|
@ -18,8 +23,9 @@ window.startRecording = async () => {
|
||||||
const arrayBuffer = await blob.arrayBuffer();
|
const arrayBuffer = await blob.arrayBuffer();
|
||||||
const byteArray = new Uint8Array(arrayBuffer);
|
const byteArray = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
// Send to Blazor server
|
if (wizardDotNetRef) {
|
||||||
DotNet.invokeMethodAsync('BLAIzor', 'SendAudioToServer', Array.from(byteArray));
|
wizardDotNetRef.invokeMethodAsync('SendAudioToServer', Array.from(byteArray));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mediaRecorder.start();
|
mediaRecorder.start();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue