feat: support ai model with no batch

This commit is contained in:
Gabe
2025-10-31 14:36:33 +08:00
parent 53d441b3f5
commit 0a4a2b46c1
4 changed files with 94 additions and 40 deletions

View File

@@ -22,12 +22,14 @@ import {
API_SPE_TYPES, API_SPE_TYPES,
INPUT_PLACE_FROM, INPUT_PLACE_FROM,
INPUT_PLACE_TO, INPUT_PLACE_TO,
// INPUT_PLACE_TEXT, INPUT_PLACE_TEXT,
INPUT_PLACE_KEY, INPUT_PLACE_KEY,
INPUT_PLACE_MODEL, INPUT_PLACE_MODEL,
DEFAULT_USER_AGENT, DEFAULT_USER_AGENT,
defaultSystemPrompt, defaultSystemPrompt,
defaultSubtitlePrompt, defaultSubtitlePrompt,
defaultNobatchPrompt,
defaultNobatchUserPrompt,
} from "../config"; } from "../config";
import { msAuth } from "../libs/auth"; import { msAuth } from "../libs/auth";
import { genDeeplFree } from "./deepl"; import { genDeeplFree } from "./deepl";
@@ -66,38 +68,41 @@ const genSystemPrompt = ({ systemPrompt, from, to }) =>
.replaceAll(INPUT_PLACE_TO, to); .replaceAll(INPUT_PLACE_TO, to);
const genUserPrompt = ({ const genUserPrompt = ({
// userPrompt, nobatchUserPrompt,
useBatchFetch,
tone, tone,
glossary = {}, glossary = {},
// from, from,
to, to,
texts, texts,
docInfo, docInfo,
}) => { }) => {
const prompt = JSON.stringify({ if (useBatchFetch) {
targetLanguage: to, return JSON.stringify({
title: docInfo.title, targetLanguage: to,
description: docInfo.description, title: docInfo.title,
segments: texts.map((text, i) => ({ id: i, text })), description: docInfo.description,
glossary, segments: texts.map((text, i) => ({ id: i, text })),
tone, glossary,
}); tone,
});
}
// if (userPrompt.includes(INPUT_PLACE_TEXT)) { return nobatchUserPrompt
// return userPrompt .replaceAll(INPUT_PLACE_FROM, from)
// .replaceAll(INPUT_PLACE_FROM, from) .replaceAll(INPUT_PLACE_TO, to)
// .replaceAll(INPUT_PLACE_TO, to) .replaceAll(INPUT_PLACE_TEXT, texts[0]);
// .replaceAll(INPUT_PLACE_TEXT, prompt);
// }
return prompt;
}; };
const parseAIRes = (raw) => { const parseAIRes = (raw, useBatchFetch = true) => {
if (!raw) { if (!raw) {
return []; return [];
} }
if (!useBatchFetch) {
return [[raw]];
}
try { try {
const jsonString = extractJson(raw); const jsonString = extractJson(raw);
if (!jsonString) return []; if (!jsonString) return [];
@@ -627,7 +632,10 @@ export const genTransReq = async ({ reqHook, ...args }) => {
apiSlug, apiSlug,
key, key,
systemPrompt, systemPrompt,
userPrompt, // userPrompt,
nobatchPrompt = defaultNobatchPrompt,
nobatchUserPrompt = defaultNobatchUserPrompt,
useBatchFetch,
from, from,
to, to,
texts, texts,
@@ -647,11 +655,16 @@ export const genTransReq = async ({ reqHook, ...args }) => {
} }
if (API_SPE_TYPES.ai.has(apiType)) { if (API_SPE_TYPES.ai.has(apiType)) {
args.systemPrompt = genSystemPrompt({ systemPrompt, from, to }); args.systemPrompt = genSystemPrompt({
systemPrompt: useBatchFetch ? systemPrompt : nobatchPrompt,
from,
to,
});
args.userPrompt = !!events args.userPrompt = !!events
? JSON.stringify(events) ? JSON.stringify(events)
: genUserPrompt({ : genUserPrompt({
userPrompt, nobatchUserPrompt,
useBatchFetch,
from, from,
to, to,
texts, texts,
@@ -721,6 +734,7 @@ export const parseTransRes = async (
history, history,
userMsg, userMsg,
apiType, apiType,
useBatchFetch,
} }
) => { ) => {
// 执行 response hook // 执行 response hook
@@ -811,13 +825,13 @@ export const parseTransRes = async (
content: modelMsg.content, content: modelMsg.content,
}); });
} }
return parseAIRes(modelMsg?.content); return parseAIRes(modelMsg?.content, useBatchFetch);
case OPT_TRANS_GEMINI: case OPT_TRANS_GEMINI:
modelMsg = res?.candidates?.[0]?.content; modelMsg = res?.candidates?.[0]?.content;
if (history && userMsg && modelMsg) { if (history && userMsg && modelMsg) {
history.add(userMsg, modelMsg); history.add(userMsg, modelMsg);
} }
return parseAIRes(res?.candidates?.[0]?.content?.parts?.[0]?.text ?? ""); return parseAIRes(modelMsg?.parts?.[0]?.text ?? "", useBatchFetch);
case OPT_TRANS_CLAUDE: case OPT_TRANS_CLAUDE:
modelMsg = { role: res?.role, content: res?.content?.text }; modelMsg = { role: res?.role, content: res?.content?.text };
if (history && userMsg && modelMsg) { if (history && userMsg && modelMsg) {
@@ -826,7 +840,7 @@ export const parseTransRes = async (
content: modelMsg.content, content: modelMsg.content,
}); });
} }
return parseAIRes(res?.content?.[0]?.text ?? ""); return parseAIRes(res?.content?.[0]?.text ?? "", useBatchFetch);
case OPT_TRANS_CLOUDFLAREAI: case OPT_TRANS_CLOUDFLAREAI:
return [[res?.result?.translated_text]]; return [[res?.result?.translated_text]];
case OPT_TRANS_OLLAMA: case OPT_TRANS_OLLAMA:
@@ -845,7 +859,7 @@ export const parseTransRes = async (
content: modelMsg.content, content: modelMsg.content,
}); });
} }
return parseAIRes(modelMsg?.content); return parseAIRes(modelMsg?.content, useBatchFetch);
case OPT_TRANS_CUSTOMIZE: case OPT_TRANS_CUSTOMIZE:
return (res?.translations ?? res)?.map((item) => [item.text, item.src]); return (res?.translations ?? res)?.map((item) => [item.text, item.src]);
default: default:

