aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs')
-rw-r--r--Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs116
1 files changed, 116 insertions, 0 deletions
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs
new file mode 100644
index 000000000..4eed2182d
--- /dev/null
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/ChatMessageLogger.cs
@@ -0,0 +1,116 @@
+using Azure.Core;
+using Azure.Identity;
+using Kusto.Data;
+using Kusto.Data.Common;
+using Kusto.Data.Net.Client;
+using Microsoft.Extensions.Options;
+using System.Text.Json;
+using Tango.Portal.Chat.Web.Models;
+
+namespace Tango.Portal.Chat.Web.Services
+{
+ public sealed class ChatMessageLogger
+ {
+ private readonly ICslAdminProvider _admin;
+ private readonly string _database;
+ private readonly ILogger<ChatMessageLogger> _logger;
+
+ public ChatMessageLogger(IOptions<AdxOptions> opts, ILogger<ChatMessageLogger> logger)
+ {
+ var options = opts.Value;
+ _database = options.Database;
+ _logger = logger;
+
+ var cred = new ClientSecretCredential(
+ options.TenantId,
+ options.ClientId,
+ options.ClientSecret);
+
+ var kcsb = new KustoConnectionStringBuilder(options.ClusterUri)
+ .WithAadAzureTokenCredentialsAuthentication(cred);
+
+ _admin = KustoClientFactory.CreateCslAdminProvider(kcsb);
+ }
+
+ public async Task LogMessageAsync(ChatConversationMessage message, CancellationToken ct = default)
+ {
+ try
+ {
+ var json = JsonSerializer.Serialize(message);
+ var command = $".ingest inline into table ChatConversationsTable with (jsonMappingReference = 'ChatConversationsTableMapping') <| {json}";
+
+ var props = new ClientRequestProperties
+ {
+ ClientRequestId = $"chat_log_{Guid.NewGuid()}"
+ };
+
+ using var result = await _admin.ExecuteControlCommandAsync(_database, command, props);
+ _logger.LogDebug("Successfully logged chat message {MessageId} for session {SessionId}",
+ message.ID, message.SessionID);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to log chat message {MessageId} for session {SessionId}",
+ message.ID, message.SessionID);
+ }
+ }
+
+ public async Task LogQuestionAsync(string sessionId, string userEmail, string userName, string question, CancellationToken ct = default)
+ {
+ var message = new ChatConversationMessage
+ {
+ SessionID = sessionId,
+ Time = DateTime.UtcNow,
+ Email = userEmail,
+ User = userName,
+ Role = "user",
+ Classification = "question",
+ Message = question,
+ Provider = string.Empty
+ };
+
+ await LogMessageAsync(message, ct);
+ }
+
+ public async Task LogAnswerAsync(string sessionId, string userEmail, string userName, ChatResponse response, string assistantType, string provider, CancellationToken ct = default)
+ {
+ var answerMessage = BuildAnswerMessage(response);
+
+ var message = new ChatConversationMessage
+ {
+ SessionID = sessionId,
+ Time = DateTime.UtcNow,
+ Email = userEmail,
+ User = userName,
+ Role = "assistant",
+ Classification = assistantType,
+ Message = answerMessage,
+ Provider = provider
+ };
+
+ await LogMessageAsync(message, ct);
+ }
+
+ private static string BuildAnswerMessage(ChatResponse response)
+ {
+ var parts = new List<string>();
+
+ if (!string.IsNullOrEmpty(response.Answer))
+ {
+ parts.Add($"Answer: {response.Answer}");
+ }
+
+ if (!string.IsNullOrEmpty(response.UsedKql))
+ {
+ parts.Add($"Used KQL: {response.UsedKql}");
+ }
+
+ if (!string.IsNullOrEmpty(response.Ploty))
+ {
+ parts.Add($"Visualization: {response.Ploty}");
+ }
+
+ return parts.Count > 0 ? string.Join("\n\n", parts) : response.Answer ?? string.Empty;
+ }
+ }
+} \ No newline at end of file