feat: Support batch fetch, and update AI prompt

This commit is contained in:
Gabe
2025-09-03 00:37:35 +08:00
parent 2bfb27f346
commit d4e58fc925
14 changed files with 576 additions and 244 deletions

153
src/libs/batchQueue.js Normal file
View 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();
}
};

View File

@@ -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 = () => {

View File

@@ -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] : "{}";
};