feat: add custom api examples
This commit is contained in:
@@ -147,6 +147,12 @@ If encountering a 403 error, refer to: https://github.com/fishjar/kiss-translato
|
|||||||
|
|
||||||
Tampermonkey scripts require adding domains to the whitelist; otherwise, requests cannot be sent.
|
Tampermonkey scripts require adding domains to the whitelist; otherwise, requests cannot be sent.
|
||||||
|
|
||||||
|
### How to set up a hook function for a custom API
|
||||||
|
|
||||||
|
Custom APIs are very powerful and flexible, and can theoretically connect to any translation API.
|
||||||
|
|
||||||
|
Example reference: [custom-api_v2.md](https://github.com/fishjar/kiss-translator/blob/master/custom-api_v2.md)
|
||||||
|
|
||||||
## Future Plans
|
## Future Plans
|
||||||
|
|
||||||
This is a side project with no strict timeline. Community contributions are welcome. The following are preliminary feature directions:
|
This is a side project with no strict timeline. Community contributions are welcome. The following are preliminary feature directions:
|
||||||
|
|||||||
@@ -143,6 +143,12 @@
|
|||||||
|
|
||||||
油猴脚本需要增加域名白名单,否则不能发出请求。
|
油猴脚本需要增加域名白名单,否则不能发出请求。
|
||||||
|
|
||||||
|
### 如何设置自定义接口的hook函数
|
||||||
|
|
||||||
|
自定义接口功能非常强大、灵活,理论可以接入任何翻译接口。
|
||||||
|
|
||||||
|
示例参考: [custom-api_v2.md](https://github.com/fishjar/kiss-translator/blob/master/custom-api_v2.md)
|
||||||
|
|
||||||
## 未来规划
|
## 未来规划
|
||||||
|
|
||||||
本项目为业余开发,无严格时间表,欢迎社区共建。以下为初步设想的功能方向:
|
本项目为业余开发,无严格时间表,欢迎社区共建。以下为初步设想的功能方向:
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# 自定义接口示例(本文档已过期,新版不再适用)
|
# 自定义接口示例(本文档已过期,新版不再适用)
|
||||||
|
|
||||||
|
V2版的示例请查看这里:[custom-api_v2.md](https://github.com/fishjar/kiss-translator/blob/master/custom-api_v2.md)
|
||||||
|
|
||||||
以下示例为网友提供,仅供学习参考。
|
以下示例为网友提供,仅供学习参考。
|
||||||
|
|
||||||
## 本地运行 Seed-X-PPO-7B 量化模型
|
## 本地运行 Seed-X-PPO-7B 量化模型
|
||||||
|
|||||||
110
custom-api_v2.md
Normal file
110
custom-api_v2.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# 自定义接口示例
|
||||||
|
|
||||||
|
## 谷歌翻译接口
|
||||||
|
|
||||||
|
URL
|
||||||
|
|
||||||
|
```
|
||||||
|
https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN
|
||||||
|
```
|
||||||
|
|
||||||
|
Request Hook
|
||||||
|
|
||||||
|
```js
|
||||||
|
async (args) => {
|
||||||
|
const url = args.url.replace("{{text}}", args.texts[0]);
|
||||||
|
const method = "GET";
|
||||||
|
return { url, method };
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Response Hook
|
||||||
|
|
||||||
|
```js
|
||||||
|
async ({ res }) => {
|
||||||
|
return { translations: [[res.sentences[0].trans]] };
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Ollama
|
||||||
|
|
||||||
|
* 注意 ollama 启动参数需要添加环境变量 `OLLAMA_ORIGINS=*`
|
||||||
|
* 检查环境变量生效命令:`systemctl show ollama | grep OLLAMA_ORIGINS`
|
||||||
|
|
||||||
|
URL
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:11434/v1/chat/completions
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
||||||
|
'Act as a translation API. Output a single raw JSON object only. No extra text or fences.\n\nInput:\n{"targetLanguage":"<lang>","title":"<context>","description":"<context>","segments":[{"id":1,"text":"..."}],"glossary":{"sourceTerm":"targetTerm"},"tone":"<formal|casual>"}\n\nOutput:\n{"translations":[{"id":1,"text":"...","sourceLanguage":"<detected>"}]}\n\nRules:\n1. Use title/description for context only; do not output them.\n2. Keep id, order, and count of segments.\n3. Preserve whitespace, HTML entities, and all HTML-like tags (e.g., <i1>, <a1>). Translate inner text only.\n4. Highest priority: Follow \'glossary\'. Use value for translation; if value is "", keep the key.\n5. Do not translate: content in <code>, <pre>, text enclosed in backticks, or placeholders like {1}, {{1}}, [1], [[1]].\n6. Apply the specified tone to the translation.\n7. Detect sourceLanguage for each segment.\n8. Return empty or unchanged inputs as is.\n\nExample:\nInput: {"targetLanguage":"zh-CN","segments":[{"id":1,"text":"A <b>React</b> component."}],"glossary":{"component":"组件","React":""}}\nOutput: {"translations":[{"id":1,"text":"一个<b>React</b>组件","sourceLanguage":"en"}]}\n\nFail-safe: On any error, return {"translations":[]}.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
async ({ res }) => {
|
||||||
|
const extractJson = (raw) => {
|
||||||
|
const jsonRegex = /({.*}|\[.*\])/s;
|
||||||
|
const match = raw.match(jsonRegex);
|
||||||
|
return match ? match[0] : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseAIRes = (raw) => {
|
||||||
|
if (!raw) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jsonString = extractJson(raw);
|
||||||
|
if (!jsonString) return [];
|
||||||
|
|
||||||
|
const data = JSON.parse(jsonString);
|
||||||
|
if (Array.isArray(data.translations)) {
|
||||||
|
return data.translations.map((item) => [
|
||||||
|
item?.text ?? "",
|
||||||
|
item?.sourceLanguage ?? "",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("parseAIRes", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const translations = parseAIRes(res?.choices?.[0]?.message?.content);
|
||||||
|
|
||||||
|
return { translations };
|
||||||
|
};
|
||||||
|
```
|
||||||
@@ -98,8 +98,9 @@ const parseAIRes = (raw) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonString = extractJson(raw);
|
const jsonString = extractJson(raw);
|
||||||
const data = JSON.parse(jsonString);
|
if (!jsonString) return [];
|
||||||
|
|
||||||
|
const data = JSON.parse(jsonString);
|
||||||
if (Array.isArray(data.translations)) {
|
if (Array.isArray(data.translations)) {
|
||||||
// todo: 考虑序号id可能会打乱
|
// todo: 考虑序号id可能会打乱
|
||||||
return data.translations.map((item) => [
|
return data.translations.map((item) => [
|
||||||
@@ -925,7 +926,7 @@ export const handleTranslate = async (
|
|||||||
userMsg,
|
userMsg,
|
||||||
...apiSetting,
|
...apiSetting,
|
||||||
});
|
});
|
||||||
if (!Array.isArray(result)) {
|
if (!result?.length) {
|
||||||
throw new Error("tranlate got an unexpected result");
|
throw new Error("tranlate got an unexpected result");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -409,16 +409,16 @@ Good morning.
|
|||||||
\`\`\``;
|
\`\`\``;
|
||||||
|
|
||||||
const defaultRequestHook = `async (args, { url, body, headers, userMsg, method } = {}) => {
|
const defaultRequestHook = `async (args, { url, body, headers, userMsg, method } = {}) => {
|
||||||
console.log("request hook args:", args);
|
console.log("request hook args:", { args, url, body, headers, userMsg, method });
|
||||||
// return { url, body, headers, userMsg, method };
|
// return { url, body, headers, userMsg, method };
|
||||||
}`;
|
};`;
|
||||||
|
|
||||||
const defaultResponseHook = `async ({ res, ...args }) => {
|
const defaultResponseHook = `async ({ res, ...args }) => {
|
||||||
console.log("reaponse hook args:", res, args);
|
console.log("reaponse hook args:", { res, args });
|
||||||
// const translations = [["你好", "zh"]];
|
// const translations = [["你好", "zh"]];
|
||||||
// const modelMsg = "";
|
// const modelMsg = "";
|
||||||
// return { translations, modelMsg };
|
// return { translations, modelMsg };
|
||||||
}`;
|
};`;
|
||||||
|
|
||||||
// 翻译接口默认参数
|
// 翻译接口默认参数
|
||||||
const defaultApi = {
|
const defaultApi = {
|
||||||
|
|||||||
@@ -137,46 +137,42 @@ ${customApiLangs}
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const requestHookHelperZH = `1、第一个参数包含如下字段:'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
const requestHookHelperZH = `1、第一个参数包含如下字段:'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
||||||
2、返回值必须是包含以下字段的对象: 'url', 'body', 'headers', 'userMsg', 'method'
|
2、返回值必须是包含以下字段的对象: 'url', 'body', 'headers', 'method'
|
||||||
3、如返回空值,则hook函数不会产生任何效果。
|
3、如返回空值,则hook函数不会产生任何效果。
|
||||||
|
|
||||||
// 示例
|
// 示例
|
||||||
async (args, { url, body, headers, userMsg, method } = {}) => {
|
async (args, { url, body, headers, userMsg, method } = {}) => {
|
||||||
console.log("request hook args:", args);
|
|
||||||
return { url, body, headers, userMsg, method };
|
return { url, body, headers, userMsg, method };
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const requestHookHelperEN = `1. The first parameter contains the following fields: 'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
const requestHookHelperEN = `1. The first parameter contains the following fields: 'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
||||||
2. The return value must be an object containing the following fields: 'url', 'body', 'headers', 'userMsg', 'method'
|
2. The return value must be an object containing the following fields: 'url', 'body', 'headers', 'method'
|
||||||
3. If a null value is returned, the hook function will have no effect.
|
3. If a null value is returned, the hook function will have no effect.
|
||||||
|
|
||||||
// Example
|
// Example
|
||||||
async (args, { url, body, headers, userMsg, method } = {}) => {
|
async (args, { url, body, headers, userMsg, method } = {}) => {
|
||||||
console.log("request hook args:", args);
|
|
||||||
return { url, body, headers, userMsg, method };
|
return { url, body, headers, userMsg, method };
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const responsetHookHelperZH = `1、第一个参数包含如下字段:'res', ...
|
const responsetHookHelperZH = `1、第一个参数包含如下字段:'res', ...
|
||||||
2、返回值必须是包含以下字段的对象: 'translations', 'modelMsg'
|
2、返回值必须是包含以下字段的对象: 'translations'
|
||||||
('translations' 应为一个二维数组:[[译文, 源语言]])
|
('translations' 应为一个二维数组:[[译文, 源语言]])
|
||||||
3、如返回空值,则hook函数不会产生任何效果。
|
3、如返回空值,则hook函数不会产生任何效果。
|
||||||
|
|
||||||
// 示例
|
// 示例
|
||||||
async ({ res, ...args }) => {
|
async ({ res, ...args }) => {
|
||||||
console.log("reaponse hook args:", res, args);
|
|
||||||
const translations = [["你好", "zh"]];
|
const translations = [["你好", "zh"]];
|
||||||
const modelMsg = "";
|
const modelMsg = "";
|
||||||
return { translations, modelMsg };
|
return { translations, modelMsg };
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const responsetHookHelperEN = `1. The first parameter contains the following fields: 'res', ...
|
const responsetHookHelperEN = `1. The first parameter contains the following fields: 'res', ...
|
||||||
2. The return value must be an object containing the following fields: 'translations', 'modelMsg'
|
2. The return value must be an object containing the following fields: 'translations'
|
||||||
('translations' should be a two-dimensional array: [[translation, source language]]).
|
('translations' should be a two-dimensional array: [[translation, source language]]).
|
||||||
3. If a null value is returned, the hook function will have no effect.
|
3. If a null value is returned, the hook function will have no effect.
|
||||||
|
|
||||||
// Example
|
// Example
|
||||||
async ({ res, ...args }) => {
|
async ({ res, ...args }) => {
|
||||||
console.log("reaponse hook args:", res, args);
|
|
||||||
const translations = [["你好", "zh"]];
|
const translations = [["你好", "zh"]];
|
||||||
const modelMsg = "";
|
const modelMsg = "";
|
||||||
return { translations, modelMsg };
|
return { translations, modelMsg };
|
||||||
|
|||||||
Reference in New Issue
Block a user