diff options
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.cs | 116 |
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 |
