From 1c24250a4212ebf005e60635ab80f95f05ae768a Mon Sep 17 00:00:00 2001 From: Marco Minerva Date: Wed, 4 Jun 2025 12:34:02 +0200 Subject: [PATCH] Add citation handling and styling in Ask.razor Updated Ask.razor to include regex for citation extraction and display. Introduced a new method to extract citations and updated the Message class to store them. Added a Citation class for individual citation representation. Enhanced app.css with styles for citation display. --- .../Components/Pages/Ask.razor | 67 +++++++++++++++++++ SqlDatabaseVectorSearch/wwwroot/css/app.css | 6 ++ 2 files changed, 73 insertions(+) diff --git a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor index 9e07b45..60a2af1 100644 --- a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor +++ b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor @@ -1,4 +1,5 @@ @page "/ask" +@using System.Text.RegularExpressions @inject IServiceProvider ServiceProvider @inject IJSRuntime JSRuntime @@ -72,6 +73,23 @@ + @if (message.Citations is not null && message.Citations.Count() > 0) + { +
+ @foreach (var citation in message.Citations) + { +
+
+ @citation.FileName @if (!string.IsNullOrEmpty(citation.PageNumber)) + { + pag. @citation.PageNumber + } +
+
@citation.Quote
+
+ } +
+ } } } @@ -178,10 +196,17 @@ } else if (delta.StreamState == StreamState.Append) { + // Adds tokens to the assistant message as they are received assistantMessage.Text += delta.Answer; } else if (delta.StreamState == StreamState.End) { + // Extracts citations, if any. + var (cleanText, citations) = ExtractCitations(assistantMessage.Text); + + assistantMessage.Text = cleanText; + assistantMessage.Citations = citations; + assistantMessage.IsCompleted = true; assistantMessage.TokenUsage += FormatTokenUsage(delta.TokenUsage); } @@ -269,6 +294,36 @@ await JSRuntime.InvokeVoidAsync("scrollTo", chat); } + private static (string, IEnumerable) ExtractCitations(string? text) + { + var citations = new List(); + + if (string.IsNullOrEmpty(text)) + { + return (text ?? string.Empty, citations); + } + + var pattern = "(.*?)<\\/citation>"; + + var matches = Regex.Matches(text, pattern, RegexOptions.Singleline); + foreach (Match match in matches) + { + if (match.Success && match.Groups.Count == 4) + { + citations.Add(new Citation + { + FileName = match.Groups[1].Value, + PageNumber = match.Groups[2].Value, + Quote = match.Groups[3].Value + }); + } + } + + // Remove all tags from the text + var cleanText = Regex.Replace(text, pattern, string.Empty, RegexOptions.Singleline).TrimEnd(); + return (cleanText, citations); + } + public class Message { public string? Text { get; set; } @@ -278,5 +333,17 @@ public bool IsCompleted { get; set; } public string? TokenUsage { get; set; } + + // List of citations extracted from the answer + public IEnumerable? Citations { get; set; } + } + + public class Citation + { + public string FileName { get; set; } = null!; + + public string Quote { get; set; } = null!; + + public string PageNumber { get; set; } = null!; } } \ No newline at end of file diff --git a/SqlDatabaseVectorSearch/wwwroot/css/app.css b/SqlDatabaseVectorSearch/wwwroot/css/app.css index 1f3f172..3d4dd04 100644 --- a/SqlDatabaseVectorSearch/wwwroot/css/app.css +++ b/SqlDatabaseVectorSearch/wwwroot/css/app.css @@ -62,3 +62,9 @@ h1:focus { .blazor-error-boundary::after { content: "An error has occurred." } + +.citation-box { + width: fit-content; + max-width: 100%; + background-color: #f8f9fa; +}