Skip to content

Commit f8ad220

Browse files
committed
Add support for ChatGPT translate
1 parent 0ea774d commit f8ad220

File tree

5 files changed

+99
-4
lines changed

5 files changed

+99
-4
lines changed

mathtranslate/chatgpt.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from .config import config
2+
import json
3+
import requests
4+
from urllib import parse
5+
import openai
6+
import sys
7+
import time
8+
9+
class GPTTranslator:
10+
def __init__(self):
11+
self.baseURL=parse.urlparse(config.openai_api_endpoint)._replace(path='/v1') #make sure using ${host}/v1/chat api
12+
self.model = config.openai_model_name
13+
self.key = config.openai_api_key
14+
self.client=openai.OpenAI(api_key=self.key,base_url=self.baseURL.geturl())
15+
16+
17+
def format_prompt(self, text, language_to, language_from):
18+
PROMPT_PROTOTYPE = "As an academic expert with specialized knowledge in various fields, please provide a proficient and precise translation translation from {} to {} of the academic text enclosed in 🔤. It is crucial to maintaining the original phrase or sentence and ensure accuracy while utilizing the appropriate language. Please provide only the translated result without any additional explanation and remove 🔤. The text is as follows: 🔤 {} 🔤 "
19+
#prompt prototype changed from https://github.com/windingwind/zotero-pdf-translate
20+
return PROMPT_PROTOTYPE.format(language_from,language_to,text)
21+
22+
def get_server_errormsg(self,error):
23+
try :
24+
return error.response.json()['error']['message']
25+
except Exception :
26+
return error.message
27+
28+
def call_openai_api(self,prompt):
29+
messages= [{
30+
"role": "user",
31+
"content": prompt
32+
}]
33+
try:
34+
return self.client.chat.completions.create(model=self.model,temperature=0.8,messages=messages)
35+
except openai.RateLimitError as e:
36+
print('API rate limit exceeded, retry after 15s')
37+
time.sleep(15)
38+
self.call_openai_api(prompt)
39+
except openai.InternalServerError as e:
40+
print('Api server failed({}). retry after 30s.'.format(self.get_server_errormsg(e)))
41+
time.sleep(30)
42+
self.call_openai_api(prompt)
43+
except (openai.PermissionDeniedError,openai.AuthenticationError) as e:
44+
print('OpenAI api Authentication failed ({}). please check your api setting by:\n translate_tex --setgpt'.format(self.get_server_errormsg(e)))
45+
sys.exit(-1)
46+
except openai.APIError as e:
47+
print('Api requests failed with error:{}. please check your server status'.format(e.message))
48+
49+
50+
51+
52+
def translate(self, text, language_to, language_from):
53+
while True:
54+
result = self.call_openai_api(self.format_prompt(text, language_to, language_from))
55+
content_translated = result.choices[0].message.content
56+
if '🔤' not in content_translated:
57+
return content_translated
58+
59+

mathtranslate/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ class Config:
1313
default_threads_path = 'DEFAULT_THREADS'
1414
tencent_secret_id_path = 'TENCENT_ID'
1515
tencent_secret_key_path = 'TENCENT_KEY'
16+
openai_model_name_path = 'OPENAI_MODEL'
17+
openai_api_endpoint_path = 'OPENAI_URL'
18+
openai_api_key_path = 'OPENAI_KEY'
1619

1720
default_engine_default = 'google'
1821
default_language_from_default = 'en'
@@ -22,6 +25,9 @@ class Config:
2225
default_threads_default = 0
2326
tencent_secret_id_default = None
2427
tencent_secret_key_default = None
28+
openai_model_name_default = 'gpt-3.5-turbo'
29+
openai_api_endpoint_default = 'https://api.openai.com'
30+
openai_api_key_default = None
2531

2632
math_code = 'XMATHX'
2733
log_file = f'{app_dir}/translate_log'
@@ -62,6 +68,9 @@ def load(self):
6268
self.default_loading_dir = self.read_variable(self.default_loading_dir_path, self.default_loading_dir_default)
6369
self.default_saving_dir = self.read_variable(self.default_saving_dir_path, self.default_saving_dir_default)
6470
self.default_threads = int(self.read_variable(self.default_threads_path, self.default_threads_default))
71+
self.openai_model_name = self.read_variable(self.openai_model_name_path,self.openai_model_name_default)
72+
self.openai_api_endpoint = self.read_variable(self.openai_api_endpoint_path,self.openai_api_endpoint_default)
73+
self.openai_api_key = self.read_variable(self.openai_api_key_path,self.openai_api_key_default)
6574
if not os.path.exists(self.default_loading_dir):
6675
self.default_loading_dir = self.default_loading_dir_default
6776
if not os.path.exists(self.default_saving_dir):

