From 5a6d7c56bae6161ab6405c44a0955893f1385dad Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sat, 20 Jul 2024 09:53:30 +0200 Subject: [PATCH] Minor refact + work on groq --- SubtitleEdit.sln.DotSettings | 1 + src/libse/AutoTranslate/ChatGptTranslate.cs | 2 +- src/libse/AutoTranslate/GroqTranslate.cs | 120 ++++++++++++++++++++ src/libse/Settings/Settings.cs | 28 +++++ src/libse/Settings/ToolsSettings.cs | 4 + 5 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/libse/AutoTranslate/GroqTranslate.cs diff --git a/SubtitleEdit.sln.DotSettings b/SubtitleEdit.sln.DotSettings index 61e5bb287..1a607200a 100644 --- a/SubtitleEdit.sln.DotSettings +++ b/SubtitleEdit.sln.DotSettings @@ -32,6 +32,7 @@ True True True + True True True True diff --git a/src/libse/AutoTranslate/ChatGptTranslate.cs b/src/libse/AutoTranslate/ChatGptTranslate.cs index fa8511f8a..ee471e1f5 100644 --- a/src/libse/AutoTranslate/ChatGptTranslate.cs +++ b/src/libse/AutoTranslate/ChatGptTranslate.cs @@ -76,7 +76,7 @@ namespace Nikse.SubtitleEdit.Core.AutoTranslate var model = Configuration.Settings.Tools.ChatGptModel; if (string.IsNullOrEmpty(model)) { - model = "gpt-4o-mini"; + model = Models[0]; Configuration.Settings.Tools.ChatGptModel = model; } diff --git a/src/libse/AutoTranslate/GroqTranslate.cs b/src/libse/AutoTranslate/GroqTranslate.cs new file mode 100644 index 000000000..551734562 --- /dev/null +++ b/src/libse/AutoTranslate/GroqTranslate.cs @@ -0,0 +1,120 @@ +using Nikse.SubtitleEdit.Core.Common; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Core.Translate; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Nikse.SubtitleEdit.Core.Settings; + +namespace Nikse.SubtitleEdit.Core.AutoTranslate +{ + public class GroqTranslate : IAutoTranslator + { + private HttpClient _httpClient; + + public static string StaticName { get; set; } = "Groq"; + public string Name => StaticName; + public string Url => "https://groq.com/"; + public string Error { get; set; } + public int MaxCharacters => 1500; + + /// + /// See https://console.groq.com/docs/models + /// + public static string[] Models => new[] + { + "llama3-8b-8192", + "llama3-70b-8192", + "llama3-groq-8b-8192-tool-use-preview", + "llama3-groq-70b-8192-tool-use-preview", + "mixtral-8x7b-32768", + "gemma-7b-it", + "gemma2-9b-it", + }; + + public void Initialize() + { + _httpClient?.Dispose(); + _httpClient = new HttpClient(); + _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); + _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json"); + _httpClient.BaseAddress = new Uri(Configuration.Settings.Tools.GroqUrl.TrimEnd('/')); + _httpClient.Timeout = TimeSpan.FromMinutes(15); + + if (!string.IsNullOrEmpty(Configuration.Settings.Tools.GroqApiKey)) + { + _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + Configuration.Settings.Tools.GroqApiKey); + } + } + + public List GetSupportedSourceLanguages() + { + return ListLanguages(); + } + + public List GetSupportedTargetLanguages() + { + return ListLanguages(); + } + + //curl -X POST "https://api.groq.com/openai/v1/chat/completions" \ + //-H "Authorization: Bearer $GROQ_API_KEY" \ + //-H "Content-Type: application/json" \ + //-d '{"messages": [{"role": "user", "content": "Explain the importance of fast language models"}], "model": "llama3-8b-8192"} + + public async Task Translate(string text, string sourceLanguageCode, string targetLanguageCode, CancellationToken cancellationToken) + { + var model = Configuration.Settings.Tools.GroqModel; + if (string.IsNullOrEmpty(model)) + { + model = Models[0]; + Configuration.Settings.Tools.GroqModel = model; + } + + if (string.IsNullOrEmpty(Configuration.Settings.Tools.GroqPrompt)) + { + Configuration.Settings.Tools.GroqPrompt = new ToolsSettings().GroqPrompt; + } + var prompt = string.Format(Configuration.Settings.Tools.GroqPrompt, sourceLanguageCode, targetLanguageCode); + var input = "{\"model\": \"" + model + "\",\"messages\": [{ \"role\": \"user\", \"content\": \"" + prompt + "\\n\\n" + Json.EncodeJsonText(text.Trim()) + "\" }]}"; + var content = new StringContent(input, Encoding.UTF8); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + var result = await _httpClient.PostAsync(string.Empty, content, cancellationToken); + var bytes = await result.Content.ReadAsByteArrayAsync(); + var json = Encoding.UTF8.GetString(bytes).Trim(); + if (!result.IsSuccessStatusCode) + { + Error = json; + SeLogger.Error("Groq Translate failed calling API: Status code=" + result.StatusCode + Environment.NewLine + json); + } + + result.EnsureSuccessStatusCode(); + + var parser = new SeJsonParser(); + var resultText = parser.GetFirstObject(json, "content"); + if (resultText == null) + { + return string.Empty; + } + + var outputText = Json.DecodeJsonText(resultText).Trim(); + if (outputText.StartsWith('"') && outputText.EndsWith('"') && !text.StartsWith('"')) + { + outputText = outputText.Trim('"').Trim(); + } + + outputText = ChatGptTranslate.FixNewLines(outputText); + outputText = ChatGptTranslate.RemovePreamble(text, outputText); + return outputText.Trim(); + } + + public static List ListLanguages() + { + return ChatGptTranslate.ListLanguages(); + } + } +} diff --git a/src/libse/Settings/Settings.cs b/src/libse/Settings/Settings.cs index a91dc6c62..129e8739b 100644 --- a/src/libse/Settings/Settings.cs +++ b/src/libse/Settings/Settings.cs @@ -2186,6 +2186,30 @@ namespace Nikse.SubtitleEdit.Core.Settings settings.Tools.ChatGptModel = subNode.InnerText; } + subNode = node.SelectSingleNode("GroqUrl"); + if (subNode != null) + { + settings.Tools.GroqUrl = subNode.InnerText; + } + + subNode = node.SelectSingleNode("GroqPrompt"); + if (subNode != null) + { + settings.Tools.GroqPrompt = subNode.InnerText; + } + + subNode = node.SelectSingleNode("GroqApiKey"); + if (subNode != null) + { + settings.Tools.GroqApiKey = subNode.InnerText; + } + + subNode = node.SelectSingleNode("GroqModel"); + if (subNode != null) + { + settings.Tools.GroqModel = subNode.InnerText; + } + subNode = node.SelectSingleNode("LmStudioApiUrl"); if (subNode != null) { @@ -8881,6 +8905,10 @@ namespace Nikse.SubtitleEdit.Core.Settings textWriter.WriteElementString("ChatGptPrompt", settings.Tools.ChatGptPrompt); textWriter.WriteElementString("ChatGptApiKey", settings.Tools.ChatGptApiKey); textWriter.WriteElementString("ChatGptModel", settings.Tools.ChatGptModel); + textWriter.WriteElementString("GroqUrl", settings.Tools.GroqUrl); + textWriter.WriteElementString("GroqPrompt", settings.Tools.GroqPrompt); + textWriter.WriteElementString("GroqKey", settings.Tools.GroqApiKey); + textWriter.WriteElementString("GroqModel", settings.Tools.GroqModel); textWriter.WriteElementString("LmStudioApiUrl", settings.Tools.LmStudioApiUrl); textWriter.WriteElementString("LmStudioModel", settings.Tools.LmStudioModel); textWriter.WriteElementString("LmStudioPrompt", settings.Tools.LmStudioPrompt); diff --git a/src/libse/Settings/ToolsSettings.cs b/src/libse/Settings/ToolsSettings.cs index c3791b02e..29d572398 100644 --- a/src/libse/Settings/ToolsSettings.cs +++ b/src/libse/Settings/ToolsSettings.cs @@ -70,6 +70,10 @@ namespace Nikse.SubtitleEdit.Core.Settings public string ChatGptPrompt { get; set; } public string ChatGptApiKey { get; set; } public string ChatGptModel { get; set; } + public string GroqUrl { get; set; } + public string GroqPrompt { get; set; } + public string GroqApiKey { get; set; } + public string GroqModel { get; set; } public string LmStudioApiUrl { get; set; } public string LmStudioModel { get; set; } public string LmStudioPrompt { get; set; }