aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs')
-rw-r--r--Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs73
1 files changed, 68 insertions, 5 deletions
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs
index fc970d52e..7b2d8c3ef 100644
--- a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Services/LlmClient.cs
@@ -34,6 +34,17 @@ namespace Tango.Portal.Chat.Web.Services
public async Task<ProposeKqlResult> ProposeKqlAsync(String plannerPrompt, String plotySample,
string question, string schemaJson, IEnumerable<ChatMessage>? history, CancellationToken ct = default)
{
+ return _opt.Provider switch
+ {
+ LlmProvider.Claude => await ProposeKqlWithClaudeAsync(plannerPrompt, plotySample, question, schemaJson, history, ct),
+ LlmProvider.OpenAI => await ProposeKqlWithOpenAIAsync(plannerPrompt, plotySample, question, schemaJson, history, ct),
+ _ => await ProposeKqlWithOpenAIAsync(plannerPrompt, plotySample, question, schemaJson, history, ct) // Default to OpenAI
+ };
+ }
+
+ private async Task<ProposeKqlResult> ProposeKqlWithOpenAIAsync(String plannerPrompt, String plotySample,
+ string question, string schemaJson, IEnumerable<ChatMessage>? history, CancellationToken ct)
+ {
var messages = new List<object> { new { role = "system", content = plannerPrompt } };
if (history != null)
@@ -55,8 +66,8 @@ namespace Tango.Portal.Chat.Web.Services
using var req = new HttpRequestMessage(HttpMethod.Post, _opt.Endpoint);
req.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
- if (_opt.IsAzure) req.Headers.Add("api-key", _opt.ApiKey);
- else req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _opt.ApiKey);
+
+ req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _opt.ApiKey);
using var resp = await _http.SendAsync(req, ct);
resp.EnsureSuccessStatusCode();
@@ -64,7 +75,60 @@ namespace Tango.Portal.Chat.Web.Services
var root = JsonNode.Parse(body)!.AsObject();
var content = root["choices"]![0]!["message"]!["content"]?.ToString() ?? "{}";
- content = StripCodeFences(content); // your existing helper
+ content = StripCodeFences(content);
+
+ var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
+ opts.Converters.Add(new FlexibleStringListConverter());
+ var result = JsonSerializer.Deserialize<ProposeKqlResult>(content, opts)
+ ?? new ProposeKqlResult { Kql = "", Parameters = new() };
+ return result;
+ }
+
+ private async Task<ProposeKqlResult> ProposeKqlWithClaudeAsync(String plannerPrompt, String plotySample,
+ string question, string schemaJson, IEnumerable<ChatMessage>? history, CancellationToken ct)
+ {
+ var messages = new List<object>();
+
+ if (history != null)
+ {
+ foreach (var m in history.TakeLast(6))
+ {
+ messages.Add(new {
+ role = m.Role == "assistant" ? "assistant" : "user",
+ content = CapString(m.Content, 1000)
+ });
+ }
+ }
+
+ var schemaBlock = $"SCHEMA:\n{schemaJson}";
+ var userMessage = $"Question: {question}\n\n{schemaBlock}\n\n{plotySample}\n\nPlease respond with valid JSON only.";
+ messages.Add(new { role = "user", content = userMessage });
+
+ var payload = new
+ {
+ model = !string.IsNullOrEmpty(_opt.ClaudeModel) ? _opt.ClaudeModel : "claude-3-5-sonnet-20241022",
+ max_tokens = _opt.MaxTokens,
+ temperature = _opt.Temperature,
+ system = plannerPrompt,
+ messages = messages
+ };
+
+ var endpoint = !string.IsNullOrEmpty(_opt.ClaudeEndpoint) ? _opt.ClaudeEndpoint : "https://api.anthropic.com/v1/messages";
+ var apiKey = !string.IsNullOrEmpty(_opt.ClaudeApiKey) ? _opt.ClaudeApiKey : _opt.ApiKey;
+
+ using var req = new HttpRequestMessage(HttpMethod.Post, endpoint);
+ req.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
+ req.Headers.Add("x-api-key", apiKey);
+ req.Headers.Add("anthropic-version", "2023-06-01");
+
+ using var resp = await _http.SendAsync(req, ct);
+ resp.EnsureSuccessStatusCode();
+ var body = await resp.Content.ReadAsStringAsync(ct);
+
+ var root = JsonNode.Parse(body)!.AsObject();
+ var contentArray = root["content"]?.AsArray();
+ var content = contentArray?[0]?["text"]?.ToString() ?? "{}";
+ content = StripCodeFences(content);
var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
opts.Converters.Add(new FlexibleStringListConverter());
@@ -123,8 +187,7 @@ namespace Tango.Portal.Chat.Web.Services
var json = JsonSerializer.Serialize(payload);
req.Content = new StringContent(json, Encoding.UTF8, "application/json");
- if (_opt.IsAzure) req.Headers.Add("api-key", _opt.ApiKey);
- else req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _opt.ApiKey);
+ req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _opt.ApiKey);
using var resp = await _http.SendAsync(req, ct);
resp.EnsureSuccessStatusCode();