mtengines
Version:
Machine Translation (MT) library written in TypeScript
299 lines • 11.6 kB
JavaScript
/*******************************************************************************
* Copyright (c) 2023-2026 Maxprograms.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse License 1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-v10.html
*
* Contributors:
* Maxprograms - initial API and implementation
*******************************************************************************/
import { DOMBuilder, SAXParser } from 'typesxml';
import { MTMatch } from './MTMatch.js';
import { MTUtils } from './MTUtils.js';
export class MistralTranslator {
apiKey;
model;
srcLang = '';
tgtLang = '';
constructor(apiKey, model) {
this.apiKey = apiKey;
if (model) {
this.model = model;
}
}
setModel(model) {
this.model = model;
}
getMTMatch(source, terms) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.generatePrompt(source, this.srcLang, this.tgtLang, terms);
let messages = [
{
role: "user",
content: prompt,
},
];
return new Promise((resolve, reject) => {
fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
messages: messages,
}),
}).then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}).then((data) => {
let choices = data.choices;
let translation = choices[0].message.content;
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (translation.startsWith('```') && translation.endsWith('```')) {
translation = translation.substring(3, translation.length - 3).trim();
}
let target = MTUtils.toXMLElement(translation);
let space = source.getAttribute('xml:space');
if (space) {
target.setAttribute(space);
}
resolve(new MTMatch(source, target, this.getShortName()));
}).catch((error) => {
reject(error);
});
});
}
handlesTags() {
return true;
}
fixesMatches() {
return true;
}
fixMatch(originalSource, matchSource, matchTarget) {
return new Promise((resolve, reject) => {
this.fixTranslation(originalSource, matchSource, matchTarget).then((translation) => {
let target = MTUtils.toXMLElement(translation);
resolve(new MTMatch(originalSource, target, this.getShortName()));
}).catch((error) => {
reject(error);
});
});
}
fixTranslation(originalSource, matchSource, matchTarget) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.fixMatchPrompt(originalSource, matchSource, matchTarget);
let messages = [
{ "role": "system", "content": MTUtils.getRole(this.srcLang, this.tgtLang) },
{ "role": "user", "content": prompt }
];
return new Promise((resolve, reject) => {
fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
messages: messages,
}),
}).then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}).then((data) => {
let choices = data.choices;
let translation = choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (!translation.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) {
translation = '<target>' + translation + '</target>';
}
resolve(translation);
}).catch((error) => {
reject(error);
});
});
}
fixesTags() {
return true;
}
fixTags(source, target) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
let prompt = MTUtils.fixTagsPrompt(source, target, this.srcLang, this.tgtLang);
let messages = [
{ "role": "system", "content": MTUtils.getRole(this.srcLang, this.tgtLang) },
{ "role": "user", "content": prompt }
];
return new Promise((resolve, reject) => {
fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
messages: messages,
}),
}).then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}).then((completion) => {
let choices = completion.choices;
let translation = choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (translation.startsWith('```xml') && translation.endsWith('```')) {
translation = translation.substring(6, translation.length - 3).trim();
}
if (!translation.trim().startsWith('<target') && !translation.trim().endsWith('</target>')) {
translation = '<target>' + translation + '</target>';
}
let contentHandler = new DOMBuilder();
let xmlParser = new SAXParser();
xmlParser.setContentHandler(contentHandler);
xmlParser.parseString(translation);
let newDoc = contentHandler.getDocument();
if (newDoc) {
const targetElement = newDoc.getRoot();
if (targetElement) {
resolve(targetElement);
}
else {
reject(new Error('No root element found in fixTags response'));
}
}
else {
reject(new Error('Error parsing XML from fixTags response'));
}
}).catch((error) => {
reject(error);
});
});
}
getName() {
return 'Mistral AI';
}
getShortName() {
return 'Mistral';
}
getSourceLanguages() {
return MTUtils.getLanguages();
}
getTargetLanguages() {
return MTUtils.getLanguages();
}
setSourceLanguage(lang) {
this.srcLang = lang;
}
getSourceLanguage() {
return this.srcLang;
}
setTargetLanguage(lang) {
this.tgtLang = lang;
}
getTargetLanguage() {
return this.tgtLang;
}
translate(source) {
if (!this.model) {
return Promise.reject(new Error('Model is not set.'));
}
if (this.srcLang === '' || this.tgtLang === '') {
return Promise.reject(new Error('Source and Target languages must be set before translation.'));
}
let prompt = MTUtils.translatePropmt(source, this.srcLang, this.tgtLang);
let messages = [
{
role: "user",
content: prompt,
},
];
return new Promise((resolve, reject) => {
fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
messages: messages,
}),
}).then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}).then((data) => {
let choices = data.choices;
let translation = choices[0].message.content;
if (translation.startsWith('\n\n')) {
translation = translation.substring(2);
}
while (translation.startsWith('"') && translation.endsWith('"')) {
translation = translation.substring(1, translation.length - 1);
}
if (source.startsWith('"') && source.endsWith('"')) {
translation = '"' + translation + '"';
}
resolve(translation);
})
.catch((error) => {
reject(error);
});
});
}
async getAvailableModels() {
if (!this.apiKey) {
return Promise.reject(new Error('API key is not set.'));
}
try {
const response = await fetch('https://api.mistral.ai/v1/models', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.apiKey,
},
});
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
const data = await response.json();
return data.data.map((model) => [model.id, model.display_name]);
}
catch (error) {
console.error('Error fetching available models:', error);
throw error;
}
}
}
//# sourceMappingURL=MistralTranslator.js.map