diff options
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.cs | 73 |
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(); |
