aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs
diff options
context:
space:
mode:
authorRoy Ben Shabat <roy.mail.net@gmail.com>2025-09-02 20:36:48 +0300
committerRoy Ben Shabat <roy.mail.net@gmail.com>2025-09-02 20:36:48 +0300
commitdc0d050ad35973e1ae09e3c7e47bc1fb13eedcd2 (patch)
treef1921cbdfca16b254a9046ab1e8dd17427d81dcd /Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs
parentf41758ec7d1e28deae447199fc0024467c84a9d4 (diff)
downloadTango-dc0d050ad35973e1ae09e3c7e47bc1fb13eedcd2.tar.gz
Tango-dc0d050ad35973e1ae09e3c7e47bc1fb13eedcd2.zip
Portal AI
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.cs127
1 files changed, 127 insertions, 0 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
new file mode 100644
index 000000000..35b170347
--- /dev/null
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Controllers/ChatController.cs
@@ -0,0 +1,127 @@
+using System.Data;
+using System.Text.Json;
+using ChatADX.Web.Models;
+using ChatADX.Web.Services;
+using Kusto.Data.Data;
+using Microsoft.AspNetCore.Mvc;
+
+namespace ChatADX.Web.Controllers
+{
+ [ApiController]
+ [Route("api/[controller]")]
+ public sealed class ChatController : ControllerBase
+ {
+ private readonly SchemaRegistry _schema;
+ private readonly KqlGuard _guard;
+ private readonly KustoQueryService _adx;
+ private readonly LlmClient _llm;
+ private static readonly string[] AllowTables = new[] { "JobRunsTable", "JobStatusTable", "TelemetryTable", "MachinesTable" };
+
+ public ChatController(SchemaRegistry schema, KqlGuard guard, KustoQueryService adx, LlmClient llm)
+ {
+ _schema = schema;
+ _guard = guard;
+ _adx = adx;
+ _llm = llm;
+ }
+
+ [HttpPost("ask")]
+ public async Task<ActionResult<ChatResponse>> Ask([FromBody] ChatRequest req, CancellationToken ct)
+ {
+ try
+ {
+ var schemaJson = _schema.GetSchemaJson();
+
+ // 1) Ask the model for KQL
+ var plan = await _llm.ProposeKqlAsync(req.Question, schemaJson, req.History, ct);
+
+ if (plan.Assistant == "data")
+ {
+ // 2) Guardrail validation
+ var val = _guard.Validate(plan.Kql);
+ if (!val.IsOk) return BadRequest(new { error = "Invalid KQL", details = val.Error, plan });
+
+ // 4) Execute in ADX
+ DataTable table;
+ try
+ {
+ table = await _adx.QueryAsync(plan.Kql, plan.Parameters, ct);
+ }
+ catch (Exception ex)
+ {
+ // Return error to the client so they can iterate
+ return new ChatResponse
+ {
+ Answer = $"Seems like my kusto query ran into some issue..\n{ex.Message}",
+ ThreadId = req.ThreadId,
+ UsedKql = plan.Kql
+ };
+ }
+
+ // 5) Build compact facts (limit rows/cols)
+ var preview = ToPreview(table, 200);
+ var facts = JsonSerializer.Serialize(preview);
+
+ // 6) Ask model for final answer
+ //var answer = await _llm.AnswerFromFactsAsync(req.Question, facts, plan.Kql, ct);
+
+ var run = await _llm.AnswerWithAssistantAsync(
+ LlmClient.AssistantType.Data,
+ req.Question,
+ facts,
+ plan.Kql,
+ req.ThreadId, // <-- reuse if provided
+ ct);
+
+ return new ChatResponse
+ {
+ Answer = run.Answer,
+ UsedKql = plan.Kql,
+ Preview = preview,
+ ThreadId = run.ThreadId // <-- echo back the thread id used/created
+ };
+ }
+ else
+ {
+ // AFTER
+ var run = await _llm.AnswerWithAssistantAsync(
+ LlmClient.AssistantType.Docs,
+ req.Question,
+ string.Empty,
+ plan.Kql,
+ req.ThreadId, // <-- reuse if provided
+ ct);
+
+ return new ChatResponse
+ {
+ Answer = run.Answer,
+ ThreadId = run.ThreadId
+ };
+ }
+ }
+ catch (Exception ex)
+ {
+ return new ChatResponse
+ {
+ Answer = $"Ooops something went wrong...\n{ex.Message}",
+ ThreadId = req.ThreadId
+ };
+ }
+ }
+
+ private static object ToPreview(DataTable dt, int maxRows)
+ {
+ var cols = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToArray();
+ var rows = new List<Dictionary<string, object?>>();
+ int count = 0;
+ foreach (DataRow r in dt.Rows)
+ {
+ if (count++ >= maxRows) break;
+ var d = new Dictionary<string, object?>();
+ foreach (var c in cols) d[c] = r[c];
+ rows.Add(d);
+ }
+ return new { columns = cols, rows };
+ }
+ }
+}