UNPKG

mtengines

Version:

Machine Translation (MT) library written in TypeScript

299 lines 11.6 kB
/******************************************************************************* * 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