feat: Support batch fetch, and update AI prompt
This commit is contained in:
153
src/libs/batchQueue.js
Normal file
153
src/libs/batchQueue.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import { fetchTranslate } from "../apis/trans";
|
||||
|
||||
/**
|
||||
* 批处理队列
|
||||
* @param {*} translator
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const batchQueue = (
|
||||
{ translator, from, to, docInfo, apiSetting, usePool },
|
||||
{ batchInterval = 1000, batchSize = 10, batchLength = 10000 } = {}
|
||||
) => {
|
||||
const queue = [];
|
||||
let isProcessing = false;
|
||||
let timer = null;
|
||||
|
||||
const sendBatchRequest = async (payloads) => {
|
||||
const texts = payloads.map((item) => item.text);
|
||||
return fetchTranslate({
|
||||
translator,
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
docInfo,
|
||||
apiSetting,
|
||||
usePool,
|
||||
});
|
||||
};
|
||||
|
||||
const processQueue = async () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
|
||||
if (queue.length === 0 || isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProcessing = true;
|
||||
|
||||
let tasksToProcess = [];
|
||||
let currentBatchLength = 0;
|
||||
let endIndex = 0;
|
||||
|
||||
for (const task of queue) {
|
||||
const textLength = task.payload.text?.length || 0;
|
||||
if (
|
||||
endIndex >= batchSize ||
|
||||
(currentBatchLength + textLength > batchLength && endIndex > 0)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
currentBatchLength += textLength;
|
||||
endIndex++;
|
||||
}
|
||||
|
||||
if (endIndex > 0) {
|
||||
tasksToProcess = queue.splice(0, endIndex);
|
||||
}
|
||||
|
||||
if (tasksToProcess.length === 0) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const payloads = tasksToProcess.map((item) => item.payload);
|
||||
const responses = await sendBatchRequest(payloads);
|
||||
|
||||
tasksToProcess.forEach((taskItem, index) => {
|
||||
const response = responses[index];
|
||||
if (response) {
|
||||
taskItem.resolve(response);
|
||||
} else {
|
||||
taskItem.reject(new Error(`No response for item at index ${index}`));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
tasksToProcess.forEach((taskItem) => taskItem.reject(error));
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
if (queue.length > 0) {
|
||||
if (queue.length >= batchSize) {
|
||||
setTimeout(processQueue, 0);
|
||||
} else {
|
||||
scheduleProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleProcessing = () => {
|
||||
if (!isProcessing && !timer && queue.length > 0) {
|
||||
timer = setTimeout(processQueue, batchInterval);
|
||||
}
|
||||
};
|
||||
|
||||
const addTask = (data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const payload = data;
|
||||
queue.push({ payload, resolve, reject });
|
||||
|
||||
if (queue.length >= batchSize) {
|
||||
processQueue();
|
||||
} else {
|
||||
scheduleProcessing();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const destroy = () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
queue.forEach((task) =>
|
||||
task.reject(new Error("Queue instance was destroyed."))
|
||||
);
|
||||
queue.length = 0;
|
||||
};
|
||||
|
||||
return { addTask, destroy };
|
||||
};
|
||||
|
||||
// 实例字典
|
||||
const queueMap = new Map();
|
||||
|
||||
/**
|
||||
* 获取批处理实例
|
||||
* @param {*} translator
|
||||
* @returns
|
||||
*/
|
||||
export const getBatchQueue = (args, opts) => {
|
||||
const { translator, from, to } = args;
|
||||
const key = `${translator}_${from}_${to}`;
|
||||
if (queueMap.has(key)) {
|
||||
return queueMap.get(key);
|
||||
}
|
||||
|
||||
const queue = batchQueue(args, opts);
|
||||
queueMap.set(key, queue);
|
||||
return queue;
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除所有任务
|
||||
*/
|
||||
export const clearAllBatchQueue = () => {
|
||||
for (const queue of queueMap.entries()) {
|
||||
queue.destroy();
|
||||
}
|
||||
};
|
||||
@@ -24,6 +24,7 @@ import { isExt } from "./client";
|
||||
import { injectInlineJs, injectInternalCss } from "./injector";
|
||||
import { kissLog } from "./log";
|
||||
import interpreter from "./interpreter";
|
||||
import { clearAllBatchQueue } from "./batchQueue";
|
||||
|
||||
/**
|
||||
* 翻译类
|
||||
@@ -54,6 +55,7 @@ export class Translator {
|
||||
_keepSelector = "";
|
||||
_terms = [];
|
||||
_docTitle = "";
|
||||
_docDescription = "";
|
||||
|
||||
// 显示
|
||||
_interseObserver = new IntersectionObserver(
|
||||
@@ -95,6 +97,11 @@ export class Translator {
|
||||
});
|
||||
});
|
||||
|
||||
_getDocDescription = () => {
|
||||
const meta = document.querySelector('meta[name="description"]');
|
||||
return meta ? meta.getAttribute("content") : "";
|
||||
};
|
||||
|
||||
// 插入 shadowroot
|
||||
_overrideAttachShadow = () => {
|
||||
const _this = this;
|
||||
@@ -110,6 +117,8 @@ export class Translator {
|
||||
|
||||
this._setting = setting;
|
||||
this._rule = rule;
|
||||
this._docTitle = document.title;
|
||||
this._docDescription = this._getDocDescription();
|
||||
|
||||
this._keepSelector = rule.keepSelector || "";
|
||||
this._terms = (rule.terms || "")
|
||||
@@ -126,6 +135,13 @@ export class Translator {
|
||||
return this._setting;
|
||||
}
|
||||
|
||||
get docInfo() {
|
||||
return {
|
||||
title: this._docTitle,
|
||||
description: this._docDescription,
|
||||
};
|
||||
}
|
||||
|
||||
get eventName() {
|
||||
return this._eventName;
|
||||
}
|
||||
@@ -426,6 +442,7 @@ export class Translator {
|
||||
|
||||
// 清空任务池
|
||||
clearFetchPool();
|
||||
clearAllBatchQueue();
|
||||
};
|
||||
|
||||
_removeInjector = () => {
|
||||
|
||||
@@ -289,3 +289,16 @@ export const parseJsonObj = (str) => {
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 提取json内容
|
||||
* @param {*} s
|
||||
* @returns
|
||||
*/
|
||||
export const extractJson = (raw) => {
|
||||
if (!raw) return "{}";
|
||||
|
||||
let s = raw.replace(/^\s*```(?:json)?\s*/i, "").replace(/\s*```\s*$/i, "");
|
||||
const match = s.match(/\{[\s\S]*\}/);
|
||||
return match ? match[0] : "{}";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user