Compare commits

...

2 Commits

Author SHA1 Message Date
Adam f1aedcaa1e refactor: Session 1 - critical safety bug fixes
- Remove hardcoded Replicate API key -> appsettings.Development.json
- Replace static Dictionary with ConcurrentDictionary for thread safety
- Remove static myHome field; ProcessAudio2 and OpenEmailForm2 now
  use session-keyed _instances lookup for correct multi-user routing
- Register _instances cleanup in DisposeAsync
- CreateSiteWizard: Singleton -> Scoped, static _instance replaced
  with DotNetObjectReference for per-circuit JS interop
- Remove duplicate DbContext registration from Program.cs
- HandleBrandNameChanged: remove spurious async void
- UpdateTextContentForVoice: add try/catch to contain exceptions
- UI event handlers (UpdateContent/Finished/Status): early-return
  guards + try/catch with _logger instead of silent failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:44:56 +01:00
Adam e0325bb4bb chore: rotate OpenAI key, point Qdrant at local dev instance
Switch Qdrant from cloud (GCP europe-west3) to local Tailscale
host (100.117.141.100) with TLS disabled. Rotate OpenAI API key
in appsettings and embedding service.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:44:35 +01:00
11 changed files with 175 additions and 199 deletions

View File

@ -197,8 +197,8 @@
<script>
function openContactForm(emailAddress) {
console.log(emailAddress);
if (emailAddress) {
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress)
if (emailAddress && sessionId) {
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress, sessionId)
}
}
</script>
@ -337,11 +337,6 @@
StateHasChanged();
}
public Index()
{
myHome = this; // Set the static reference to the current instance
}
protected override async Task OnInitializedAsync()
{
await _logger.InfoAsync("Index component initialized.", $"{SiteId}");
@ -359,42 +354,25 @@
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.Append(content);
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
await InvokeAsync(() =>
{
StateHasChanged();
});
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
await InvokeAsync(StateHasChanged);
}
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)
{
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");
//await ConvertTextToSpeech();
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
Console.Write(_scopedContentService.CurrentDOM);
}
catch (Exception ex) { await _logger.ErrorAsync("UpdateFinished failed", ex.Message); }
}
private async Task ContentChangedInForm()
@ -406,17 +384,16 @@
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;
//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)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
@ -451,4 +428,4 @@
}
}
}

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Mvc;
using Microsoft.JSInterop;
using Radzen;
using System.Collections.Concurrent;
using System.Text;
using System.Text.Json;
@ -24,10 +25,9 @@ namespace BLAIzor.Components.Pages
[Inject] protected NotificationService NotificationService { 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 static MainPageBase myHome;
public int SiteId;
public SiteInfo SiteInfo;
@ -65,29 +65,17 @@ namespace BLAIzor.Components.Pages
{
// Logic here
}
public async void HandleBrandNameChanged()
public void HandleBrandNameChanged()
{
SelectedBrandName = _scopedContentService.SelectedBrandName;
//await InvokeAsync(() =>
// {
// StateHasChanged();
// });
try
{
StateHasChanged();
//await Task.Run(() =>
//{
// StateHasChanged();
//}).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(ex);
_ = _logger.ErrorAsync("HandleBrandNameChanged failed", ex.Message);
}
}
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;
if (!string.IsNullOrEmpty(UserInput))
@ -260,13 +248,15 @@ namespace BLAIzor.Components.Pages
protected async void UpdateTextContentForVoice(string receivedSessionId, string content)
{
Console.WriteLine("UPDATETEXTCONTENT called");
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
if (receivedSessionId != SessionId) return;
TextContent = content;
try
{
TextContent = 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")]
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");
if (myHome != null)
var languageCode = instance._scopedContentService.SelectedLanguage switch
{
if (myHome.STTEnabled == false) return;
Console.WriteLine("STT ENABLED -------------------------------------------------------------------------------");
var languageCode = "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();
"English" => "en-US",
"German" => "de-DE",
_ => "hu-HU"
};
byte[] audioBytes = Convert.FromBase64String(base64Audio);
myHome.HtmlContent.Clear();
var response = await speech.RecognizeAsync(new RecognitionConfig
var credentialsPath = instance.configuration.GetSection("GoogleAPI").GetValue<string>("CredentialsPath");
var speech = new SpeechClientBuilder { CredentialsPath = credentialsPath }.Build();
byte[] audioBytes = Convert.FromBase64String(base64Audio);
instance.HtmlContent.Clear();
var response = await speech.RecognizeAsync(new RecognitionConfig
{
Encoding = RecognitionConfig.Types.AudioEncoding.Mp3,
SampleRateHertz = 48000,
LanguageCode = languageCode
}, RecognitionAudio.FromBytes(audioBytes));
foreach (var result in response.Results)
{
foreach (var alternative in result.Alternatives)
{
Encoding = RecognitionConfig.Types.AudioEncoding.Mp3,
SampleRateHertz = 48000, // Match the actual sample rate
LanguageCode = languageCode
}, RecognitionAudio.FromBytes(audioBytes));
Console.Write("BILLED: " + response.TotalBilledTime);
foreach (var result in response.Results)
{
//Console.Write("RESULT: " + result.Alternatives.Count);
foreach (var alternative in result.Alternatives)
{
//Console.WriteLine($"Transcription: {alternative.Transcript}");
await myHome.HandleVoiceCommand(alternative.Transcript, SessionId);
}
await instance.HandleVoiceCommand(alternative.Transcript, sessionId);
}
}
}
[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()
@ -377,11 +350,8 @@ namespace BLAIzor.Components.Pages
public async ValueTask DisposeAsync()
{
//await CssTemplateService.DeleteSessionCssFile(SessionId);
_instances.TryRemove(SessionId, out _);
_scopedContentService.OnBrandNameChanged -= HandleBrandNameChanged;
//AIService.OnContentReceived -= UpdateContent;
//AIService.OnContentReceiveFinished -= UpdateFinished;
//AIService.OnStatusChangeReceived -= UpdateStatus;
ChatGptService.OnTextContentAvailable -= UpdateTextContentForVoice;
}

View File

@ -373,10 +373,8 @@
<script>
function openContactForm(emailAddress) {
console.log(emailAddress);
if (emailAddress) {
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress)
if (emailAddress && sessionId) {
DotNet.invokeMethodAsync('BLAIzor', 'OpenEmailForm2', emailAddress, sessionId)
}
}
</script>
@ -481,12 +479,6 @@
StateHasChanged();
}
public Preview()
{
myHome = this; // Set the static reference to the current instance
}
protected override async Task OnParametersSetAsync()
{
SessionId = _scopedContentService.SessionId;
@ -557,33 +549,67 @@
}
}
// else
// {
// UpdateContent(SessionId, HtmlContent.ToString(), currentMenuItem);
// }
UserInput = string.Empty;
_initVoicePending = true;
}
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.Append(content);
//TODO SAVE TO DB
if (menuItem != null)
await InvokeAsync(StateHasChanged);
}
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;
// IsContentSaved = string.IsNullOrEmpty(currentMenuItem.StoredHtml) ? false : true;
// _logger.InfoAsync($"Preview - UpdateContent: {IsContentSaved}");
displayOptions = true;
StateHasChanged();
if (!string.IsNullOrEmpty(menuItem.StoredHtml))
{
HtmlContent.Clear();
HtmlContent.Append(menuItem.StoredHtml);
if (currentMenuItem.ContentItemId != null)
{
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);
}
}
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
await InvokeAsync(() =>
{
StateHasChanged();
});
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
UserInput = string.Empty;
}
}
@ -600,13 +626,13 @@
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");
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
Console.Write(_scopedContentService.CurrentDOM);
}
catch (Exception ex) { await _logger.ErrorAsync("UpdateFinished failed", ex.Message); }
}
private async Task ContentChangedInForm()
@ -618,17 +644,16 @@
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;
//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)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
@ -1106,4 +1131,4 @@
}
}
}

