@page "/intelligence" @attribute [Authorize] @using NexusReader.Application.DTOs.AI @using NexusReader.Application.Abstractions.Services @using NexusReader.Application.DTOs.User @using NexusReader.UI.Shared.Components.Atoms @using System.Net.Http.Json @inject HttpClient Http @inject IKnowledgeService KnowledgeService @inject AuthenticationStateProvider AuthStateProvider

Global Intelligence

Interrogate, explore, and synthesize grounded knowledge from your library using Polyglot KM-RAG

@if (_chatMessages.Count == 0) {

Start Interrogating Your Library

Ask complex questions across your entire ebook collection. The KM-RAG engine dynamically builds semantic maps, resolves dependencies, and formulates high-fidelity, grounded answers with interactive popover citations.

} else {
@foreach (var message in _chatMessages) {
@if (message.Sender == "User") { } else { }
@message.Sender @message.Timestamp.ToString("HH:mm")
@foreach (var segment in message.Segments) { @if (segment.IsCitation) { } else { @RenderMarkdown(segment.Text) } }
} @if (_isLoading) {
AI Thinking...
Analyzing conceptual graphs and synthesizing response...
}
}
@code { private string _question = string.Empty; private string _selectedBookId = string.Empty; private bool _isLoading; private List? _books; private List _chatMessages = new(); public class ChatMessage { public string Id { get; set; } = Guid.NewGuid().ToString(); public string Sender { get; set; } = string.Empty; // "User" or "AI" public string Text { get; set; } = string.Empty; public DateTime Timestamp { get; set; } = DateTime.UtcNow; public List Segments { get; set; } = new(); public List Citations { get; set; } = new(); } public class ResponseSegment { public string Text { get; set; } = string.Empty; public bool IsCitation { get; set; } public string CitationId { get; set; } = string.Empty; } protected override async Task OnInitializedAsync() { try { _books = await Http.GetFromJsonAsync>("api/library/books"); } catch (Exception ex) { Console.WriteLine($"[Intelligence] Failed to load books for scope selector: {ex.Message}"); } } private async Task HandleKeyUp(KeyboardEventArgs e) { if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_question) && !_isLoading) { await AskQuestionAsync(); } } private async Task AskQuestionAsync() { if (string.IsNullOrWhiteSpace(_question) || _isLoading) return; var userQuestion = _question; _question = string.Empty; // Clear input field immediately _isLoading = true; // Add user query message _chatMessages.Add(new ChatMessage { Sender = "User", Text = userQuestion, Segments = new List { new ResponseSegment { Text = userQuestion, IsCitation = false } } }); StateHasChanged(); try { Guid? ebookId = null; if (!string.IsNullOrEmpty(_selectedBookId) && Guid.TryParse(_selectedBookId, out var parsedId)) { ebookId = parsedId; } var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var tenantId = authState.User.FindFirst("TenantId")?.Value ?? "global"; var result = await KnowledgeService.AskQuestionAsync(userQuestion, tenantId, ebookId); if (result.IsSuccess) { var response = result.Value; _chatMessages.Add(new ChatMessage { Sender = "AI", Text = response.Answer, Segments = ParseSegments(response.Answer), Citations = response.Citations }); } else { var errMsg = $"Error: {result.Errors.FirstOrDefault()?.Message ?? "An error occurred."}"; _chatMessages.Add(new ChatMessage { Sender = "AI", Text = errMsg, Segments = new List { new ResponseSegment { Text = errMsg, IsCitation = false } } }); } } catch (Exception ex) { var errMsg = $"Network/API Error: {ex.Message}"; _chatMessages.Add(new ChatMessage { Sender = "AI", Text = errMsg, Segments = new List { new ResponseSegment { Text = errMsg, IsCitation = false } } }); } finally { _isLoading = false; StateHasChanged(); } } private List ParseSegments(string text) { var segments = new List(); if (string.IsNullOrEmpty(text)) return segments; // Matches [Source ID: some-id] OR raw GUIDs in brackets [e225e58f-7539-cd51-e0ab-82741ec7e65c] var regex = new System.Text.RegularExpressions.Regex( @"\[Source ID:\s*([^\]]+)\]|\[([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\]", System.Text.RegularExpressions.RegexOptions.IgnoreCase); var matches = regex.Matches(text); int lastIndex = 0; foreach (System.Text.RegularExpressions.Match match in matches) { if (match.Index > lastIndex) { segments.Add(new ResponseSegment { Text = text.Substring(lastIndex, match.Index - lastIndex), IsCitation = false }); } var citationId = match.Groups[1].Success ? match.Groups[1].Value.Trim() : match.Groups[2].Value.Trim(); segments.Add(new ResponseSegment { IsCitation = true, CitationId = citationId }); lastIndex = match.Index + match.Length; } if (lastIndex < text.Length) { segments.Add(new ResponseSegment { Text = text.Substring(lastIndex), IsCitation = false }); } return segments; } private MarkupString RenderMarkdown(string text) { if (string.IsNullOrEmpty(text)) return new MarkupString(string.Empty); // 1. HTML Encode to prevent XSS var html = System.Net.WebUtility.HtmlEncode(text); // 2. Bold: **text** -> text html = System.Text.RegularExpressions.Regex.Replace(html, @"\*\*(.*?)\*\*", "$1"); // 3. Italic: *text* -> text html = System.Text.RegularExpressions.Regex.Replace(html, @"\*(.*?)\*", "$1"); // 4. Code blocks: ```language ... ``` ->
...
html = System.Text.RegularExpressions.Regex.Replace(html, @"```(?:[a-zA-Z0-9+#]+)?\s*([\s\S]*?)\s*```", "
$1
"); // 5. Inline Code: `code` -> code html = System.Text.RegularExpressions.Regex.Replace(html, @"`(.*?)`", "$1"); // 6. Newlines: \n ->
html = html.Replace("\n", "
"); return new MarkupString(html); } }