"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeepSeekEngine = void 0;
const TranslationEngineOption_1 = require("./TranslationEngineOption");
const TranslationEngineWrapper_1 = require("./TranslationEngineWrapper");
class DeepSeekEngine extends TranslationEngineWrapper_1.TranslationEngineWrapper {
constructor(processor, thisAddon) {
super(processor, thisAddon, {
id: 'deepseek',
name: 'DeepSeek',
languages: {
en: 'English',
ja: 'Japanese'
},
batchDelay: 1,
description: thisAddon.package.description,
mode: 'rowByRow'
});
this.aborted = false;
this.paused = false;
this.pauseQueue = [];
// Replace URLs option with API key option
this.optionApiKey = new TranslationEngineOption_1.TranslationEngineOption({
wrapper: this,
id: 'apiKey',
default: '',
description: "Your DeepSeek API key. You can get one from the DeepSeek website.",
name: 'API Key',
category: TranslationEngineOption_1.TranslationEngineOptionCategories.LIMITS,
priority: -10,
formType: 'password'
});
this.optionModel = new TranslationEngineOption_1.TranslationEngineOption({
wrapper: this,
id: 'model',
default: 'deepseek-chat',
description: "The DeepSeek model to use for translation.",
name: 'Model',
category: TranslationEngineOption_1.TranslationEngineOptionCategories.OPTIONS
});
this.optionTemperature = new TranslationEngineOption_1.TranslationEngineOption({
wrapper: this,
id: 'temperature',
default: 0.3,
description: "Lower values make translations more consistent, higher values more creative.",
name: 'Temperature',
category: TranslationEngineOption_1.TranslationEngineOptionCategories.OPTIONS
});
this.optionMaxLength = new TranslationEngineOption_1.TranslationEngineOption({
wrapper: this,
id: 'maxTranslationLength',
default: 5000,
description: "Maximum amount of characters that will be sent per request to DeepSeek API.",
name: 'Request Character Limit',
category: TranslationEngineOption_1.TranslationEngineOptionCategories.LIMITS,
priority: -9
});
this.optionConcurrency = new TranslationEngineOption_1.TranslationEngineOption({
wrapper: this,
id: 'maxTranslationJobs',
default: 1,
description: 'Maximum concurrent requests to DeepSeek API.',
name: 'Maximum Request Count',
category: TranslationEngineOption_1.TranslationEngineOptionCategories.LIMITS,
priority: -8
});
}
async doTranslate(toTranslate, options) {
this.resetStatus();
let returnTranslations;
let returnError;
if (options.sl != 'ja') {
this.error(`[DeepSeek] The project specifies the source language as not being Japanese (${options.sl}). Since this engine is optimized for Japanese to English, we will use Japanese instead.`);
}
if (options.tl != 'en') {
this.error(`[DeepSeek] The project specifies the destination language as not being English (${options.tl}). Since this engine is optimized for Japanese to English, we will use English instead.`);
}
let translations = new Array(toTranslate.length);
let translatingIndex = 0;
let completeThreads = 0;
let totalThreads = this.optionConcurrency.getValue();
totalThreads = totalThreads < 1 ? 1 : totalThreads;
let maximumBatchSize = this.optionMaxLength.getValue();
let complete = () => {
if (++completeThreads == totalThreads) {
returnTranslations(translations);
}
};
let translationProgress = document.createTextNode('0');
let translatedCount = 0;
let errorProgress = document.createTextNode('');
let errorCount = 0;
this.print(document.createTextNode('[DeepSeek] Translating texts: '), translationProgress,
document.createTextNode('/' + toTranslate.length), errorProgress);
const updateTranslatedCount = (count) => {
translatedCount += count;
translationProgress.nodeValue = translatedCount.toString();
options.progress(Math.round((100 * translatedCount) / toTranslate.length));
};
const updateErrorCount = (count) => {
errorCount += count;
errorProgress.nodeValue = ' (' + errorCount.toString() + ' failed translations)';
};
const startThread = async () => {
if (this.aborted) {
returnError('Aborted.');
return;
}
if (this.paused) {
this.pauseQueue.push(() => {
startThread();
});
return;
}
let batchLength = 0;
let batch = [];
let batchIndexes = [];
while (translatingIndex < toTranslate.length) {
let index = translatingIndex;
if (toTranslate[index] !== undefined &&
(batchLength == 0 || batchLength + toTranslate[index].length <= maximumBatchSize)) {
batch.push(toTranslate[index]);
batchIndexes.push(index);
batchLength += toTranslate[index].length;
translatingIndex++;
} else {
break;
}
}
if (batch.length == 0) {
complete();
return;
}
try {
const apiKey = this.optionApiKey.getValue();
if (!apiKey) {
throw new Error('API key is required');
}
const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: this.optionModel.getValue(),
messages: [
{
role: "system",
content: "You are a professional translator. Translate the following Japanese text to natural, fluent English. Maintain the original meaning and tone."
},
{
role: "user",
content: `Translate these Japanese phrases to English:\n${batch.join('\n')}`
}
],
temperature: this.optionTemperature.getValue()
})
});
if (!response.ok) {
throw new Error(`${response.status} - ${response.statusText}`);
}
const result = await response.json();
const translatedText = result.choices[0]?.message?.content;
// Split the response back into individual translations
const translatedLines = translatedText.split('\n');
if (translatedLines.length !== batch.length) {
throw new Error(`Received ${translatedLines.length} translations for ${batch.length} inputs`);
}
for (let i = 0; i < batch.length; i++) {
translations[batchIndexes[i]] = translatedLines[i];
}
updateTranslatedCount(batch.length);
} catch (error) {
updateErrorCount(batch.length);
this.error(`[DeepSeek] Error during translation: ${error.message}`);
console.error('[DeepSeek] Translation error:', error);
} finally {
startThread();
}
};
return new Promise((resolve, reject) => {
returnTranslations = resolve;
returnError = reject;
for (let i = 0; i < totalThreads; i++) {
startThread();
}
});
}
resetStatus() {
this.aborted = false;
this.paused = false;
this.pauseQueue = [];
}
abort() {
this.aborted = true;
}
pause() {
this.paused = true;
}
resume() {
this.paused = false;
this.pauseQueue.forEach((action) => {
action();
});
this.pauseQueue = [];
}
}
exports.DeepSeekEngine = DeepSeekEngine;