View File

@ -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()
@ -483,19 +487,22 @@
// STT Hook
private static CreateSiteWizard? _instance;
private DotNetObjectReference<CreateSiteWizard>? _dotNetRef;
[JSInvokable]
public static async Task SendAudioToServer(List<byte> audioData)
public async Task SendAudioToServer(List<byte> audioData)
{
if (_instance is null) return;
var result = await _instance.WhisperService.TranscribeAsync(audioData.ToArray());
var result = await WhisperService.TranscribeAsync(audioData.ToArray());
if (!string.IsNullOrWhiteSpace(result))
{
_instance.Steps[_instance.CurrentStep].Answer = result;
_instance.StateHasChanged();
Steps[CurrentStep].Answer = result;
StateHasChanged();
}
}
public void Dispose()
{
_dotNetRef?.Dispose();
}
}

View File

@ -21,30 +21,26 @@ var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
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 =>
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
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
//builder.Services.AddBlazorServerOptions(options =>
//{
// options.MaxBufferSize = 50 * 1024 * 1024; // Set to 50 MB
//});
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = 50 * 1024 * 1024;
});
var env = builder.Environment; // This is IWebHostEnvironment
if (env.IsProduction())
{
// do production-specific setup
}
var env = builder.Environment;
// Add services to the container.
builder.Services.AddRazorComponents()
@ -52,16 +48,6 @@ builder.Services.AddRazorComponents()
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.AddScoped<ISimpleLogger, SimpleLogger>();
builder.Services.AddScoped<AIService>();
@ -91,14 +77,14 @@ builder.Services.AddScoped<CssInjectorService>();
builder.Services.AddScoped<LocalVectorSearchService>();
builder.Services.AddScoped<WebsiteContentLoaderService>();
builder.Services.AddScoped<CacheService>();
builder.Services.AddSingleton<CreateSiteWizard>();
builder.Services.AddScoped<CreateSiteWizard>();
builder.Services.AddScoped<WhisperTranscriptionService>();
builder.Services.AddScoped<IBrightDataService, BrightDataService>();
builder.Services.AddHttpClient<ReplicateService>(client =>
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh");
new AuthenticationHeaderValue("Bearer", configuration["Replicate:ApiKey"]);
});
builder.Services.AddHostedService<TempFileCleanupService>();

