Add translate via OpenRouter API - thx Nikodim :)

This commit is contained in:
Nikolaj Olsson 2024-07-24 07:04:28 +02:00
parent 4e7fbeaa7a
commit 024e4d4b55
5 changed files with 204 additions and 0 deletions

View File

@ -0,0 +1,112 @@
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 OpenRouterTranslate : IAutoTranslator
{
private HttpClient _httpClient;
public static string StaticName { get; set; } = "OpenRouter";
public string Name => StaticName;
public string Url => "https://openrouter.ai/";
public string Error { get; set; }
public int MaxCharacters => 1500;
/// <summary>
/// See https://openrouter.ai/docs/models
/// </summary>
public static string[] Models => new[]
{
"meta-llama/llama-3.1-8b-instruct",
"openai/gpt-4o-mini",
"google/gemma-2-27b-it",
"anthropic/claude-3.5-sonnet",
};
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.OpenRouterUrl.TrimEnd('/'));
_httpClient.Timeout = TimeSpan.FromMinutes(15);
if (!string.IsNullOrEmpty(Configuration.Settings.Tools.OpenRouterApiKey))
{
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + Configuration.Settings.Tools.OpenRouterApiKey);
}
}
public List<TranslationPair> GetSupportedSourceLanguages()
{
return ListLanguages();
}
public List<TranslationPair> GetSupportedTargetLanguages()
{
return ListLanguages();
}
public async Task<string> Translate(string text, string sourceLanguageCode, string targetLanguageCode, CancellationToken cancellationToken)
{
var model = Configuration.Settings.Tools.OpenRouterModel;
if (string.IsNullOrEmpty(model))
{
model = Models[0];
Configuration.Settings.Tools.OpenRouterModel = model;
}
if (string.IsNullOrEmpty(Configuration.Settings.Tools.OpenRouterPrompt))
{
Configuration.Settings.Tools.OpenRouterPrompt = new ToolsSettings().OpenRouterPrompt;
}
var prompt = string.Format(Configuration.Settings.Tools.OpenRouterPrompt, 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("OpenRouter 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<TranslationPair> ListLanguages()
{
return ChatGptTranslate.ListLanguages();
}
}
}

View File

@ -2210,6 +2210,30 @@ namespace Nikse.SubtitleEdit.Core.Settings
settings.Tools.GroqModel = subNode.InnerText;
}
subNode = node.SelectSingleNode("OpenRouterUrl");
if (subNode != null)
{
settings.Tools.OpenRouterUrl = subNode.InnerText;
}
subNode = node.SelectSingleNode("OpenRouterPrompt");
if (subNode != null)
{
settings.Tools.OpenRouterPrompt = subNode.InnerText;
}
subNode = node.SelectSingleNode("OpenRouterApiKey");
if (subNode != null)
{
settings.Tools.OpenRouterApiKey = subNode.InnerText;
}
subNode = node.SelectSingleNode("OpenRouterModel");
if (subNode != null)
{
settings.Tools.OpenRouterModel = subNode.InnerText;
}
subNode = node.SelectSingleNode("LmStudioApiUrl");
if (subNode != null)
{
@ -8909,6 +8933,10 @@ namespace Nikse.SubtitleEdit.Core.Settings
textWriter.WriteElementString("GroqPrompt", settings.Tools.GroqPrompt);
textWriter.WriteElementString("GroqApiKey", settings.Tools.GroqApiKey);
textWriter.WriteElementString("GroqModel", settings.Tools.GroqModel);
textWriter.WriteElementString("OpenRouterUrl", settings.Tools.OpenRouterUrl);
textWriter.WriteElementString("OpenRouterPrompt", settings.Tools.OpenRouterPrompt);
textWriter.WriteElementString("OpenRouterApiKey", settings.Tools.OpenRouterApiKey);
textWriter.WriteElementString("OpenRouterModel", settings.Tools.OpenRouterModel);
textWriter.WriteElementString("LmStudioApiUrl", settings.Tools.LmStudioApiUrl);
textWriter.WriteElementString("LmStudioModel", settings.Tools.LmStudioModel);
textWriter.WriteElementString("LmStudioPrompt", settings.Tools.LmStudioPrompt);

View File

@ -75,6 +75,10 @@ namespace Nikse.SubtitleEdit.Core.Settings
public string GroqPrompt { get; set; }
public string GroqApiKey { get; set; }
public string GroqModel { get; set; }
public string OpenRouterUrl { get; set; }
public string OpenRouterPrompt { get; set; }
public string OpenRouterApiKey { get; set; }
public string OpenRouterModel { get; set; }
public string LmStudioApiUrl { get; set; }
public string LmStudioModel { get; set; }
public string LmStudioPrompt { get; set; }
@ -466,6 +470,9 @@ namespace Nikse.SubtitleEdit.Core.Settings
GroqUrl = "https://api.groq.com/openai/v1/chat/completions";
GroqPrompt = "Translate from {0} to {1}, keep punctuation as input, do not censor the translation, give only the output without comments:";
GroqModel = GroqTranslate.Models[0];
OpenRouterUrl = "https://api.groq.com/openai/v1/chat/completions";
OpenRouterPrompt = "Translate from {0} to {1}, keep punctuation as input, do not censor the translation, give only the output without comments:";
OpenRouterModel = OpenRouterTranslate.Models[0];
LmStudioPrompt = "Translate from {0} to {1}, keep punctuation as input, do not censor the translation, give only the output without comments:";
OllamaApiUrl = "http://localhost:11434/api/generate";
OllamaModels = "llama3.1,llama3,llama2,mistral,dolphin-phi,phi,neural-chat,starling-lm,codellama,llama2-uncensored,llama2:13b,llama2:70b,orca-mini,vicuna,llava,gemma:2b,gemma:7b";

View File

@ -131,6 +131,7 @@ namespace Nikse.SubtitleEdit.Forms.Translate
new OllamaTranslate(),
new AnthropicTranslate(),
new GroqTranslate(),
new OpenRouterTranslate(),
new GeminiTranslate(),
new PapagoTranslate(),
new NoLanguageLeftBehindServe(),
@ -430,6 +431,32 @@ namespace Nikse.SubtitleEdit.Forms.Translate
}
if (engineType == typeof(OpenRouterTranslate))
{
FillUrls(new List<string>
{
Configuration.Settings.Tools.OpenRouterUrl,
});
labelApiKey.Left = nikseComboBoxUrl.Right + 12;
nikseTextBoxApiKey.Text = Configuration.Settings.Tools.OpenRouterApiKey;
nikseTextBoxApiKey.Left = labelApiKey.Right + 3;
labelApiKey.Visible = true;
nikseTextBoxApiKey.Visible = true;
labelFormality.Text = LanguageSettings.Current.AudioToText.Model;
labelFormality.Visible = true;
comboBoxFormality.Left = labelFormality.Right + 3;
comboBoxFormality.Visible = true;
comboBoxFormality.DropDownStyle = ComboBoxStyle.DropDown;
comboBoxFormality.Items.Clear();
comboBoxFormality.Items.AddRange(OpenRouterTranslate.Models);
comboBoxFormality.Text = Configuration.Settings.Tools.OpenRouterModel;
return;
}
if (engineType == typeof(GeminiTranslate))
{
nikseComboBoxUrl.Visible = false;
@ -1120,6 +1147,12 @@ namespace Nikse.SubtitleEdit.Forms.Translate
Configuration.Settings.Tools.GroqModel = comboBoxFormality.Text.Trim();
}
if (engineType == typeof(OpenRouterTranslate) && !string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text))
{
Configuration.Settings.Tools.OpenRouterApiKey = nikseTextBoxApiKey.Text.Trim();
Configuration.Settings.Tools.OpenRouterModel = comboBoxFormality.Text.Trim();
}
if (engineType == typeof(GeminiTranslate) && !string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text))
{
Configuration.Settings.Tools.GeminiProApiKey = nikseTextBoxApiKey.Text.Trim();

View File

@ -71,6 +71,22 @@ namespace Nikse.SubtitleEdit.Forms.Translate
nikseTextBoxPrompt.Text = new ToolsSettings().AnthropicPrompt;
}
}
else if (_engineType == typeof(GroqTranslate))
{
nikseTextBoxPrompt.Text = Configuration.Settings.Tools.GroqPrompt;
if (string.IsNullOrWhiteSpace(nikseTextBoxPrompt.Text))
{
nikseTextBoxPrompt.Text = new ToolsSettings().GroqPrompt;
}
}
else if (_engineType == typeof(OpenRouterTranslate))
{
nikseTextBoxPrompt.Text = Configuration.Settings.Tools.OpenRouterPrompt;
if (string.IsNullOrWhiteSpace(nikseTextBoxPrompt.Text))
{
nikseTextBoxPrompt.Text = new ToolsSettings().OpenRouterPrompt;
}
}
else
{
labelPrompt.Visible = false;
@ -127,6 +143,14 @@ namespace Nikse.SubtitleEdit.Forms.Translate
{
Configuration.Settings.Tools.AnthropicPrompt = nikseTextBoxPrompt.Text;
}
else if (_engineType == typeof(GroqTranslate))
{
Configuration.Settings.Tools.GroqPrompt = nikseTextBoxPrompt.Text;
}
else if (_engineType == typeof(OpenRouterTranslate))
{
Configuration.Settings.Tools.OpenRouterPrompt = nikseTextBoxPrompt.Text;
}
if (comboBoxParagraphHandling.SelectedIndex == 1)
{