mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2025-01-31 21:11:39 +01:00
Add DeepSeek translate API + update OpenRouter models
This commit is contained in:
parent
a296ba459f
commit
bd6b398cc4
111
src/libse/AutoTranslate/DeepSeekTranslate.cs
Normal file
111
src/libse/AutoTranslate/DeepSeekTranslate.cs
Normal file
@ -0,0 +1,111 @@
|
||||
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 DeepSeekTranslate : IAutoTranslator
|
||||
{
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public static string StaticName { get; set; } = "DeepSeek";
|
||||
public override string ToString() => StaticName;
|
||||
public string Name => StaticName;
|
||||
public string Url => "https://api.deepseek.com";
|
||||
public string Error { get; set; }
|
||||
public int MaxCharacters => 1500;
|
||||
|
||||
/// <summary>
|
||||
/// See https://api-docs.deepseek.com/
|
||||
/// </summary>
|
||||
public static string[] Models => new[]
|
||||
{
|
||||
"deepseek-reasoner",
|
||||
"deepseek-chat",
|
||||
};
|
||||
|
||||
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.DeepSeekUrl.TrimEnd('/'));
|
||||
_httpClient.Timeout = TimeSpan.FromMinutes(15);
|
||||
|
||||
if (!string.IsNullOrEmpty(Configuration.Settings.Tools.DeepSeekApiKey))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + Configuration.Settings.Tools.DeepSeekApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
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.DeepSeekModel;
|
||||
if (string.IsNullOrEmpty(model))
|
||||
{
|
||||
model = Models[0];
|
||||
Configuration.Settings.Tools.DeepSeekModel = model;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Configuration.Settings.Tools.DeepSeekPrompt))
|
||||
{
|
||||
Configuration.Settings.Tools.DeepSeekPrompt = new ToolsSettings().DeepSeekPrompt;
|
||||
}
|
||||
var prompt = string.Format(Configuration.Settings.Tools.DeepSeekPrompt, 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("DeepSeek 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -28,9 +28,11 @@ namespace Nikse.SubtitleEdit.Core.AutoTranslate
|
||||
/// </summary>
|
||||
public static string[] Models => new[]
|
||||
{
|
||||
"meta-llama/llama-3.1-8b-instruct",
|
||||
"openai/gpt-4o-mini",
|
||||
"google/gemma-2-27b-it",
|
||||
"deepseek/deepseek-r1",
|
||||
"google/gemini-2.0-flash-thinking-exp:free",
|
||||
"microsoft/phi-4",
|
||||
"meta-llama/llama-3.3-70b-instruct",
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"anthropic/claude-3.5-sonnet",
|
||||
};
|
||||
|
||||
|
@ -2221,6 +2221,30 @@ namespace Nikse.SubtitleEdit.Core.Settings
|
||||
settings.Tools.GroqModel = subNode.InnerText;
|
||||
}
|
||||
|
||||
subNode = node.SelectSingleNode("DeepSeekUrl");
|
||||
if (subNode != null)
|
||||
{
|
||||
settings.Tools.DeepSeekUrl = subNode.InnerText;
|
||||
}
|
||||
|
||||
subNode = node.SelectSingleNode("DeepSeekPrompt");
|
||||
if (subNode != null)
|
||||
{
|
||||
settings.Tools.DeepSeekPrompt = subNode.InnerText;
|
||||
}
|
||||
|
||||
subNode = node.SelectSingleNode("DeepSeekApiKey");
|
||||
if (subNode != null)
|
||||
{
|
||||
settings.Tools.DeepSeekApiKey = subNode.InnerText;
|
||||
}
|
||||
|
||||
subNode = node.SelectSingleNode("DeepSeekModel");
|
||||
if (subNode != null)
|
||||
{
|
||||
settings.Tools.DeepSeekModel = subNode.InnerText;
|
||||
}
|
||||
|
||||
subNode = node.SelectSingleNode("OpenRouterUrl");
|
||||
if (subNode != null)
|
||||
{
|
||||
@ -9071,6 +9095,10 @@ namespace Nikse.SubtitleEdit.Core.Settings
|
||||
xmlWriter.WriteElementString("GroqPrompt", settings.Tools.GroqPrompt);
|
||||
xmlWriter.WriteElementString("GroqApiKey", settings.Tools.GroqApiKey);
|
||||
xmlWriter.WriteElementString("GroqModel", settings.Tools.GroqModel);
|
||||
xmlWriter.WriteElementString("DeepSeekUrl", settings.Tools.DeepSeekUrl);
|
||||
xmlWriter.WriteElementString("DeepSeekPrompt", settings.Tools.DeepSeekPrompt);
|
||||
xmlWriter.WriteElementString("DeepSeekApiKey", settings.Tools.DeepSeekApiKey);
|
||||
xmlWriter.WriteElementString("DeepSeekModel", settings.Tools.DeepSeekModel);
|
||||
xmlWriter.WriteElementString("OpenRouterUrl", settings.Tools.OpenRouterUrl);
|
||||
xmlWriter.WriteElementString("OpenRouterPrompt", settings.Tools.OpenRouterPrompt);
|
||||
xmlWriter.WriteElementString("OpenRouterApiKey", settings.Tools.OpenRouterApiKey);
|
||||
|
@ -76,6 +76,10 @@ namespace Nikse.SubtitleEdit.Core.Settings
|
||||
public string GroqPrompt { get; set; }
|
||||
public string GroqApiKey { get; set; }
|
||||
public string GroqModel { get; set; }
|
||||
public string DeepSeekUrl { get; set; }
|
||||
public string DeepSeekPrompt { get; set; }
|
||||
public string DeepSeekApiKey { get; set; }
|
||||
public string DeepSeekModel { get; set; }
|
||||
public string OpenRouterUrl { get; set; }
|
||||
public string OpenRouterPrompt { get; set; }
|
||||
public string OpenRouterApiKey { get; set; }
|
||||
@ -484,6 +488,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];
|
||||
DeepSeekUrl = "https://api.deepseek.com/chat/completions";
|
||||
DeepSeekPrompt = "Translate from {0} to {1}, keep punctuation as input, do not censor the translation, give only the output without comments:";
|
||||
DeepSeekModel = DeepSeekTranslate.Models[0];
|
||||
OpenRouterUrl = "https://openrouter.ai/api/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];
|
||||
|
@ -130,6 +130,7 @@ namespace Nikse.SubtitleEdit.Forms.Translate
|
||||
new OllamaTranslate(),
|
||||
new AnthropicTranslate(),
|
||||
new GroqTranslate(),
|
||||
new DeepSeekTranslate(),
|
||||
new OpenRouterTranslate(),
|
||||
new GeminiTranslate(),
|
||||
new PapagoTranslate(),
|
||||
@ -445,6 +446,30 @@ namespace Nikse.SubtitleEdit.Forms.Translate
|
||||
return;
|
||||
}
|
||||
|
||||
if (engineType == typeof(DeepSeekTranslate))
|
||||
{
|
||||
FillUrls(new List<string>
|
||||
{
|
||||
Configuration.Settings.Tools.DeepSeekUrl,
|
||||
});
|
||||
|
||||
labelApiKey.Left = nikseComboBoxUrl.Right + 12;
|
||||
nikseTextBoxApiKey.Text = Configuration.Settings.Tools.DeepSeekApiKey;
|
||||
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(DeepSeekTranslate.Models);
|
||||
comboBoxFormality.Text = Configuration.Settings.Tools.DeepSeekModel;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (engineType == typeof(OpenRouterTranslate))
|
||||
{
|
||||
@ -1184,6 +1209,12 @@ namespace Nikse.SubtitleEdit.Forms.Translate
|
||||
Configuration.Settings.Tools.GroqModel = comboBoxFormality.Text.Trim();
|
||||
}
|
||||
|
||||
if (engineType == typeof(DeepSeekTranslate) && !string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text))
|
||||
{
|
||||
Configuration.Settings.Tools.DeepSeekApiKey = nikseTextBoxApiKey.Text.Trim();
|
||||
Configuration.Settings.Tools.DeepSeekModel = comboBoxFormality.Text.Trim();
|
||||
}
|
||||
|
||||
if (engineType == typeof(OpenRouterTranslate) && !string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text))
|
||||
{
|
||||
Configuration.Settings.Tools.OpenRouterApiKey = nikseTextBoxApiKey.Text.Trim();
|
||||
|
@ -79,6 +79,14 @@ namespace Nikse.SubtitleEdit.Forms.Translate
|
||||
nikseTextBoxPrompt.Text = new ToolsSettings().GroqPrompt;
|
||||
}
|
||||
}
|
||||
else if (_engineType == typeof(DeepSeekTranslate))
|
||||
{
|
||||
nikseTextBoxPrompt.Text = Configuration.Settings.Tools.DeepSeekPrompt;
|
||||
if (string.IsNullOrWhiteSpace(nikseTextBoxPrompt.Text))
|
||||
{
|
||||
nikseTextBoxPrompt.Text = new ToolsSettings().DeepSeekPrompt;
|
||||
}
|
||||
}
|
||||
else if (_engineType == typeof(OpenRouterTranslate))
|
||||
{
|
||||
nikseTextBoxPrompt.Text = Configuration.Settings.Tools.OpenRouterPrompt;
|
||||
@ -150,6 +158,10 @@ namespace Nikse.SubtitleEdit.Forms.Translate
|
||||
{
|
||||
Configuration.Settings.Tools.GroqPrompt = nikseTextBoxPrompt.Text;
|
||||
}
|
||||
else if (_engineType == typeof(DeepSeekTranslate))
|
||||
{
|
||||
Configuration.Settings.Tools.DeepSeekPrompt = nikseTextBoxPrompt.Text;
|
||||
}
|
||||
else if (_engineType == typeof(OpenRouterTranslate))
|
||||
{
|
||||
Configuration.Settings.Tools.OpenRouterPrompt = nikseTextBoxPrompt.Text;
|
||||
|
Loading…
x
Reference in New Issue
Block a user