View File

@ -6,7 +6,7 @@ using Newtonsoft.Json;
public class OpenAIEmbeddingService
{
private readonly string _apiKey = "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";
private readonly string _apiKey = "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";
private readonly HttpClient _httpClient;
public OpenAIEmbeddingService()

View File

@ -13,8 +13,10 @@ namespace BLAIzor.Services
public class QDrantService
{
public static IConfiguration? _configuration;
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 = "https://fe7d5c9e-8cd1-4ad9-af5a-af2bf3b93219.europe-west3-0.gcp.cloud.qdrant.io:6333";
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 = "";
@ -44,7 +46,7 @@ namespace BLAIzor.Services
public async Task<int> GetCollectionCount(string collectionName)
{
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
var result = await client.CountAsync(
collectionName: collectionName,
exact: true
@ -68,7 +70,7 @@ namespace BLAIzor.Services
public async Task<bool> CollectionExistsAsync(string collectionName)
{
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
return await client.CollectionExistsAsync(collectionName);
}
@ -76,7 +78,7 @@ namespace BLAIzor.Services
{
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
bool doesExist = await CollectionExistsAsync(collectionName);
if (doesExist)
{
@ -168,7 +170,7 @@ namespace BLAIzor.Services
}
else
{
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
bool doesExist = await client.CollectionExistsAsync(site.VectorCollectionName);
if (!doesExist)
{
@ -249,7 +251,7 @@ namespace BLAIzor.Services
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);
var result = await client.RetrieveAsync(
@ -299,7 +301,7 @@ namespace BLAIzor.Services
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(
collectionName: $"Site{siteId.ToString()}",
id: Convert.ToUInt64(pointId),
@ -367,7 +369,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
var doesCollectionExist = await client.CollectionExistsAsync(collectionName);
if (doesCollectionExist)
@ -475,7 +477,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
var pointStruct = new PointStruct();
pointStruct = new PointStruct
@ -505,7 +507,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
var pointStructList = new List<PointStruct>();
for (int i = 0; i < ids.Count; i++)
{
@ -535,7 +537,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
var pointStructList = new List<PointStruct>();
for (int i = 0; i < chunks.Count; i++)
{
@ -576,7 +578,7 @@ namespace BLAIzor.Services
public async Task DeletePointAsync(int pointId, string collectionName)
{
_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]);
Console.WriteLine(result.Status);
@ -585,7 +587,7 @@ namespace BLAIzor.Services
public async Task DeletePointsAsync(ulong[] pointIds, string collectionName)
{
_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);
Console.WriteLine(result.Status);
@ -593,7 +595,7 @@ namespace BLAIzor.Services
public async Task DeletePointsAsync(Guid[] pointIds, string collectionName)
{
_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);
Console.WriteLine(result.Status);
@ -604,7 +606,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
var client = new QdrantClient(_qdrantHost, 6334, false, _apiKey);
await client.DeleteCollectionAsync(collectionName);
}

View File

@ -33,7 +33,7 @@ namespace BLAIzor.Services
}
}
public event Action OnBrandNameChanged;
public event Action? OnBrandNameChanged;
public int SelectedSiteId { get; set; } = 1;
public WebsiteContentModel WebsiteContentModel { get; set; }

View File

@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Information",
"BLAIzor": "Information"
}
},
"Replicate": {
"ApiKey": "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh"
}
}

View File

@ -45,7 +45,7 @@
},
"OpenAI": {
//"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
//"Model": "gpt-4.1-mini"
//"Model": "gpt-4o-mini"

View File

@ -1,5 +1,10 @@
let mediaRecorder;
let recordedChunks = [];
let wizardDotNetRef = null;
window.initWizardAudio = (dotnetRef) => {
wizardDotNetRef = dotnetRef;
};
window.startRecording = async () => {
recordedChunks = [];
@ -18,8 +23,9 @@ window.startRecording = async () => {
const arrayBuffer = await blob.arrayBuffer();
const byteArray = new Uint8Array(arrayBuffer);
// Send to Blazor server
DotNet.invokeMethodAsync('BLAIzor', 'SendAudioToServer', Array.from(byteArray));
if (wizardDotNetRef) {
wizardDotNetRef.invokeMethodAsync('SendAudioToServer', Array.from(byteArray));
}
};
mediaRecorder.start();