diff options
Diffstat (limited to 'Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs')
| -rw-r--r-- | Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs | 108 |
1 files changed, 96 insertions, 12 deletions
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs index a13b6a47a..396651e3f 100644 --- a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs +++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs @@ -19,13 +19,15 @@ namespace Tango.Portal.Chat.Web.Controllers private readonly KqlGuard _guard; private readonly KustoQueryService _adx; private readonly LlmClient _llm; + private readonly ChatMessageLogger _logger; - public ChatController(SchemaRegistry schema, KqlGuard guard, KustoQueryService adx, LlmClient llm) + public ChatController(SchemaRegistry schema, KqlGuard guard, KustoQueryService adx, LlmClient llm, ChatMessageLogger logger) { _schema = schema; _guard = guard; _adx = adx; _llm = llm; + _logger = logger; } [HttpPost("ask")] @@ -33,6 +35,36 @@ namespace Tango.Portal.Chat.Web.Controllers { try { + if (!SessionUtils.IsUserAuthenticated(HttpContext)) + { + return new ChatResponse + { + Answer = "User is not authenticated or session expired", + ThreadId = req.ThreadId + }; + } + + var sessionUser = SessionUtils.GetSessionUser(HttpContext); + var sessionId = HttpContext.Session.Id; + + // Log the question + _ = Task.Run(async () => + { + try + { + await _logger.LogQuestionAsync( + sessionId, + sessionUser?.Email ?? "unknown", + sessionUser?.FullName ?? "unknown", + req.Question, + ct); + } + catch + { + // Ignore logging failures + } + }, ct); + var schemaJson = _schema.GetSchemaJson(); var plannerPrompt = _schema.GetPlannerPrompt(); var plotySample = _schema.GetPlotySample(); @@ -50,30 +82,77 @@ namespace Tango.Portal.Chat.Web.Controllers plan = await _llm.ProposeKqlAsync(plannerPrompt, plotySample, req.Question, schemaJson, req.History, ct); } + ChatResponse response; if (plan.Assistant == "data" || plan.Assistant == "ploty") { - return await AnswerWithDataAssistant(req, plan, ct); + response = await AnswerWithDataAssistant(req, plan, ct); } else if (plan.Assistant == "docs") { - return await AnswerWithDocsAssistant(req, plan, ct); + response = await AnswerWithDocsAssistant(req, plan, ct); } else { - return AnswerWithPlannerConversation(req, plan); + response = AnswerWithPlannerConversation(req, plan); } + + // Log the answer + _ = Task.Run(async () => + { + try + { + await _logger.LogAnswerAsync( + sessionId, + sessionUser?.Email ?? "unknown", + sessionUser?.FullName ?? "unknown", + response, + plan.Assistant, + plan.Provider.ToString(), + ct); + } + catch + { + // Ignore logging failures + } + }, ct); + + return response; } catch (Exception ex) { - return new ChatResponse + var errorResponse = new ChatResponse { Answer = $"Ooops something went wrong...\n{ex.Message}", ThreadId = req.ThreadId }; + + // Log the error response + var sessionUser = SessionUtils.GetSessionUser(HttpContext); + var sessionId = req.ThreadId ?? Guid.NewGuid().ToString(); + _ = Task.Run(async () => + { + try + { + await _logger.LogAnswerAsync( + sessionId, + sessionUser?.Email ?? "unknown", + sessionUser?.FullName ?? "unknown", + errorResponse, + "error", + "Unknown", + ct); + } + catch + { + // Ignore logging failures + } + }, ct); + + return errorResponse; } } - private static ActionResult<ChatResponse> AnswerWithPlannerConversation(ChatRequest req, ProposeKqlResult plan) + private ChatResponse AnswerWithPlannerConversation(ChatRequest req, ProposeKqlResult plan) { return new ChatResponse { @@ -82,7 +161,7 @@ namespace Tango.Portal.Chat.Web.Controllers }; } - private async Task<ActionResult<ChatResponse>> AnswerWithDocsAssistant(ChatRequest req, ProposeKqlResult plan, CancellationToken ct) + private async Task<ChatResponse> AnswerWithDocsAssistant(ChatRequest req, ProposeKqlResult plan, CancellationToken ct) { // AFTER var run = await _llm.AnswerWithAssistantAsync( @@ -100,7 +179,7 @@ namespace Tango.Portal.Chat.Web.Controllers }; } - private async Task<ActionResult<ChatResponse>> AnswerWithDataAssistant(ChatRequest req, ProposeKqlResult plan, CancellationToken ct) + private async Task<ChatResponse> AnswerWithDataAssistant(ChatRequest req, ProposeKqlResult plan, CancellationToken ct) { // 2) Guardrail validation var val = _guard.Validate(plan.Kql); @@ -125,7 +204,12 @@ namespace Tango.Portal.Chat.Web.Controllers { if (plan.Provider == LlmProvider.OpenAI) { - return await Ask(req, ct, LlmProvider.Claude); + var fallbackResult = await Ask(req, ct, LlmProvider.Claude); + return fallbackResult.Value ?? new ChatResponse + { + Answer = "Fallback to Claude failed", + ThreadId = req.ThreadId + }; } else { @@ -156,7 +240,7 @@ namespace Tango.Portal.Chat.Web.Controllers } } - private async Task<ActionResult<ChatResponse>> AnswerWithDataAssistantInternal(ChatRequest req, ProposeKqlResult plan, DataTable table, CancellationToken ct) + private async Task<ChatResponse> AnswerWithDataAssistantInternal(ChatRequest req, ProposeKqlResult plan, DataTable table, CancellationToken ct) { var preview = DataHelper.ToPreview(table, 200); var facts = JsonSerializer.Serialize(preview); @@ -181,7 +265,7 @@ namespace Tango.Portal.Chat.Web.Controllers }; } - private static ActionResult<ChatResponse> AnswerWithMarkdownTable(ChatRequest req, ProposeKqlResult plan, DataTable table) + private ChatResponse AnswerWithMarkdownTable(ChatRequest req, ProposeKqlResult plan, DataTable table) { var markdown = DataHelper.ToMarkdownTable(table); @@ -194,7 +278,7 @@ namespace Tango.Portal.Chat.Web.Controllers }; } - private static ActionResult<ChatResponse> AnswerWithPloty(ChatRequest req, ProposeKqlResult plan, DataTable table) + private ChatResponse AnswerWithPloty(ChatRequest req, ProposeKqlResult plan, DataTable table) { String? ploty = table.Rows[0]["ploty"]?.ToString(); |