View File

@@ -340,6 +340,9 @@ Object.entries(OPT_LANGS_TO_SPEC).forEach(([t, m]) => {
OPT_LANGS_TO_CODE[t] = specToCode(m); OPT_LANGS_TO_CODE[t] = specToCode(m);
}); });
export const defaultNobatchPrompt = `You are a professional, authentic machine translation engine.`;
export const defaultNobatchUserPrompt = `Translate the following source text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}. Output translation directly without any additional text.\n\nSource Text: ${INPUT_PLACE_TEXT}\n\nTranslated Text:`;
export const defaultSystemPrompt = `Act as a translation API. Output a single raw JSON object only. No extra text or fences. export const defaultSystemPrompt = `Act as a translation API. Output a single raw JSON object only. No extra text or fences.
Input: Input:
@@ -430,6 +433,8 @@ const defaultApi = {
model: "", // 模型名称 model: "", // 模型名称
systemPrompt: defaultSystemPrompt, systemPrompt: defaultSystemPrompt,
subtitlePrompt: defaultSubtitlePrompt, subtitlePrompt: defaultSubtitlePrompt,
nobatchPrompt: defaultNobatchPrompt,
nobatchUserPrompt: defaultNobatchUserPrompt,
userPrompt: "", userPrompt: "",
tone: BUILTIN_STONES[0], // 翻译风格 tone: BUILTIN_STONES[0], // 翻译风格
placeholder: BUILTIN_PLACEHOLDERS[0], // 占位符 placeholder: BUILTIN_PLACEHOLDERS[0], // 占位符

View File

@@ -62,7 +62,7 @@ export function AlertProvider({ children }) {
<Alert <Alert
onClose={handleClose} onClose={handleClose}
severity={severity} severity={severity}
sx={{ maxWidth: "80%" }} sx={{ minWidth: "300px", maxWidth: "80%" }}
> >
{message} {message}
</Alert> </Alert>

View File

@@ -44,6 +44,8 @@ import {
BUILTIN_PLACEHOLDERS, BUILTIN_PLACEHOLDERS,
BUILTIN_PLACETAGS, BUILTIN_PLACETAGS,
OPT_TRANS_AZUREAI, OPT_TRANS_AZUREAI,
defaultNobatchPrompt,
defaultNobatchUserPrompt,
} from "../../config"; } from "../../config";
import ValidationInput from "../../hooks/ValidationInput"; import ValidationInput from "../../hooks/ValidationInput";
@@ -54,8 +56,9 @@ function TestButton({ api }) {
const handleApiTest = async () => { const handleApiTest = async () => {
try { try {
setLoading(true); setLoading(true);
const text = "hello world";
const { trText } = await apiTranslate({ const { trText } = await apiTranslate({
text: "hello world", text,
fromLang: "en", fromLang: "en",
toLang: "zh-CN", toLang: "zh-CN",
apiSetting: { ...api }, apiSetting: { ...api },
@@ -65,7 +68,13 @@ function TestButton({ api }) {
if (!trText) { if (!trText) {
throw new Error("empty result"); throw new Error("empty result");
} }
alert.success(i18n("test_success")); alert.success(
<>
<div>{i18n("test_success")}</div>
<div>{text}</div>
<div>{trText}</div>
</>
);
} catch (err) { } catch (err) {
// alert.error(`${i18n("test_failed")}: ${err.message}`); // alert.error(`${i18n("test_failed")}: ${err.message}`);
let msg = err.message; let msg = err.message;
@@ -164,6 +173,8 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
model = "", model = "",
apiType, apiType,
systemPrompt = "", systemPrompt = "",
nobatchPrompt = defaultNobatchPrompt,
nobatchUserPrompt = defaultNobatchUserPrompt,
subtitlePrompt = "", subtitlePrompt = "",
// userPrompt = "", // userPrompt = "",
customHeader = "", customHeader = "",
@@ -305,16 +316,40 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
</Grid> </Grid>
</Box> </Box>
<TextField {useBatchFetch ? (
size="small" <TextField
label={"SYSTEM PROMPT"} size="small"
name="systemPrompt" label={"BATCH SYSTEM PROMPT"}
value={systemPrompt} name="systemPrompt"
onChange={handleChange} value={systemPrompt}
multiline onChange={handleChange}
maxRows={10} multiline
helperText={i18n("system_prompt_helper")} maxRows={10}
/> helperText={i18n("system_prompt_helper")}
/>
) : (
<>
<TextField
size="small"
label={"SYSTEM PROMPT"}
name="nobatchPrompt"
value={nobatchPrompt}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
size="small"
label={"USER PROMPT"}
name="nobatchUserPrompt"
value={nobatchUserPrompt}
onChange={handleChange}
multiline
maxRows={10}
/>
</>
)}
<TextField <TextField
size="small" size="small"
label={"SUBTITLE PROMPT"} label={"SUBTITLE PROMPT"}