9#include <QNetworkRequest>
10#include <QNetworkReply>
11#include <QNetworkAccessManager>
13using namespace Qt::Literals::StringLiterals;
17MachineTranslator::MachineTranslator()
18 : m_request(std::make_unique<QNetworkRequest>()),
19 m_manager(std::make_unique<QNetworkAccessManager>()),
20 m_translator(std::make_unique<Ollama>())
22 m_request->setHeader(QNetworkRequest::ContentTypeHeader,
"application/json"_L1);
23 m_request->setTransferTimeout(5 * 60 * 1000);
30 QMutexLocker locker(&m_queueMutex);
32 m_pendingBatches.clear();
33 auto batches = m_translator->makeBatches(messages, userContext);
35 for (
auto &b : batches)
36 m_pendingBatches.enqueue(std::move(b));
43 QMutexLocker locker(&m_queueMutex);
44 m_pendingBatches.clear();
51 m_translator->setUrl(url);
52 m_request->setUrl(m_translator->translationEndpoint());
57 if (
auto wakeupPayload = m_translator->stageModel(modelName)) {
70 auto *tempManager =
new QNetworkAccessManager(
this);
72 QNetworkRequest wakeupRequest(m_translator->translationEndpoint());
73 wakeupRequest.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json"_L1);
74 wakeupRequest.setTransferTimeout(30000);
76 QNetworkReply *reply = tempManager->post(wakeupRequest, *wakeupPayload);
78 connect(reply, &QNetworkReply::finished,
this, [tempManager, reply]() {
80 tempManager->deleteLater();
87 QNetworkRequest req(m_translator->discoveryEndpoint());
88 QNetworkReply *reply = m_manager->get(req);
89 connect(reply, &QNetworkReply::finished,
this, [
this, reply]() {
92 if (reply->error() == QNetworkReply::NoError) {
93 const QByteArray response = reply->readAll();
94 models = m_translator->extractModels(response);
96 emit modelsReceived(std::move(models));
102 Q_ASSERT_X(!m_queueMutex.tryLock(), Q_FUNC_INFO,
103 "The function requires m_queueMutex to be held.");
107 const QByteArray body = m_translator->payload(b);
108 QNetworkReply *reply = m_manager->post(*m_request, body);
109 connect(reply, &QNetworkReply::finished,
this,
110 [
this, reply, batch = std::move(b), session = m_session.load()] {
111 translationReceived(reply, std::move(batch), session);
117 Q_ASSERT_X(!m_queueMutex.tryLock(), Q_FUNC_INFO,
118 "The function requires m_queueMutex to be held.");
119 if (m_stopped || m_pendingBatches.isEmpty())
122 const int batchesToSchedule =
123 qMin(s_maxConcurrentBatches - m_inFlightCount, m_pendingBatches.size());
124 for (
int i = 0; i < batchesToSchedule; ++i) {
125 Batch batch = m_pendingBatches.dequeue();
126 translateBatch(std::move(batch));
132 reply->deleteLater();
134 if (m_stopped || session != m_session) {
135 QMutexLocker locker(&m_queueMutex);
137 processNextBatches();
141 bool shouldRetry =
false;
143 if (reply->error() != QNetworkReply::NoError) {
144 const bool isRetriableError = reply->error() == QNetworkReply::OperationCanceledError
145 || reply->error() == QNetworkReply::TimeoutError
146 || reply->error() == QNetworkReply::UnknownNetworkError;
147 shouldRetry = b
.tries < s_maxTries && isRetriableError;
149 QList<
const TranslatorMessage *> failed;
150 for (
const auto &i : std::as_const(b.items))
151 failed.append(i.msg);
152 emit translationFailed(std::move(failed));
155 const QByteArray response = reply->readAll();
156 QList<Item> items = std::move(b.items);
157 QHash<
const TranslatorMessage *, QString> out;
158 QHash<QString, QString> translations = m_translator->extractTranslations(response);
161 QList<Item> nonMatched;
162 for (Item &i : items) {
163 if (i.msg->translation().isEmpty()) {
164 if (
auto translation = translations.find(i.msg->sourceText());
165 translation != translations.end()) {
166 out[i.msg] = *translation;
167 translations.erase(translation);
169 nonMatched.append(std::move(i));
175 constexpr int similarityThreshold = 200;
176 for (Item &i : nonMatched) {
177 StringSimilarityMatcher matcher(i.msg->sourceText());
180 for (
auto it = translations.cbegin(); it != translations.cend(); ++it) {
181 const int score = matcher.getSimilarityScore(it.key());
182 if (score >= similarityThreshold && score > bestScore) {
184 bestMatch = it.key();
188 if (!bestMatch.isEmpty())
189 out[i.msg] = translations.take(bestMatch);
191 b.items.append(std::move(i));
194 const bool nonTranslatedItems = !b.items.empty();
195 shouldRetry = nonTranslatedItems && b
.tries < s_maxTries;
196 if (nonTranslatedItems && !shouldRetry) {
197 QList<
const TranslatorMessage *> failed;
198 for (
const auto &i : std::as_const(b.items))
199 failed.append(i.msg);
200 emit translationFailed(std::move(failed));
203 emit batchTranslated(std::move(out));
206 QMutexLocker locker(&m_queueMutex);
210 m_pendingBatches.prepend(std::move(b));
212 processNextBatches();
void setUrl(const QString &url)
void translate(const Messages &messages, const QString &userContext=QString())
void activateTranslationModel(const QString &modelName)