diff --git a/requirements.txt b/requirements.txt index c5e81f2f..6cf0b473 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,6 +20,8 @@ hf-xet==1.1.2 setuptools==80.8.0 langchain-openai==0.3.32 langchain-google-genai==2.1.10 +google-genai==1.45.0 +grpcio==1.67.1 SudachiPy==0.6.10 SudachiDict-core==20250825 SudachiDict-full==20250825 diff --git a/requirements_cuda.txt b/requirements_cuda.txt index 2e41d2a4..b411a604 100644 --- a/requirements_cuda.txt +++ b/requirements_cuda.txt @@ -21,6 +21,8 @@ hf-xet==1.1.2 setuptools==80.8.0 langchain-openai==0.3.32 langchain-google-genai==2.1.10 +google-genai==1.45.0 +grpcio==1.67.1 SudachiPy==0.6.10 SudachiDict-core==20250825 SudachiDict-full==20250825 diff --git a/src-python/config.py b/src-python/config.py index 49df48d0..96376397 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -35,16 +35,6 @@ try: except Exception: # pragma: no cover - optional runtime whisper_models = {} # type: ignore -try: - from models.translation.translation_gemini import _MODELS as gemini_models -except Exception: # pragma: no cover - optional runtime - gemini_models = [] # type: ignore - -try: - from models.translation.translation_plamo import _MODELS as plamo_models -except Exception: # pragma: no cover - optional runtime - plamo_models = [] # type: ignore - from utils import errorLogging, validateDictStructure, getComputeDeviceList json_serializable_vars = {} @@ -173,14 +163,6 @@ class Config: def SELECTABLE_TRANSLATION_ENGINE_LIST(self): return self._SELECTABLE_TRANSLATION_ENGINE_LIST - @property - def SELECTABLE_PLAMO_MODEL_LIST(self): - return self._SELECTABLE_PLAMO_MODEL_LIST - - @property - def SELECTABLE_GEMINI_MODEL_LIST(self): - return self._SELECTABLE_GEMINI_MODEL_LIST - @property def SELECTABLE_TRANSCRIPTION_ENGINE_LIST(self): return self._SELECTABLE_TRANSCRIPTION_ENGINE_LIST @@ -329,6 +311,33 @@ class Config: if isinstance(value, dict): self._SELECTABLE_TRANSCRIPTION_ENGINE_STATUS = value + @property + def SELECTABLE_PLAMO_MODEL_LIST(self): + return self._SELECTABLE_PLAMO_MODEL_LIST + + @SELECTABLE_PLAMO_MODEL_LIST.setter + def SELECTABLE_PLAMO_MODEL_LIST(self, value): + if isinstance(value, list): + self._SELECTABLE_PLAMO_MODEL_LIST = value + + @property + def SELECTABLE_GEMINI_MODEL_LIST(self): + return self._SELECTABLE_GEMINI_MODEL_LIST + + @SELECTABLE_GEMINI_MODEL_LIST.setter + def SELECTABLE_GEMINI_MODEL_LIST(self, value): + if isinstance(value, list): + self._SELECTABLE_GEMINI_MODEL_LIST = value + + @property + def SELECTABLE_OPENAI_MODEL_LIST(self): + return self._SELECTABLE_OPENAI_MODEL_LIST + + @SELECTABLE_OPENAI_MODEL_LIST.setter + def SELECTABLE_OPENAI_MODEL_LIST(self, value): + if isinstance(value, list): + self._SELECTABLE_OPENAI_MODEL_LIST = value + # Save Json Data ## Main Window @property @@ -936,6 +945,18 @@ class Config: self._GEMINI_MODEL = value self.saveConfig(inspect.currentframe().f_code.co_name, value) + @property + @json_serializable('OPENAI_MODEL') + def OPENAI_MODEL(self): + return self._OPENAI_MODEL + + @OPENAI_MODEL.setter + def OPENAI_MODEL(self, value): + if isinstance(value, str): + if value in self.SELECTABLE_OPENAI_MODEL_LIST: + self._OPENAI_MODEL = value + self.saveConfig(inspect.currentframe().f_code.co_name, value) + @property @json_serializable('AUTO_CLEAR_MESSAGE_BOX') def AUTO_CLEAR_MESSAGE_BOX(self): @@ -1160,8 +1181,9 @@ class Config: self._SELECTABLE_TRANSCRIPTION_ENGINE_LIST = list(transcription_lang[first_key].values())[0].keys() except Exception: self._SELECTABLE_TRANSCRIPTION_ENGINE_LIST = [] - self._SELECTABLE_PLAMO_MODEL_LIST = plamo_models - self._SELECTABLE_GEMINI_MODEL_LIST = gemini_models + self._SELECTABLE_PLAMO_MODEL_LIST = [] + self._SELECTABLE_GEMINI_MODEL_LIST = [] + self._SELECTABLE_OPENAI_MODEL_LIST = [] self._SELECTABLE_UI_LANGUAGE_LIST = ["en", "ja", "ko", "zh-Hant", "zh-Hans"] self._COMPUTE_MODE = "cuda" if torch.cuda.is_available() else "cpu" self._SELECTABLE_COMPUTE_DEVICE_LIST = getComputeDeviceList() @@ -1313,13 +1335,15 @@ class Config: "DeepL_API": None, "Plamo_API": None, "Gemini_API": None, + "OpenAI_API": None, } self._USE_EXCLUDE_WORDS = True self._SELECTED_TRANSLATION_COMPUTE_DEVICE = copy.deepcopy(self.SELECTABLE_COMPUTE_DEVICE_LIST[0]) self._SELECTED_TRANSCRIPTION_COMPUTE_DEVICE = copy.deepcopy(self.SELECTABLE_COMPUTE_DEVICE_LIST[0]) self._CTRANSLATE2_WEIGHT_TYPE = "m2m100_418M-ct2-int8" - self._PLAMO_MODEL = "plamo-2.0-prime" - self._GEMINI_MODEL = "gemini-2.5-flash-lite" + self._PLAMO_MODEL = None + self._GEMINI_MODEL = None + self._OPENAI_MODEL = None self._SELECTED_TRANSLATION_COMPUTE_TYPE = "auto" self._WHISPER_WEIGHT_TYPE = "base" self._SELECTED_TRANSCRIPTION_COMPUTE_TYPE = "auto" diff --git a/src-python/controller.py b/src-python/controller.py index 2c33751d..a6c099a9 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1605,36 +1605,6 @@ class Controller: self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} - def getPlamoModelList(self, *args, **kwargs) -> dict: - return {"status":200, "result": config.SELECTABLE_PLAMO_MODEL_LIST} - - def setPlamoModel(self, data, *args, **kwargs) -> dict: - printLog("Set Plamo Model", data) - try: - data = str(data) - result = model.authenticationTranslatorPlamoAuthKey(auth_key=config.AUTH_KEYS["Plamo_API"], model_name=data) - if result is True: - config.PLAMO_MODEL = data - response = {"status":200, "result":config.PLAMO_MODEL} - else: - response = { - "status":400, - "result":{ - "message":"Plamo model is not valid", - "data": config.PLAMO_MODEL - } - } - except Exception as e: - errorLogging() - response = { - "status":400, - "result":{ - "message":f"Error {e}", - "data": config.PLAMO_MODEL - } - } - return response - def getPlamoAuthKey(self, *args, **kwargs) -> dict: return {"status":200, "result":config.AUTH_KEYS["Plamo_API"]} @@ -1644,13 +1614,19 @@ class Controller: try: data = str(data) if len(data) == 72: - result = model.authenticationTranslatorPlamoAuthKey(auth_key=data, model_name=config.PLAMO_MODEL) + result = model.authenticationTranslatorPlamoAuthKey(auth_key=data) if result is True: key = data auth_keys = config.AUTH_KEYS auth_keys[translator_name] = key config.AUTH_KEYS = auth_keys config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True + config.SELECTABLE_PLAMO_MODEL_LIST = model.getTranslatorPlamoModelList() + # ここにrunが必要 + if config.PLAMO_MODEL not in config.SELECTABLE_PLAMO_MODEL_LIST: + config.PLAMO_MODEL = config.SELECTABLE_PLAMO_MODEL_LIST[0] + # ここにrunが必要 + model.updateTranslatorPlamoClient() self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: @@ -1689,23 +1665,27 @@ class Controller: self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} - def getGeminiModelList(self, *args, **kwargs) -> dict: - return {"status":200, "result": config.SELECTABLE_GEMINI_MODEL_LIST} + def getPlamoModelList(self, *args, **kwargs) -> dict: + return {"status":200, "result": config.SELECTABLE_PLAMO_MODEL_LIST} - def setGeminiModel(self, data, *args, **kwargs) -> dict: - printLog("Set Gemini Model", data) + def getPlamoModel(self, *args, **kwargs) -> dict: + return {"status":200, "result":config.PLAMO_MODEL} + + def setPlamoModel(self, data, *args, **kwargs) -> dict: + printLog("Set Plamo Model", data) try: data = str(data) - result = model.authenticationTranslatorGeminiAuthKey(auth_key=config.AUTH_KEYS["Gemini_API"], model_name=data) + result = model.setTranslatorPlamoModel(model=data) if result is True: - config.GEMINI_MODEL = data - response = {"status":200, "result":config.GEMINI_MODEL} + config.PLAMO_MODEL = data + model.updateTranslatorPlamoClient() + response = {"status":200, "result":config.PLAMO_MODEL} else: response = { "status":400, "result":{ - "message":"Gemini model is not valid", - "data": config.GEMINI_MODEL + "message":"Plamo model is not valid", + "data": config.PLAMO_MODEL } } except Exception as e: @@ -1714,7 +1694,7 @@ class Controller: "status":400, "result":{ "message":f"Error {e}", - "data": config.GEMINI_MODEL + "data": config.PLAMO_MODEL } } return response @@ -1728,13 +1708,19 @@ class Controller: try: data = str(data) if len(data) >= 20: - result = model.authenticationTranslatorGeminiAuthKey(auth_key=data, model_name=config.GEMINI_MODEL) + result = model.authenticationTranslatorGeminiAuthKey(auth_key=data) if result is True: key = data auth_keys = config.AUTH_KEYS auth_keys[translator_name] = key config.AUTH_KEYS = auth_keys config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True + config.SELECTABLE_GEMINI_MODEL_LIST = model.getTranslatorGeminiModelList() + # ここにrunが必要 + if config.GEMINI_MODEL not in config.SELECTABLE_GEMINI_MODEL_LIST: + config.GEMINI_MODEL = config.SELECTABLE_GEMINI_MODEL_LIST[0] + # ここにrunが必要 + model.updateTranslatorGeminiClient() self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: @@ -1773,6 +1759,118 @@ class Controller: self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} + def getGeminiModelList(self, *args, **kwargs) -> dict: + return {"status":200, "result": config.SELECTABLE_GEMINI_MODEL_LIST} + + def getGeminiModel(self, *args, **kwargs) -> dict: + return {"status":200, "result":config.GEMINI_MODEL} + + def setGeminiModel(self, data, *args, **kwargs) -> dict: + printLog("Set Gemini Model", data) + try: + data = str(data) + result = model.setTranslatorGeminiModel(model=data) + if result is True: + config.GEMINI_MODEL = data + model.updateTranslatorGeminiClient() + response = {"status":200, "result":config.GEMINI_MODEL} + else: + response = { + "status":400, + "result":{ + "message":"Gemini model is not valid", + "data": config.GEMINI_MODEL + } + } + except Exception as e: + errorLogging() + response = { + "status":400, + "result":{ + "message":f"Error {e}", + "data": config.GEMINI_MODEL + } + } + return response + + @staticmethod + def getOpenAiAuthKey(*args, **kwargs) -> dict: + return {"status":200, "result":config.AUTH_KEYS["OpenAI_API"]} + + def setOpenAiAuthKey(self, data, *args, **kwargs) -> dict: + printLog("Set OpenAI Auth Key", data) + translator_name = "OpenAI_API" + try: + data = str(data) + if len(data) >= 20: + key = data + auth_keys = config.AUTH_KEYS + auth_keys[translator_name] = key + config.AUTH_KEYS = auth_keys + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True + self.updateTranslationEngineAndEngineList() + response = {"status":200, "result":config.AUTH_KEYS[translator_name]} + else: + response = { + "status":400, + "result":{ + "message":"OpenAI auth key length is not correct", + "data": config.AUTH_KEYS[translator_name] + } + } + except Exception as e: + errorLogging() + response = { + "status":400, + "result":{ + "message":f"Error {e}", + "data": config.AUTH_KEYS[translator_name] + } + } + return response + + def delOpenAiAuthKey(self, *args, **kwargs) -> dict: + translator_name = "OpenAI_API" + auth_keys = config.AUTH_KEYS + auth_keys[translator_name] = None + config.AUTH_KEYS = auth_keys + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False + self.updateTranslationEngineAndEngineList() + return {"status":200, "result":config.AUTH_KEYS[translator_name]} + + def getOpenAiModelList(self, *args, **kwargs) -> dict: + return {"status":200, "result": config.SELECTABLE_OPENAI_MODEL_LIST} + + def getOpenAiModel(self, *args, **kwargs) -> dict: + return {"status":200, "result":config.OPENAI_MODEL} + + def setOpenAiModel(self, data, *args, **kwargs) -> dict: + printLog("Set OpenAI Model", data) + try: + data = str(data) + result = model.setTranslatorOpenAiModel(model=data) + if result is True: + config.OPENAI_MODEL = data + response = {"status":200, "result":config.OPENAI_MODEL} + else: + response = { + "status":400, + "result":{ + "message":"OpenAI model is not valid", + "data": config.OPENAI_MODEL + } + } + except Exception as e: + errorLogging() + response = { + "status":400, + "result":{ + "message":f"Error {e}", + "data": config.OPENAI_MODEL + } + } + return response + @staticmethod def getCtranslate2WeightType(*args, **kwargs) -> dict: return {"status":200, "result":config.CTRANSLATE2_WEIGHT_TYPE} @@ -2651,9 +2749,14 @@ class Controller: printLog("Start check Plamo API Key") config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.AUTH_KEYS[engine] is not None: - if model.authenticationTranslatorPlamoAuthKey(auth_key=config.AUTH_KEYS[engine], model=config.PLAMO_MODEL) is True: + if model.authenticationTranslatorPlamoAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True printLog("Plamo API Key is valid") + config.SELECTABLE_PLAMO_MODEL_LIST = model.getTranslatorPlamoModelList() + if config.PLAMO_MODEL not in config.SELECTABLE_PLAMO_MODEL_LIST: + config.PLAMO_MODEL = config.SELECTABLE_PLAMO_MODEL_LIST[0] + model.setTranslatorPlamoModel(config.PLAMO_MODEL) + model.updateTranslatorPlamoClient() else: # error update Auth key auth_keys = config.AUTH_KEYS @@ -2664,15 +2767,38 @@ class Controller: printLog("Start check Gemini API Key") config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.AUTH_KEYS[engine] is not None: - if model.authenticationTranslatorGeminiAuthKey(auth_key=config.AUTH_KEYS[engine], model=config.GEMINI_MODEL) is True: + if model.authenticationTranslatorGeminiAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True printLog("Gemini API Key is valid") + config.SELECTABLE_GEMINI_MODEL_LIST = model.getTranslatorGeminiModelList() + if config.GEMINI_MODEL not in config.SELECTABLE_GEMINI_MODEL_LIST: + config.GEMINI_MODEL = config.SELECTABLE_GEMINI_MODEL_LIST[0] + model.setTranslatorGeminiModel(config.GEMINI_MODEL) + model.updateTranslatorGeminiClient() else: # error update Auth key auth_keys = config.AUTH_KEYS auth_keys[engine] = None config.AUTH_KEYS = auth_keys printLog("Gemini API Key is invalid") + case "OpenAI_API": + printLog("Start check OpenAI API Key") + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False + if config.AUTH_KEYS[engine] is not None: + if model.authenticationTranslatorOpenAIAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("OpenAI API Key is valid") + config.SELECTABLE_OPENAI_MODEL_LIST = model.getTranslatorOpenAIModelList() + if config.OPENAI_MODEL not in config.SELECTABLE_OPENAI_MODEL_LIST: + config.OPENAI_MODEL = config.SELECTABLE_OPENAI_MODEL_LIST[0] + model.setTranslatorOpenAiModel(config.OPENAI_MODEL) + model.updateTranslatorOpenAIClient() + else: + # error update Auth key + auth_keys = config.AUTH_KEYS + auth_keys[engine] = None + config.AUTH_KEYS = auth_keys + printLog("OpenAI API Key is invalid") case _: if connected_network is True: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True diff --git a/src-python/mainloop.py b/src-python/mainloop.py index 51215eb9..d8550687 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -175,17 +175,26 @@ mapping = { "/delete/data/deepl_auth_key": {"status": False, "variable":controller.delDeeplAuthKey}, "/get/data/plamo_model_list": {"status": False, "variable":controller.getPlamoModelList}, + "/get/data/plamo_model": {"status": False, "variable":controller.getPlamoModel}, "/set/data/plamo_model": {"status": False, "variable":controller.setPlamoModel}, "/get/data/plamo_auth_key": {"status": False, "variable":controller.getPlamoAuthKey}, "/set/data/plamo_auth_key": {"status": False, "variable":controller.setPlamoAuthKey}, "/delete/data/plamo_auth_key": {"status": False, "variable":controller.delPlamoAuthKey}, "/get/data/gemini_model_list": {"status": True, "variable":controller.getGeminiModelList}, + "/get/data/gemini_model": {"status": True, "variable":controller.getGeminiModel}, "/set/data/gemini_model": {"status": True, "variable":controller.setGeminiModel}, "/get/data/gemini_auth_key": {"status": True, "variable":controller.getGeminiAuthKey}, "/set/data/gemini_auth_key": {"status": True, "variable":controller.setGeminiAuthKey}, "/delete/data/gemini_auth_key": {"status": True, "variable":controller.delGeminiAuthKey}, + "/get/data/openai_model_list": {"status": True, "variable":controller.getOpenAiModelList}, + "/get/data/openai_model": {"status": True, "variable":controller.getOpenAiModel}, + "/set/data/openai_model": {"status": True, "variable":controller.setOpenAiModel}, + "/get/data/openai_auth_key": {"status": True, "variable":controller.getOpenAiAuthKey}, + "/set/data/openai_auth_key": {"status": True, "variable":controller.setOpenAiAuthKey}, + "/delete/data/openai_auth_key": {"status": True, "variable":controller.delOpenAiAuthKey}, + "/get/data/convert_message_to_romaji": {"status": True, "variable":controller.getConvertMessageToRomaji}, "/set/enable/convert_message_to_romaji": {"status": True, "variable":controller.setEnableConvertMessageToRomaji}, "/set/disable/convert_message_to_romaji": {"status": True, "variable":controller.setDisableConvertMessageToRomaji}, diff --git a/src-python/model.py b/src-python/model.py index 2cbc611c..926ccbb2 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -198,14 +198,57 @@ class Model: result = self.translator.authenticationDeepLAuthKey(auth_key) return result - def authenticationTranslatorPlamoAuthKey(self, auth_key: str, model: str) -> bool: - result = self.translator.authenticationPlamoAuthKey(auth_key, model=model, root_path=config.PATH_LOCAL) + def authenticationTranslatorPlamoAuthKey(self, auth_key: str) -> bool: + result = self.translator.authenticationPlamoAuthKey(auth_key, root_path=config.PATH_LOCAL) return result - def authenticationTranslatorGeminiAuthKey(self, auth_key: str, model: str) -> bool: - result = self.translator.authenticationGeminiAuthKey(auth_key, model=model, root_path=config.PATH_LOCAL) + def getTranslatorPlamoModelList(self) -> list[str]: + self.ensure_initialized() + return self.translator.getPlamoModelList() + + def setTranslatorPlamoModel(self, model: str) -> bool: + self.ensure_initialized() + result = self.translator.setPlamoModel(model=model) return result + def updateTranslatorPlamoClient(self) -> None: + self.ensure_initialized() + self.translator.updatePlamoClient() + + def authenticationTranslatorGeminiAuthKey(self, auth_key: str) -> bool: + result = self.translator.authenticationGeminiAuthKey(auth_key, root_path=config.PATH_LOCAL) + return result + + def getTranslatorGeminiModelList(self) -> list[str]: + self.ensure_initialized() + return self.translator.getGeminiModelList() + + def setTranslatorGeminiModel(self, model: str) -> bool: + self.ensure_initialized() + result = self.translator.setGeminiModel(model=model) + return result + + def updateTranslatorGeminiClient(self) -> None: + self.ensure_initialized() + self.translator.updateGeminiClient() + + def authenticationTranslatorOpenAIAuthKey(self, auth_key: str, base_url: Optional[str] = None) -> bool: + result = self.translator.authenticationOpenAIAuthKey(auth_key, base_url=base_url, root_path=config.PATH_LOCAL) + return result + + def getTranslatorOpenAIModelList(self) -> list[str]: + self.ensure_initialized() + return self.translator.getOpenAIModelList() + + def setTranslatorOpenAiModel(self, model: str) -> bool: + self.ensure_initialized() + result = self.translator.setOpenAIModel(model=model) + return result + + def updateTranslatorOpenAIClient(self) -> None: + self.ensure_initialized() + self.translator.updateOpenAIClient() + def startLogger(self): self.ensure_initialized() os_makedirs(config.PATH_LOGS, exist_ok=True) diff --git a/src-python/models/translation/prompt/translation_gemini.yml b/src-python/models/translation/prompt/translation_gemini.yml index 478b2c5e..8c7b56fc 100644 --- a/src-python/models/translation/prompt/translation_gemini.yml +++ b/src-python/models/translation/prompt/translation_gemini.yml @@ -1,7 +1,10 @@ system_prompt: | - Please translate the following text from {input_lang} to {output_lang}. - Only provide the translated text as the output. - {text} + You are a helpful translation assistant. + Supported languages: + {supported_languages} + + Translate the user provided text from {input_lang} to {output_lang}. + Return ONLY the translated text. Do not add quotes or extra commentary. supported_languages: | Arabic diff --git a/src-python/models/translation/prompt/translation_openai.yml b/src-python/models/translation/prompt/translation_openai.yml new file mode 100644 index 00000000..802d0b1d --- /dev/null +++ b/src-python/models/translation/prompt/translation_openai.yml @@ -0,0 +1,49 @@ +system_prompt: | + You are a helpful translation assistant. + Supported languages: + {supported_languages} + + Translate the user provided text from {input_lang} to {output_lang}. + Return ONLY the translated text. Do not add quotes or extra commentary. + +supported_languages: | + Arabic + Bengali + Bulgarian + Catalan + Chinese Simplified + Chinese Traditional + Croatian + Czech + Danish + Dutch + English + Estonian + Finnish + French + German + Greek + Hebrew + Hindi + Hungarian + Indonesian + Italian + Japanese + Korean + Latvian + Lithuanian + Norwegian + Polish + Portuguese + Romanian + Russian + Serbian + Slovak + Slovenian + Spanish + Swahili + Swedish + Thai + Turkish + Ukrainian + Vietnamese diff --git a/src-python/models/translation/translation_gemini.py b/src-python/models/translation/translation_gemini.py index 9311c2d3..ee019c06 100644 --- a/src-python/models/translation/translation_gemini.py +++ b/src-python/models/translation/translation_gemini.py @@ -1,148 +1,138 @@ import logging +from google import genai from langchain_google_genai import ChatGoogleGenerativeAI -from langchain_core.messages import HumanMessage import yaml from os import path as os_path logger = logging.getLogger("langchain_google_genai") logger.setLevel(logging.ERROR) -_MODELS = [ - "gemini-2.5-pro", - "gemini-2.5-flash", - "gemini-2.5-flash-lite", # default - "gemini-2.0-flash", - "gemini-2.0-flash-lite", - "gemini-1.5-pro", - "gemini-1.5-flash-8b" - "gemini-1.5-flash", +def _authentication_check(api_key: str) -> bool: + """Check if the provided API key is valid by attempting to list models. + """ + try: + client = genai.Client(api_key=api_key) + client.models.list() + return True + except Exception: + return False + +def _get_available_text_models(api_key: str) -> list[str]: + """Extract only Gemini models suitable for translation and chat applications + """ + client = genai.Client(api_key=api_key) + res = client.models.list() + allowed_models = [] + + # 除外対象のキーワード + exclude_keywords = [ + "audio", + "image", + "veo", + "tts", + "robotics", + "computer-use" ] + for model in res: + model_id = model.name + if ("gemini" in model_id.lower() or "gemma" in model_id.lower()) and "generateContent" in model.supported_actions: + if any(x in model_id for x in exclude_keywords): + continue + allowed_models.append(model_id.replace("models/", "")) + allowed_models.sort() + return allowed_models + +def _load_prompt_config(root_path: str = None) -> dict: + """プロンプト設定をYAMLファイルから読み込む""" + prompt_filename = "translation_gemini.yml" + + # PyInstallerでビルドされた場合のパス + if root_path and os_path.exists(os_path.join(root_path, "_internal", "prompt", prompt_filename)): + prompt_path = os_path.join(root_path, "_internal", "prompt", prompt_filename) + # src-pythonフォルダから直接実行している場合のパス + elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename) + # translationフォルダから直接実行している場合のパス + elif os_path.exists(os_path.join(os_path.dirname(__file__), "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "prompt", prompt_filename) + else: + raise FileNotFoundError(f"Prompt file not found: {prompt_filename}") + + with open(prompt_path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) class GeminiClient: - def __init__(self, api_key: str = "", model: str = "gemini-2.5-flash-lite", root_path: str = None): - self.api_key = api_key - self.model = model + def __init__(self, root_path: str = None): + self.api_key = None + self.model = None # プロンプト設定をYAMLファイルから読み込む - prompt_config = self._load_prompt_config(root_path) + prompt_config = _load_prompt_config(root_path) self.supported_languages = prompt_config["supported_languages"] self.prompt_template = prompt_config["system_prompt"] + self.gemini_llm = None + + def getModelList(self) -> list[str]: + return _get_available_text_models(self.api_key) + + def getAuthKey(self) -> str: + return self.api_key + + def setAuthKey(self, api_key: str) -> bool: + result = _authentication_check(api_key) + if result: + self.api_key = api_key + return result + + def getModel(self) -> str: + return self.model + + def setModel(self, model: str) -> bool: + if model in self.getModelList(): + self.model = model + return True + else: + return False + + def updateClient(self) -> None: self.gemini_llm = ChatGoogleGenerativeAI( model=self.model, api_key=self.api_key, ) - def _load_prompt_config(self, root_path: str = None) -> dict: - """プロンプト設定をYAMLファイルから読み込む""" - prompt_filename = "translation_gemini.yml" - - # PyInstallerでビルドされた場合のパス - if root_path and os_path.exists(os_path.join(root_path, "_internal", "prompt", prompt_filename)): - prompt_path = os_path.join(root_path, "_internal", "prompt", prompt_filename) - # src-pythonフォルダから直接実行している場合のパス - elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename)): - prompt_path = os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename) - # translationフォルダから直接実行している場合のパス - elif os_path.exists(os_path.join(os_path.dirname(__file__), "prompt", prompt_filename)): - prompt_path = os_path.join(os_path.dirname(__file__), "prompt", prompt_filename) - else: - raise FileNotFoundError(f"Prompt file not found: {prompt_filename}") - - with open(prompt_path, "r", encoding="utf-8") as f: - return yaml.safe_load(f) - - def getListModels(self) -> list[str]: - return _MODELS - - def getAuthKey(self) -> str: - """現在のAuthKeyを取得する""" - return self.api_key - - def getModel(self) -> str: - """現在のモデルを取得する""" - return self.model - - def setAuthKey(self, api_key: str) -> bool: - """AuthKeyを設定し、成功したかどうかを返す""" - try: - self.api_key = api_key - self.gemini_llm = ChatGoogleGenerativeAI( - model=self.model, - api_key=self.api_key, - ) - return True - except Exception as e: - print(f"Error setting AuthKey: {e}") - return False - - def setModel(self, model: str) -> bool: - """モデルを設定し、成功したかどうかを返す""" - try: - if model in _MODELS: - self.model = model - self.gemini_llm = ChatGoogleGenerativeAI( - model=self.model, - api_key=self.api_key, - ) - return True - else: - print(f"Model {model} is not supported.") - return False - except Exception as e: - print(f"Error setting model: {e}") - return False - def translate(self, text: str, input_lang: str, output_lang: str) -> str: - messages = self.prompt_template.format( - input_lang=input_lang, - output_lang=output_lang, - text=text - ) + system_prompt = self.prompt_template.format( + supported_languages=self.supported_languages, + input_lang=input_lang, + output_lang=output_lang + ) - output = self.gemini_llm.invoke([HumanMessage(content=messages)]) - return output.content + messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": text} + ] - def checkAuthKey(self) -> bool: - try: - self.setModel(self.model) - self.translate("Hello World", input_lang="English", output_lang="Japanese") - return True - except Exception: - return False + resp = self.gemini_llm.invoke(messages) + content = "" + if isinstance(resp.content, str): + content = resp.content + elif isinstance(resp.content, list): + for part in resp.content: + if isinstance(part, str): + content += part + elif isinstance(part, dict) and "content" in part and isinstance(part["content"], str): + content += part["content"] + return content.strip() if __name__ == "__main__": AUTH_KEY = "AUTH_KEY" - text = """ - 毎朝コーヒーを入れるのがささやかな楽しみになってる - """ - input_lang = "Japanese" - output_lang = "English" - - gemini_client = GeminiClient(api_key=AUTH_KEY, model="gemini-2.5-flash-lite") - - print("model list:", gemini_client.getListModels()) - print("AuthKey:", gemini_client.getAuthKey()) - # print("Model:", gemini_client.getModel()) - # print(f"set model: {gemini_client.setModel('gemini-2.5-flash')}") - # print(f"set AuthKey: {gemini_client.setAuthKey(AUTH_KEY)}") - # print(f"check AuthKey: {gemini_client.checkAuthKey()}") - - # try: - # translated_text = gemini_client.translate(text, input_lang, output_lang) - # print(translated_text) - # except Exception: - # print("Invalid API key. Please check your credentials.") - - # 外部ファイルから読み込んだサポート言語を使用 - for lang in gemini_client.supported_languages.split("\n"): - if lang == "": - continue - print (f"Translating to {lang}:") - try: - translated_text = gemini_client.translate(text, input_lang, lang) - print(f"Translated text: {translated_text}") - except Exception as e: - print(f"Error translating to {lang} api limit") - print(f"Error reason: {e}") - break \ No newline at end of file + client = GeminiClient() + client.setAuthKey(AUTH_KEY) + models = client.getModelList() + if models: + print("Available models:", models) + model = input("Select a model: ") + client.setModel(model) + client.updateClient() + print(client.translate("こんにちは世界", "Japanese", "English")) \ No newline at end of file diff --git a/src-python/models/translation/translation_languages.py b/src-python/models/translation/translation_languages.py index 8643c224..ab087e0c 100644 --- a/src-python/models/translation/translation_languages.py +++ b/src-python/models/translation/translation_languages.py @@ -663,4 +663,50 @@ dict_gemini_languages = { "Vietnamese": "Vietnamese", } -translation_lang["Gemini_API"] = {"source":dict_gemini_languages, "target":dict_gemini_languages} \ No newline at end of file +translation_lang["Gemini_API"] = {"source":dict_gemini_languages, "target":dict_gemini_languages} + +# OpenAI API (Chat Completions) - Gemini とほぼ同等の自然言語名を使用 +dict_openai_languages = { + "Arabic": "Arabic", + "Bengali": "Bengali", + "Bulgarian": "Bulgarian", + "Catalan": "Catalan", + "Chinese Simplified": "Simplified Chinese", + "Chinese Traditional": "Traditional Chinese", + "Croatian": "Croatian", + "Czech": "Czech", + "Danish": "Danish", + "Dutch": "Dutch", + "English": "English", + "Estonian": "Estonian", + "Finnish": "Finnish", + "French": "French", + "German": "German", + "Greek": "Greek", + "Hebrew": "Hebrew", + "Hindi": "Hindi", + "Hungarian": "Hungarian", + "Indonesian": "Indonesian", + "Italian": "Italian", + "Japanese": "Japanese", + "Korean": "Korean", + "Latvian": "Latvian", + "Lithuanian": "Lithuanian", + "Norwegian": "Norwegian", + "Polish": "Polish", + "Portuguese": "Portuguese", + "Romanian": "Romanian", + "Russian": "Russian", + "Serbian": "Serbian", + "Slovak": "Slovak", + "Slovenian": "Slovenian", + "Spanish": "Spanish", + "Swahili": "Swahili", + "Swedish": "Swedish", + "Thai": "Thai", + "Turkish": "Turkish", + "Ukrainian": "Ukrainian", + "Vietnamese": "Vietnamese", +} + +translation_lang["OpenAI_API"] = {"source": dict_openai_languages, "target": dict_openai_languages} \ No newline at end of file diff --git a/src-python/models/translation/translation_openai.py b/src-python/models/translation/translation_openai.py new file mode 100644 index 00000000..f461526c --- /dev/null +++ b/src-python/models/translation/translation_openai.py @@ -0,0 +1,148 @@ +from openai import OpenAI +from langchain_openai import ChatOpenAI +from pydantic import SecretStr +import yaml +from os import path as os_path + +def _authentication_check(api_key: str, base_url: str | None = None) -> bool: + """Check if the provided API key is valid by attempting to list models. + """ + try: + client = OpenAI(api_key=api_key, base_url=base_url) + client.models.list() + return True + except Exception: + return False + +def _get_available_text_models(api_key: str, base_url: str | None = None) -> list[str]: + """Extract only GPT models suitable for translation and chat applications (plus those with fine-tuning) + """ + client = OpenAI(api_key=api_key, base_url=base_url) + res = client.models.list() + allowed_models = [] + + for model in res.data: + model_id = model.id + root = getattr(model, "root", "") + + # 除外対象のキーワード + exclude_keywords = [ + "whisper", # 音声認識 + "embedding", # 埋め込み + "image", # 画像生成 + "tts", # 音声合成 + "audio", # 音声系(transcribe, diarize含む) + "search", # 検索補助モデル + "transcribe", # 音声→文字起こし + "diarize", # 話者分離 + "vision" # 画像入力系(旧gpt-4-visionなど) + ] + + # 除外キーワードが含まれているモデルをスキップ + if any(kw in model_id for kw in exclude_keywords): + continue + + # GPTモデルまたはFine-tune GPTモデルのみ対象 + if model_id.startswith("gpt-"): + allowed_models.append(model_id) + elif model_id.startswith("ft:") and root.startswith("gpt-"): + allowed_models.append(model_id) + + allowed_models.sort() + return allowed_models + +def _load_prompt_config(root_path: str = None) -> dict: + prompt_filename = "translation_openai.yml" + # PyInstaller 展開後 + if root_path and os_path.exists(os_path.join(root_path, "_internal", "prompt", prompt_filename)): + prompt_path = os_path.join(root_path, "_internal", "prompt", prompt_filename) + # src-python 直下実行 + elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename) + # translation フォルダ直下実行 + elif os_path.exists(os_path.join(os_path.dirname(__file__), "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "prompt", prompt_filename) + else: + raise FileNotFoundError(f"Prompt file not found: {prompt_filename}") + with open(prompt_path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) + +class OpenAIClient: + """OpenAI Translation simple wrapper. + prompt/translation_openai.yml から system_prompt / supported_languages を読み込む。 + """ + def __init__(self, base_url: str | None = None, root_path: str = None): + self.api_key = None + self.model = None + self.base_url = base_url # None の場合は公式エンドポイント + + prompt_config = _load_prompt_config(root_path) + self.supported_languages = prompt_config["supported_languages"] + self.prompt_template = prompt_config["system_prompt"] + + self.openai_llm = None + + def getModelList(self) -> list[str]: + return _get_available_text_models(self.api_key, self.base_url) if self.api_key else [] + + def getAuthKey(self) -> str: + return self.api_key + + def setAuthKey(self, api_key: str) -> bool: + result = _authentication_check(api_key, self.base_url) + if result: + self.api_key = api_key + return result + + def getModel(self) -> str: + return self.model + + def setModel(self, model: str) -> bool: + if model in self.getModelList(): + self.model = model + return True + else: + return False + + def updateClient(self) -> None: + self.openai_llm = ChatOpenAI( + base_url=self.base_url, + model=self.model, + api_key=SecretStr(self.api_key), + streaming=False, + ) + + def translate(self, text: str, input_lang: str, output_lang: str) -> str: + system_prompt = self.prompt_template.format( + supported_languages=self.supported_languages, + input_lang=input_lang, + output_lang=output_lang, + ) + messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": text}, + ] + + resp = self.openai_llm.invoke(messages) + content = "" + if isinstance(resp.content, str): + content = resp.content + elif isinstance(resp.content, list): + for part in resp.content: + if isinstance(part, str): + content += part + elif isinstance(part, dict) and "content" in part and isinstance(part["content"], str): + content += part["content"] + return content.strip() + +if __name__ == "__main__": + AUTH_KEY = "OPENAI_API_KEY" + client = OpenAIClient() + client.setAuthKey(AUTH_KEY) + models = client.getModelList() + if models: + print("Available models:", models) + model = input("Select a model: ") + client.setModel(model) + client.updateClient() + print(client.translate("こんにちは世界", "Japanese", "English")) \ No newline at end of file diff --git a/src-python/models/translation/translation_plamo.py b/src-python/models/translation/translation_plamo.py index 68192b6f..81cc55bc 100644 --- a/src-python/models/translation/translation_plamo.py +++ b/src-python/models/translation/translation_plamo.py @@ -1,147 +1,126 @@ +from openai import OpenAI from langchain_openai import ChatOpenAI from pydantic import SecretStr import yaml from os import path as os_path -_MODELS = [ - "plamo-2.0-prime" - ] +BASE_URL = "https://api.platform.preferredai.jp/v1" + +def _authentication_check(api_key: str) -> bool: + """Check if the provided API key is valid by attempting to list models. + """ + try: + client = OpenAI(api_key=api_key, base_url=BASE_URL) + client.models.list() + return True + except Exception: + return False + +def _get_available_text_models(api_key: str) -> list[str]: + """Extract all available models from the PLAMO API + """ + client = OpenAI(api_key=api_key, base_url=BASE_URL) + res = client.models.list() + allowed_models = [] + + for model in res.data: + allowed_models.append(model.id) + + allowed_models.sort() + return allowed_models + +def _load_prompt_config(root_path: str = None) -> dict: + """プロンプト設定をYAMLファイルから読み込む""" + prompt_filename = "translation_plamo.yml" + + # PyInstallerでビルドされた場合のパス + if root_path and os_path.exists(os_path.join(root_path, "_internal", "prompt", prompt_filename)): + prompt_path = os_path.join(root_path, "_internal", "prompt", prompt_filename) + # src-pythonフォルダから直接実行している場合のパス + elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename) + # translationフォルダから直接実行している場合のパス + elif os_path.exists(os_path.join(os_path.dirname(__file__), "prompt", prompt_filename)): + prompt_path = os_path.join(os_path.dirname(__file__), "prompt", prompt_filename) + else: + raise FileNotFoundError(f"Prompt file not found: {prompt_filename}") + + with open(prompt_path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) class PlamoClient: - def __init__(self, api_key: str = "", model: str = "plamo-2.0-prime", root_path: str = None): - self.api_key = api_key - self.base_url = "https://api.platform.preferredai.jp/v1" - self.model = model + def __init__(self, root_path: str = None): + self.api_key = None + self.base_url = BASE_URL + self.model = None - # プロンプト設定をYAMLファイルから読み込む - prompt_config = self._load_prompt_config(root_path) + prompt_config = _load_prompt_config(root_path) self.supported_languages = prompt_config["supported_languages"] self.prompt_template = prompt_config["system_prompt"] + self.plamo_llm = None + + def getModelList(self) -> list[str]: + return _get_available_text_models(self.api_key) if self.api_key else [] + + def getAuthKey(self) -> str: + return self.api_key + + def setAuthKey(self, api_key: str) -> bool: + result = _authentication_check(api_key) + if result: + self.api_key = api_key + return result + + def getModel(self) -> str: + return self.model + + def setModel(self, model: str) -> bool: + if model in self.getModelList(): + self.model = model + return True + else: + return False + + def updateClient(self) -> None: self.plamo_llm = ChatOpenAI( base_url=self.base_url, model=self.model, - streaming=True, + streaming=False, api_key=SecretStr(self.api_key), ) - def _load_prompt_config(self, root_path: str = None) -> dict: - """プロンプト設定をYAMLファイルから読み込む""" - prompt_filename = "translation_plamo.yml" - - # PyInstallerでビルドされた場合のパス - if root_path and os_path.exists(os_path.join(root_path, "_internal", "prompt", prompt_filename)): - prompt_path = os_path.join(root_path, "_internal", "prompt", prompt_filename) - # src-pythonフォルダから直接実行している場合のパス - elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename)): - prompt_path = os_path.join(os_path.dirname(__file__), "models", "translation", "prompt", prompt_filename) - # translationフォルダから直接実行している場合のパス - elif os_path.exists(os_path.join(os_path.dirname(__file__), "prompt", prompt_filename)): - prompt_path = os_path.join(os_path.dirname(__file__), "prompt", prompt_filename) - else: - raise FileNotFoundError(f"Prompt file not found: {prompt_filename}") - - with open(prompt_path, "r", encoding="utf-8") as f: - return yaml.safe_load(f) - - def getListModels(self) -> list[str]: - return _MODELS - - def getAuthKey(self) -> str: - """現在のAuthKeyを取得する""" - return self.api_key - - def getModel(self) -> str: - """現在のモデルを取得する""" - return self.model - - def setAuthKey(self, api_key: str) -> bool: - """AuthKeyを設定し、成功したかどうかを返す""" - try: - self.api_key = api_key - self.plamo_llm = ChatOpenAI( - base_url=self.base_url, - model=self.model, - streaming=True, - api_key=SecretStr(self.api_key), - ) - return True - except Exception as e: - return False - - def setModel(self, model: str) -> bool: - """モデルを設定し、成功したかどうかを返す""" - if model not in _MODELS: - return False - - try: - self.model = model - self.plamo_llm = ChatOpenAI( - base_url=self.base_url, - model=self.model, - streaming=True, - api_key=SecretStr(self.api_key), - ) - return True - except Exception as e: - print(f"Error setting model: {e}") - return False - def translate(self, text: str, input_lang: str, output_lang: str) -> str: + system_prompt = self.prompt_template.format( + supported_languages=self.supported_languages, + input_lang=input_lang, + output_lang=output_lang + ) messages = [ - { - "role": "system", - "content": self.prompt_template.format( - supported_languages=self.supported_languages, - input_lang=input_lang, - output_lang=output_lang - ), - }, + {"role": "system", "content": system_prompt}, {"role": "user", "content": text}, ] - output = "" - for chunk in self.plamo_llm.stream(messages): - if isinstance(chunk.content, str): - output += chunk.content - elif isinstance(chunk.content, list): - for item in chunk.content: - if isinstance(item, str): - output += item - elif isinstance(item, dict): - if "content" in item and isinstance(item["content"], str): - output += item["content"] - - return output[:-1] - - def checkAuthKey(self) -> bool: - try: - self.setModel(self.model) - self.translate("Hello World", input_lang="English", output_lang="Japanese") - return True - except Exception as e: - print(f"Error checking AuthKey: {e}") - return False + resp = self.plamo_llm.invoke(messages) + content = "" + if isinstance(resp.content, str): + content = resp.content + elif isinstance(resp.content, list): + for part in resp.content: + if isinstance(part, str): + content += part + elif isinstance(part, dict) and "content" in part and isinstance(part["content"], str): + content += part["content"] + return content.strip() if __name__ == "__main__": - AUTH_KEY = "AUTH_KEY" - text = """ - 毎朝コーヒーを入れるのがささやかな楽しみになってる - """ - input_lang = "Japanese" - output_lang = "English" - - plamo_client = PlamoClient(api_key=AUTH_KEY) - - print("model list:", plamo_client.getListModels()) - print("AuthKey:", plamo_client.getAuthKey()) - print("Model:", plamo_client.getModel()) - print(f"set model: {plamo_client.setModel('plamo-2.0-prime')}") - print(f"set AuthKey: {plamo_client.setAuthKey(AUTH_KEY)}") - print(f"check AuthKey: {plamo_client.checkAuthKey()}") - - try: - translated_text = plamo_client.translate(text, input_lang, output_lang) - print(translated_text) - except Exception: - print("Invalid API key. Please check your credentials.") \ No newline at end of file + AUTH_KEY = "PLAMO_API_KEY" + client = PlamoClient() + client.setAuthKey(AUTH_KEY) + models = client.getModelList() + if models: + print("Available models:", models) + model = input("Select a model: ") + client.setModel(model) + client.updateClient() + print(client.translate("こんにちは世界", "Japanese", "English")) \ No newline at end of file diff --git a/src-python/models/translation/translation_translator.py b/src-python/models/translation/translation_translator.py index aa0195f6..4ea4c0ba 100644 --- a/src-python/models/translation/translation_translator.py +++ b/src-python/models/translation/translation_translator.py @@ -12,6 +12,7 @@ try: from .translation_utils import ctranslate2_weights from .translation_plamo import PlamoClient from .translation_gemini import GeminiClient + from .translation_openai import OpenAIClient except Exception: import sys print(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) @@ -20,6 +21,7 @@ except Exception: from translation_utils import ctranslate2_weights from translation_plamo import PlamoClient from translation_gemini import GeminiClient + from translation_openai import OpenAIClient import ctranslate2 import transformers @@ -44,6 +46,7 @@ class Translator: self.deepl_client: Optional[DeepLClient] = None self.plamo_client: Optional[PlamoClient] = None self.gemini_client: Optional[GeminiClient] = None + self.openai_client: Optional[OpenAIClient] = None self.ctranslate2_translator: Any = None self.ctranslate2_tokenizer: Any = None self.is_loaded_ctranslate2_model: bool = False @@ -66,35 +69,107 @@ class Translator: result = False return result - def authenticationPlamoAuthKey(self, auth_key: str, model: str, root_path: str = None) -> bool: + def authenticationPlamoAuthKey(self, auth_key: str, root_path: str = None) -> bool: """Authenticate Plamo API with the provided key. Returns True on success, False on failure. """ - result = True - try: - self.plamo_client = PlamoClient(auth_key, model=model, root_path=root_path) - self.plamo_client.checkAuthKey() - except Exception: - errorLogging() + self.plamo_client = PlamoClient(root_path=root_path) + if self.plamo_client.setAuthKey(auth_key): + return True + else: self.plamo_client = None - result = False - return result + return False - def authenticationGeminiAuthKey(self, auth_key: str, model: str, root_path: str = None) -> bool: + def getPlamoModelList(self) -> list[str]: + """Get available Plamo models. + + Returns a list of model names, or an empty list on failure. + """ + if self.plamo_client is None: + return [] + return self.plamo_client.getModelList() + + def setPlamoModel(self, model: str) -> bool: + """Change the Plamo model used for translation. + + Returns True on success, False on failure. + """ + if self.plamo_client is None: + return False + return self.plamo_client.setModel(model) + + def updatePlamoClient(self) -> None: + """Update the Plamo client (fetch available models).""" + self.plamo_client.updateClient() + + def authenticationGeminiAuthKey(self, auth_key: str, root_path: str = None) -> bool: """Authenticate Gemini API with the provided key. Returns True on success, False on failure. """ - result = True - try: - self.gemini_client = GeminiClient(auth_key, model=model, root_path=root_path) - self.gemini_client.checkAuthKey() - except Exception: - errorLogging() - self.gemini_client = None - result = False - return result + self.gemini_client = GeminiClient(root_path=root_path) + if self.gemini_client.setAuthKey(auth_key): + return True + else: + return False + + def getGeminiModelList(self) -> list[str]: + """Get available Gemini models. + + Returns a list of model names, or an empty list on failure. + """ + if self.gemini_client is None: + return [] + return self.gemini_client.getModelList() + + def setGeminiModel(self, model: str) -> bool: + """Change the Gemini model used for translation. + + Returns True on success, False on failure. + """ + if self.gemini_client is None: + return False + return self.gemini_client.setModel(model) + + def updateGeminiClient(self) -> None: + """Update the Gemini client (fetch available models).""" + self.gemini_client.updateClient() + + def authenticationOpenAIAuthKey(self, auth_key: str, base_url: str | None = None, root_path: str = None) -> bool: + """Authenticate OpenAI (Chat Completions) API with the provided key. + + base_url を指定することで互換エンドポイント (例: Azure OpenAI 互換, Proxy) にも対応可能。 + Returns True on success, False on failure. + """ + self.openai_client = OpenAIClient(base_url=base_url, root_path=root_path) + if self.openai_client.setAuthKey(auth_key): + return True + else: + self.openai_client = None + return False + + def getOpenAIModelList(self) -> list[str]: + """Get available OpenAI models. + + Returns a list of model names, or an empty list on failure. + """ + if self.openai_client is None: + return [] + return self.openai_client.getModelList() + + def setOpenAIModel(self, model: str) -> bool: + """Change the OpenAI model used for translation. + + Returns True on success, False on failure. + """ + if self.openai_client is None: + return False + return self.openai_client.setModel(model) + + def updateOpenAIClient(self) -> None: + """Update the OpenAI client (fetch available models).""" + self.openai_client.updateClient() def changeCTranslate2Model(self, path: str, model_type: str, device: str = "cpu", device_index: int = 0, compute_type: str = "auto") -> None: """Load a CTranslate2 model from weights. @@ -236,6 +311,15 @@ class Translator: input_lang=source_language, output_lang=target_language, ) + case "OpenAI_API": + if self.openai_client is None: + result = False + else: + result = self.openai_client.translate( + message, + input_lang=source_language, + output_lang=target_language, + ) case "Google": if self.is_enable_translators is True and other_web_Translator is not None: result = other_web_Translator(