mathtranslate/translate.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ def __init__(self, engine, language_to, language_from):
3030
elif engine == 'tencent':
3131
from mathtranslate.tencent import Translator
3232
translator = Translator()
33+
elif engine== 'gpt':
34+
from mathtranslate.chatgpt import GPTTranslator as Translator
35+
translator = Translator()
3336
else:
34-
assert False, "engine must be google or tencent"
37+
assert False, "engine must be [google,tencent,gpt]"
3538
self.translator = translator
3639
self.language_to = language_to
3740
self.language_from = language_from

mathtranslate/utils.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def check_update(require_updated=True):
8585

8686

8787
def add_arguments(parser):
88-
parser.add_argument("-engine", default=config.default_engine, help=f'translation engine, avaiable options include google and tencent. default is {config.default_engine}')
88+
parser.add_argument("-engine", default=config.default_engine, help=f'translation engine, avaiable options include [google, tencent, gpt]. default is {config.default_engine}')
8989
parser.add_argument("-from", default=config.default_language_from, dest='l_from', help=f'language from, default is {config.default_language_from}')
9090
parser.add_argument("-to", default=config.default_language_to, dest='l_to', help=f'language to, default is {config.default_language_to}')
9191
parser.add_argument("-threads", default=config.default_threads, type=int, help='threads for tencent translation, default is auto')
@@ -96,6 +96,7 @@ def add_arguments(parser):
9696
parser.add_argument("--setdefault", action='store_true', help='set default translation engine and languages')
9797
parser.add_argument("--debug", action='store_true', help='Debug options for developers')
9898
parser.add_argument("--nocache", action='store_true', help='Debug options for developers')
99+
parser.add_argument("--setgpt",action='store_true',help='set baseUrl,apiKey and model name of your GPT service')
99100

100101

101102
def process_options(options):
@@ -110,8 +111,22 @@ def process_options(options):
110111
print('secretKey:', config.tencent_secret_key)
111112
sys.exit()
112113

114+
if options.setgpt:
115+
print('OpenAI api base URL: (leave empty for default {})'.format(config.openai_api_endpoint_default))
116+
config.set_variable(config.openai_api_endpoint_path, config.openai_api_endpoint_default)
117+
print('OpenAI api key (something like sk-xxx...):')
118+
config.set_variable(config.openai_api_key_path, config.openai_api_key_default)
119+
print('ChatGPT model name: (leave empty for default {}'.format(config.openai_model_name_default))
120+
config.set_variable(config.openai_model_name_path,config.openai_model_name_default)
121+
print('saved!')
122+
config.load()
123+
print('Base URL:', config.openai_api_endpoint)
124+
print('Api key:', config.openai_api_key)
125+
print('Model Name:', config.openai_model_name)
126+
sys.exit()
127+
113128
if options.setdefault:
114-
print('Translation engine (google or tencent, default google)')
129+
print('Translation engine [google,tencent,gpt], default google)')
115130
config.set_variable(config.default_engine_path, config.default_engine_default)
116131
print('Translation language from (default en)')
117132
config.set_variable(config.default_language_from_path, config.default_language_from_default)
@@ -148,6 +163,14 @@ def process_options(options):
148163
elif options.threads > 1:
149164
options.threads = 1
150165
print('tencent engine does not support multi-threading, set to 1')
166+
elif options.engine == 'gpt':
167+
hasgptkey = (config.openai_api_key is not None)
168+
if not hasgptkey:
169+
print('Please setup api info for openAI api first by')
170+
print('translate_tex --setgpt')
171+
sys.exit()
172+
options.threads = 1
173+
print('disable mult-threading for chatGPT api')
151174

152175
if options.threads < 0:
153176
print('threads must be a non-zero integer number (>=0 where 0 means auto), set to auto')

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"requests",
2323
"regex",
2424
"tqdm",
25-
"appdata"
25+
"appdata",
26+
"openai"
2627
],
2728
classifiers=[
2829
"Programming Language :: Python :: 3",

0 commit comments

Comments
 (0)