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; +}