Skip to content

Commit

Permalink
支持Claude(haiku)和Groq(llama3.1-70b) 翻譯 (#349)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
fchsieh and Yuukiy authored Aug 17, 2024
1 parent dd56bd1 commit f84dc7e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 1 deletion.
6 changes: 5 additions & 1 deletion core/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
# 是否翻译标题
Expand All @@ -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?
Expand Down
12 changes: 12 additions & 0 deletions core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down
80 changes: 80 additions & 0 deletions web/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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非空,说明发生了错误,返回错误消息
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit f84dc7e

Please sign in to comment.