mirror of
https://github.com/Yuukiy/JavSP.git
synced 2026-10-17 05:11:21 +08:00
支持Claude(haiku)和Groq(llama3.1-70b) 翻譯 (#349)
* add claude support * add groq (llama3.1) support) * fix error handling for ai translation * change to zh_cn for original codebase --------- Co-authored-by: Yuukiy <76897913+Yuukiy@users.noreply.github.com>
This commit is contained in:
+5
-1
@@ -115,7 +115,7 @@ aip_secret_key =
|
||||
add_label_to_cover = no
|
||||
|
||||
[Translate]
|
||||
# 翻译引擎,可选: google, bing, baidu (Google可以直接免费使用。留空表示禁用翻译功能)
|
||||
# 翻译引擎,可选: google, bing, baidu, claude(haiku), groq(llama 3.1-70b) (Google可以直接免费使用。留空表示禁用翻译功能)
|
||||
# 进阶功能的文档 https://github.com/Yuukiy/JavSP/wiki/Translation-%7C-%E7%BF%BB%E8%AF%91
|
||||
engine =
|
||||
# 是否翻译标题
|
||||
@@ -127,6 +127,10 @@ baidu_appid =
|
||||
baidu_key =
|
||||
# 微软必应翻译(Azure 认知服务 → 翻译)的密钥
|
||||
bing_key =
|
||||
# Claude的密钥 (使用haiku模型)
|
||||
claude_key =
|
||||
# Groq的密钥 (使用llama 3.1-70b模型)
|
||||
groq_key =
|
||||
|
||||
[NFO]
|
||||
# 同时将genre写入到tag?
|
||||
|
||||
@@ -310,6 +310,8 @@ def validate_translation(cfg: Config):
|
||||
trans.baidu_appid = os.getenv('JAVSP_BAIDU_APPID', trans.baidu_appid)
|
||||
trans.baidu_key = os.getenv('JAVSP_BAIDU_KEY', trans.baidu_key)
|
||||
trans.bing_key = os.getenv('JAVSP_BING_KEY', trans.bing_key)
|
||||
trans.claude_key = os.getenv('JAVSP_CLAUDE_KEY', trans.claude_key)
|
||||
trans.groq_key = os.getenv('JAVSP_GROQ_KEY', trans.groq_key)
|
||||
# 先获取访问凭据再判断翻译引擎,这样的话即使配置文件中未启用翻译也可以调试翻译功能
|
||||
if trans.engine == '':
|
||||
return
|
||||
@@ -328,6 +330,16 @@ def validate_translation(cfg: Config):
|
||||
logger.error('启用必应翻译时,key不能留空')
|
||||
elif engine_name == 'google':
|
||||
cfg.Translate.engine = engine_name
|
||||
elif engine_name == 'claude':
|
||||
if trans.claude_key:
|
||||
cfg.Translate.engine = engine_name
|
||||
else:
|
||||
logger.error('启用Claude时,key不能留空')
|
||||
elif engine_name == 'groq':
|
||||
if trans.groq_key:
|
||||
cfg.Translate.engine = engine_name
|
||||
else:
|
||||
logger.error('启用Groq时,key不能留空')
|
||||
else:
|
||||
logger.error('无效的翻译引擎: ' + engine_name)
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ def translate(texts, engine='google', actress=[]):
|
||||
仅在能判断分句时有breaks字段,子句末尾可能有换行符\n
|
||||
翻译出错: {'error': 'baidu: 54000: PARAM_FROM_TO_OR_Q_EMPTY'}
|
||||
"""
|
||||
rtn = {}
|
||||
err_msg = ''
|
||||
if engine == 'baidu':
|
||||
result = baidu_translate(texts)
|
||||
@@ -105,6 +106,24 @@ def translate(texts, engine='google', actress=[]):
|
||||
err_msg = "{}: {}: {}".format(engine, result['error_code'], result['error_msg'])
|
||||
except Exception as e:
|
||||
err_msg = "{}: {}: Exception: {}".format(engine, -2, repr(e))
|
||||
elif engine == 'claude':
|
||||
try:
|
||||
result = claude_translate(texts)
|
||||
if 'error_code' not in result:
|
||||
rtn = {'trans': result}
|
||||
else:
|
||||
err_msg = "{}: {}: {}".format(engine, result['error_code'], result['error_msg'])
|
||||
except Exception as e:
|
||||
err_msg = "{}: {}: Exception: {}".format(engine, -2, repr(e))
|
||||
elif engine == 'groq':
|
||||
try:
|
||||
result = groq_translate(texts)
|
||||
if 'error_code' not in result:
|
||||
rtn = {'trans': result}
|
||||
else:
|
||||
err_msg = "{}: {}: {}".format(engine, result['error_code'], result['error_msg'])
|
||||
except Exception as e:
|
||||
err_msg = "{}: {}: Exception: {}".format(engine, -2, repr(e))
|
||||
# else:
|
||||
# 配置文件中已经检查过翻译引擎,这里不再检查,因此如果使用不在列表中的翻译引擎,会出错
|
||||
# 如果err_msg非空,说明发生了错误,返回错误消息
|
||||
@@ -172,6 +191,67 @@ def google_trans(texts, to='zh_CN'):
|
||||
time.sleep(4) # Google翻译的API有QPS限制,因此需要等待一段时间
|
||||
return result
|
||||
|
||||
def claude_translate(texts, to="zh_CN"):
|
||||
"""使用Claude翻译文本(默认翻译为简体中文)"""
|
||||
api_url = "https://api.anthropic.com/v1/messages"
|
||||
headers = {
|
||||
"x-api-key": cfg.Translate.claude_key,
|
||||
"context-type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
}
|
||||
data = {
|
||||
"model": "claude-3-haiku-20240307",
|
||||
"system": f"Translate the following Japanese paragraph into {to}, while leaving non-Japanese text, names, or text that does not look like Japanese untranslated. Reply with the translated text only, do not add any text that is not in the original content.",
|
||||
"max_tokens": 1024,
|
||||
"messages": [{"role": "user", "content": texts}],
|
||||
}
|
||||
r = requests.post(api_url, headers=headers, json=data)
|
||||
if r.status_code == 200:
|
||||
result = r.json().get("content", [{}])[0].get("text", "").strip()
|
||||
else:
|
||||
result = {
|
||||
"error_code": r.status_code,
|
||||
"error_msg": r.json().get("error", {}).get("message", r.reason),
|
||||
}
|
||||
return result
|
||||
|
||||
def groq_translate(texts, to="zh_CN"):
|
||||
"""使用Groq翻译文本(默认翻译为简体中文)"""
|
||||
api_url = "https://api.groq.com/openai/v1/chat/completions"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {cfg.Translate.groq_key}",
|
||||
}
|
||||
data = {
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": f"Translate the following Japanese paragraph into {to}, while leaving non-Japanese text, names, or text that does not look like Japanese untranslated. Reply with the translated text only, do not add any text that is not in the original content."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": texts
|
||||
}
|
||||
],
|
||||
"model": "llama-3.1-70b-versatile",
|
||||
"temperature": 0,
|
||||
"max_tokens": 1024,
|
||||
}
|
||||
r = requests.post(api_url, headers=headers, json=data)
|
||||
if r.status_code == 200:
|
||||
if 'error' in r.json():
|
||||
result = {
|
||||
"error_code": r.status_code,
|
||||
"error_msg": r.json().get("error", {}).get("message", ""),
|
||||
}
|
||||
else:
|
||||
result = r.json().get("choices", [{}])[0].get("message", {}).get("content", "").strip()
|
||||
else:
|
||||
result = {
|
||||
"error_code": r.status_code,
|
||||
"error_msg": r.reason,
|
||||
}
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
Reference in New Issue
Block a user