diff --git a/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor b/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor
index c6700e3..6b1f8e4 100644
--- a/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor
+++ b/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor
@@ -1,4 +1,6 @@
-@inherits LayoutComponentBase
+@using System.Runtime.InteropServices
+
+@inherits LayoutComponentBase
@@ -21,6 +23,12 @@
@Body
+
+
+
+
@code {
diff --git a/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor.css b/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor.css
index a3ae64a..fd8d52c 100644
--- a/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor.css
+++ b/SqlDatabaseVectorSearch/Components/Layout/MainLayout.razor.css
@@ -1,3 +1,20 @@
+.footer-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #f8f9fa;
+ border-top: 1px solid #dee2e6;
+}
+
+ .footer-content span {
+ font-size: 0.9rem;
+ color: #6c757d;
+ }
+
+ .footer-content .ms-auto {
+ margin-left: auto;
+ }
+
#blazor-error-ui {
color-scheme: light only;
background: lightyellow;
diff --git a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor
index 06239eb..f9d4691 100644
--- a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor
+++ b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor
@@ -52,15 +52,17 @@
@if (message.IsCompleted)
{
- @*
-
+
+
+
}
@@ -105,6 +107,9 @@
private Guid conversationId = Guid.NewGuid();
private bool isAsking = false;
+ [Inject]
+ protected ToastService ToastService { get; set; } = default!;
+
private async Task HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Enter" && !isAsking && !string.IsNullOrWhiteSpace(question))
@@ -117,24 +122,28 @@
{
isAsking = true;
+ var userQuestion = new Question(conversationId, question!);
+ var userMessage = new Message { Text = userQuestion.Text, Role = "user" };
+ messages.Add(userMessage);
+
+ var assistantMessage = new Message { Role = "assistant" };
+ messages.Add(assistantMessage);
+
+ question = null;
+ await Task.Yield();
+
try
{
- var userQuestion = new Question(conversationId, question!);
- var userMessage = new Message { Text = userQuestion.Text, Role = "user" };
- messages.Add(userMessage);
-
- var assistantMessage = new Message { Role = "assistant" };
- messages.Add(assistantMessage);
-
- question = null;
- await Task.Yield();
-
await using var scope = ServiceProvider.CreateAsyncScope();
var vectorSearchService = scope.ServiceProvider.GetRequiredService();
var response = vectorSearchService.AskStreamingAsync(userQuestion);
await foreach (var delta in response)
{
+ if (delta.StreamState == StreamState.Start)
+ {
+ assistantMessage.TokenUsage = FormatTokenUsage(delta.TokenUsage);
+ }
if (delta.StreamState == StreamState.Append)
{
assistantMessage.Text += delta.Answer;
@@ -142,12 +151,18 @@
else if (delta.StreamState == StreamState.End)
{
assistantMessage.IsCompleted = true;
+ assistantMessage.TokenUsage += FormatTokenUsage(delta.TokenUsage);
}
await Task.Yield();
StateHasChanged();
}
}
+ catch (Exception ex)
+ {
+ assistantMessage.Text = $"There was an error while processing the question: {ex.Message}";
+ assistantMessage.IsCompleted = true;
+ }
finally
{
isAsking = false;
@@ -166,6 +181,40 @@
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
}
+ private string FormatTokenUsage(TokenUsageResponse? tokenUsageResponse)
+ {
+ if (tokenUsageResponse is null)
+ {
+ return string.Empty;
+ }
+
+ var reformulation = tokenUsageResponse.Reformulation is not null
+ ? $"Reformulation:
{FormatTokenUsageDetails(tokenUsageResponse.Reformulation)}
"
+ : string.Empty;
+
+ var embeddingTokenCount = tokenUsageResponse.EmbeddingTokenCount.HasValue
+ ? $"Embedding Token Count: {tokenUsageResponse.EmbeddingTokenCount}
"
+ : string.Empty;
+
+ var question = tokenUsageResponse.Question is not null
+ ? $"Question:
{FormatTokenUsageDetails(tokenUsageResponse.Question)}
"
+ : string.Empty;
+
+ return $"{reformulation}{embeddingTokenCount}{question}";
+ }
+
+ private string FormatTokenUsageDetails(TokenUsage? tokenUsage)
+ {
+ if (tokenUsage is null)
+ {
+ return string.Empty;
+ }
+
+ return $"Input Token Count: {tokenUsage.InputTokenCount}
" +
+ $"Output Token Count: {tokenUsage.OutputTokenCount}
" +
+ $"Total Token Count: {tokenUsage.TotalTokenCount}";
+ }
+
public class Message
{
public string? Text { get; set; }
@@ -173,5 +222,7 @@
public required string Role { get; set; }
public bool IsCompleted { get; set; }
+
+ public string? TokenUsage { get; set; }
}
}
\ No newline at end of file
diff --git a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor
index 695ee40..3722858 100644
--- a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor
+++ b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor
@@ -24,7 +24,7 @@
-
+
@@ -105,7 +105,6 @@ else
private Button deleteButton = default!;
private IList documents = [];
- private List messages = [];
private UploadDocumentRequest uploadDocumentRequest = new();
private EditContext? editContext;
@@ -161,9 +160,10 @@ else
uploadButton.ShowLoading();
+ var fileName = uploadDocumentRequest.File.Name;
+
try
{
- var fileName = uploadDocumentRequest.File.Name;
await using var inputStream = uploadDocumentRequest.File.OpenReadStream(20 * 1024 * 1024); // 20 MB
await using var stream = await inputStream.GetMemoryStreamAsync();
@@ -177,6 +177,10 @@ else
await LoadDocumentsAsync(scope.ServiceProvider);
}
+ catch (Exception ex)
+ {
+ ToastService.Notify(await CreateToastMessageAsync(ToastType.Danger, "Upload error", $"There was an error while uploading the document {fileName}: {ex.Message}"));
+ }
finally
{
uploadButton.HideLoading();
@@ -218,6 +222,10 @@ else
await LoadDocumentsAsync(scope.ServiceProvider);
ToastService.Notify(await CreateToastMessageAsync(ToastType.Info, "Delete documents", "The selected documents have been successfully deleted."));
}
+ catch (Exception ex)
+ {
+ ToastService.Notify(await CreateToastMessageAsync(ToastType.Danger, "Delete error", $"There was an error while deleting the documents: {ex.Message}"));
+ }
finally
{
deleteButton.HideLoading();