From b2eea5d0d78f1aac784df7c200ee5abcfc12ce58 Mon Sep 17 00:00:00 2001 From: Gabe Date: Fri, 17 Oct 2025 01:47:36 +0800 Subject: [PATCH] fix: custom api doc --- custom-api_v2.md | 42 ++++++++++++++++++++++++++++++++++++++++++ src/apis/trans.js | 21 ++++++++++++++------- src/config/api.js | 4 ++-- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/custom-api_v2.md b/custom-api_v2.md index 5c08d78..9f370e9 100644 --- a/custom-api_v2.md +++ b/custom-api_v2.md @@ -72,6 +72,39 @@ async (args) => { }; ``` +v2.0.2 Request Hook 可以简化为: + +```js +async (args) => { + const url = args.url; + const method = "POST"; + const headers = { "Content-type": "application/json" }; + const body = { + model: "gemma3", + messages: [ + { + role: "system", + content: args.defaultSystemPrompt, + }, + { + role: "user", + content: JSON.stringify({ + targetLanguage: args.to, + segments: args.texts.map((text, id) => ({ id, text })), + glossary: {}, + }), + }, + ], + temperature: 0, + max_tokens: 20480, + think: false, + stream: false, + }; + + return { url, body, headers, method }; +}; +``` + Response Hook ```js @@ -108,3 +141,12 @@ async ({ res }) => { return { translations }; }; ``` + +v2.0.2 版后内置`parseAIRes`函数,Response Hook 可以简化为: + +```js +async ({ res, parseAIRes, }) => { + const translations = parseAIRes(res?.choices?.[0]?.message?.content); + return { translations }; +}; +``` diff --git a/src/apis/trans.js b/src/apis/trans.js index 5034123..bc342d9 100644 --- a/src/apis/trans.js +++ b/src/apis/trans.js @@ -26,6 +26,8 @@ import { INPUT_PLACE_KEY, INPUT_PLACE_MODEL, DEFAULT_USER_AGENT, + defaultSystemPrompt, + defaultSubtitlePrompt, } from "../config"; import { msAuth } from "../libs/auth"; import { genDeeplFree } from "./deepl"; @@ -678,13 +680,16 @@ export const genTransReq = async ({ reqHook, ...args }) => { if (reqHook?.trim() && !events) { try { interpreter.run(`exports.reqHook = ${reqHook}`); - const hookResult = await interpreter.exports.reqHook(args, { - url, - body, - headers, - userMsg, - method, - }); + const hookResult = await interpreter.exports.reqHook( + { ...args, defaultSystemPrompt, defaultSubtitlePrompt }, + { + url, + body, + headers, + userMsg, + method, + } + ); if (hookResult && hookResult.url) { return genInit(hookResult); } @@ -732,6 +737,8 @@ export const parseTransRes = async ( fromLang, toLang, langMap, + extractJson, + parseAIRes, }); if (hookResult && Array.isArray(hookResult.translations)) { if (history && userMsg && hookResult.modelMsg) { diff --git a/src/config/api.js b/src/config/api.js index d379975..1656fcf 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -340,7 +340,7 @@ Object.entries(OPT_LANGS_TO_SPEC).forEach(([t, m]) => { OPT_LANGS_TO_CODE[t] = specToCode(m); }); -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: {"targetLanguage":"","title":"","description":"","segments":[{"id":1,"text":"..."}],"glossary":{"sourceTerm":"targetTerm"},"tone":""} @@ -381,7 +381,7 @@ Fail-safe: On any error, return {"translations":[]}.`; // 4. **Special Cases**: '[Music]' (and similar cues) are standalone entries. Translate appropriately (e.g., '[音乐]', '[Musique]'). // `; -const defaultSubtitlePrompt = `You are an expert AI for subtitle generation. Convert a JSON array of word-level timestamps into a bilingual VTT file. +export const defaultSubtitlePrompt = `You are an expert AI for subtitle generation. Convert a JSON array of word-level timestamps into a bilingual VTT file. **Workflow:** 1. Merge \`text\` fields into complete sentences; ignore empty text.