From e59d52815c80cdc27e6d8d739a681101c8936d4b Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:33:51 +0900 Subject: [PATCH 1/3] [Add] Groq: Implement Groq API integration with authentication, model management, and translation capabilities. --- src-python/config.py | 6 + src-python/controller.py | 97 ++++++++++++ src-python/mainloop.py | 2 + src-python/model.py | 17 ++ .../translation/languages/languages.yml | 4 + .../translation/prompt/translation_groq.yml | 7 + .../models/translation/translation_groq.py | 145 ++++++++++++++++++ .../translation/translation_lmstudio.py | 2 +- .../models/translation/translation_ollama.py | 2 +- .../models/translation/translation_openai.py | 3 +- .../models/translation/translation_plamo.py | 3 +- .../translation/translation_translator.py | 46 ++++++ 12 files changed, 330 insertions(+), 4 deletions(-) create mode 100644 src-python/models/translation/prompt/translation_groq.yml create mode 100644 src-python/models/translation/translation_groq.py diff --git a/src-python/config.py b/src-python/config.py index 08a2b790..f3d05069 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -638,6 +638,7 @@ class Config: SELECTABLE_PLAMO_MODEL_LIST = ManagedProperty('SELECTABLE_PLAMO_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_GEMINI_MODEL_LIST = ManagedProperty('SELECTABLE_GEMINI_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_OPENAI_MODEL_LIST = ManagedProperty('SELECTABLE_OPENAI_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) + SELECTABLE_GROQ_MODEL_LIST = ManagedProperty('SELECTABLE_GROQ_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_LMSTUDIO_MODEL_LIST = ManagedProperty('SELECTABLE_LMSTUDIO_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_OLLAMA_MODEL_LIST = ManagedProperty('SELECTABLE_OLLAMA_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) @@ -735,6 +736,7 @@ class Config: SELECTED_PLAMO_MODEL = ManagedProperty('SELECTED_PLAMO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_PLAMO_MODEL_LIST')) SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GEMINI_MODEL_LIST')) SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OPENAI_MODEL_LIST')) + SELECTED_GROQ_MODEL = ManagedProperty('SELECTED_GROQ_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GROQ_MODEL_LIST')) SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_LMSTUDIO_MODEL_LIST')) SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OLLAMA_MODEL_LIST')) @@ -815,6 +817,7 @@ class Config: self._SELECTABLE_PLAMO_MODEL_LIST = [] self._SELECTABLE_GEMINI_MODEL_LIST = [] self._SELECTABLE_OPENAI_MODEL_LIST = [] + self._SELECTABLE_GROQ_MODEL_LIST = [] self._SELECTABLE_LMSTUDIO_MODEL_LIST = [] self._SELECTABLE_OLLAMA_MODEL_LIST = [] @@ -939,6 +942,7 @@ class Config: "Plamo_API": None, "Gemini_API": None, "OpenAI_API": None, + "Groq_API": None, } self._USE_EXCLUDE_WORDS = True self._SELECTED_TRANSLATION_COMPUTE_DEVICE = copy.deepcopy(self.SELECTABLE_COMPUTE_DEVICE_LIST[0]) @@ -947,6 +951,7 @@ class Config: self._SELECTED_PLAMO_MODEL = None self._SELECTED_GEMINI_MODEL = None self._SELECTED_OPENAI_MODEL = None + self._SELECTED_GROQ_MODEL = None self._LMSTUDIO_URL = "http://127.0.0.1:1234/v1" self._SELECTED_LMSTUDIO_MODEL = None self._SELECTED_OLLAMA_MODEL = None @@ -1051,6 +1056,7 @@ class Config: ('SELECTED_PLAMO_MODEL', 'SELECTABLE_PLAMO_MODEL_LIST'), ('SELECTED_GEMINI_MODEL', 'SELECTABLE_GEMINI_MODEL_LIST'), ('SELECTED_OPENAI_MODEL', 'SELECTABLE_OPENAI_MODEL_LIST'), + ('SELECTED_GROQ_MODEL', 'SELECTABLE_GROQ_MODEL_LIST'), ('SELECTED_LMSTUDIO_MODEL', 'SELECTABLE_LMSTUDIO_MODEL_LIST'), ('SELECTED_OLLAMA_MODEL', 'SELECTABLE_OLLAMA_MODEL_LIST'), ] diff --git a/src-python/controller.py b/src-python/controller.py index 714b6a85..29d0896e 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1900,6 +1900,103 @@ class Controller: } return response + @staticmethod + def getGroqAuthKey(*args, **kwargs) -> dict: + return {"status":200, "result":config.AUTH_KEYS["Groq_API"]} + + def setGroqAuthKey(self, data, *args, **kwargs) -> dict: + printLog("Set Groq Auth Key", data) + translator_name = "Groq_API" + try: + data = str(data) + if data.startswith("gsk-") and len(data) >= 40: + result = model.authenticationTranslatorGroqAuthKey(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_GROQ_MODEL_LIST = model.getTranslatorGroqModelList() + self.run(200, self.run_mapping["selectable_groq_model_list"], config.SELECTABLE_GROQ_MODEL_LIST) + if config.SELECTED_GROQ_MODEL not in config.SELECTABLE_GROQ_MODEL_LIST: + config.SELECTED_GROQ_MODEL = config.SELECTABLE_GROQ_MODEL_LIST[0] + model.setTranslatorGroqModel(model=config.SELECTED_GROQ_MODEL) + self.run(200, self.run_mapping["selected_groq_model"], config.SELECTED_GROQ_MODEL) + model.updateTranslatorGroqClient() + self.updateTranslationEngineAndEngineList() + response = {"status":200, "result":config.AUTH_KEYS[translator_name]} + else: + response = { + "status":400, + "result":{ + "message":"Authentication failure of Groq auth key", + "data": config.AUTH_KEYS[translator_name] + } + } + else: + response = { + "status":400, + "result":{ + "message":"Groq auth key is not valid", + "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 delGroqAuthKey(self, *args, **kwargs) -> dict: + translator_name = "Groq_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 getGroqModelList(self, *args, **kwargs) -> dict: + return {"status":200, "result": config.SELECTABLE_GROQ_MODEL_LIST} + + def getGroqModel(self, *args, **kwargs) -> dict: + return {"status":200, "result":config.SELECTED_GROQ_MODEL} + + def setGroqModel(self, data, *args, **kwargs) -> dict: + printLog("Set Groq Model", data) + try: + data = str(data) + result = model.setTranslatorGroqModel(model=data) + if result is True: + config.SELECTED_GROQ_MODEL = data + model.setTranslatorGroqModel(model=config.SELECTED_GROQ_MODEL) + model.updateTranslatorGroqClient() + response = {"status":200, "result":config.SELECTED_GROQ_MODEL} + else: + response = { + "status":400, + "result":{ + "message":"Groq model is not valid", + "data": config.SELECTED_GROQ_MODEL + } + } + except Exception as e: + errorLogging() + response = { + "status":400, + "result":{ + "message":f"Error {e}", + "data": config.SELECTED_GROQ_MODEL + } + } + return response + def getTranslatorLMStudioConnection(self, *args, **kwargs) -> dict: return {"status":200, "result":model.getTranslatorLMStudioConnected()} diff --git a/src-python/mainloop.py b/src-python/mainloop.py index 20b4bf2f..2da84cbe 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -58,6 +58,8 @@ run_mapping = { "selected_gemini_model":"/run/selected_gemini_model", "selectable_openai_model_list":"/run/selectable_openai_model_list", "selected_openai_model":"/run/selected_openai_model", + "selectable_groq_model_list":"/run/selectable_groq_model_list", + "selected_groq_model":"/run/selected_groq_model", "selectable_lmstudio_model_list":"/run/selectable_lmstudio_model_list", "selected_lmstudio_model":"/run/selected_lmstudio_model", "selectable_ollama_model_list":"/run/selectable_ollama_model_list", diff --git a/src-python/model.py b/src-python/model.py index 0283d7d7..a9cc7b51 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -249,6 +249,23 @@ class Model: self.ensure_initialized() self.translator.updateOpenAIClient() + def authenticationTranslatorGroqAuthKey(self, auth_key: str) -> bool: + result = self.translator.authenticationGroqAuthKey(auth_key, root_path=config.PATH_LOCAL) + return result + + def getTranslatorGroqModelList(self) -> list[str]: + self.ensure_initialized() + return self.translator.getGroqModelList() + + def setTranslatorGroqModel(self, model: str) -> bool: + self.ensure_initialized() + result = self.translator.setGroqModel(model=model) + return result + + def updateTranslatorGroqClient(self) -> None: + self.ensure_initialized() + self.translator.updateGroqClient() + def getTranslatorLMStudioConnected(self) -> bool: self.ensure_initialized() return self.translator.getLMStudioConnected() diff --git a/src-python/models/translation/languages/languages.yml b/src-python/models/translation/languages/languages.yml index cfde921e..66bdc612 100644 --- a/src-python/models/translation/languages/languages.yml +++ b/src-python/models/translation/languages/languages.yml @@ -769,3 +769,7 @@ LMStudio: Ollama: source: *openai_langs target: *openai_langs + +Groq_API: + source: *openai_langs + target: *openai_langs diff --git a/src-python/models/translation/prompt/translation_groq.yml b/src-python/models/translation/prompt/translation_groq.yml new file mode 100644 index 00000000..bc256c3d --- /dev/null +++ b/src-python/models/translation/prompt/translation_groq.yml @@ -0,0 +1,7 @@ +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. diff --git a/src-python/models/translation/translation_groq.py b/src-python/models/translation/translation_groq.py new file mode 100644 index 00000000..04c57dbe --- /dev/null +++ b/src-python/models/translation/translation_groq.py @@ -0,0 +1,145 @@ +from openai import OpenAI +from langchain_openai import ChatOpenAI +from pydantic import SecretStr + +try: + from .translation_languages import translation_lang + from .translation_utils import loadPromptConfig +except Exception: + import sys + from os import path as os_path + sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) + from translation_languages import translation_lang, loadTranslationLanguages + from translation_utils import loadPromptConfig + translation_lang = loadTranslationLanguages(path=".", force=True) + +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="https://api.groq.com/openai/v1", + ) + client.models.list() + return True + except Exception: + return False + +def _get_available_text_models(api_key: str) -> list[str]: + """Extract only Groq models suitable for translation and chat applications. + """ + client = OpenAI( + api_key=api_key, + base_url="https://api.groq.com/openai/v1", + ) + res = client.models.list() + allowed_models = [] + + for model in res.data: + model_id = model.id + + # 除外対象のキーワード + exclude_keywords = [ + "whisper", # 音声認識 + "embedding", # 埋め込み + "image", # 画像生成 + "tts", # 音声合成 + "audio", # 音声系 + "search", # 検索補助モデル + "transcribe", # 音声→文字起こし + "diarize", # 話者分離 + "vision" # 画像入力系 + ] + + # 除外キーワードが含まれているモデルをスキップ + if any(kw in model_id.lower() for kw in exclude_keywords): + continue + + # テキスト処理用モデルのみ対象 + allowed_models.append(model_id) + + allowed_models.sort() + return allowed_models + +class GroqClient: + """Groq API Translation wrapper using OpenAI-compatible endpoint. + + Groq provides a fast LLM inference platform with an OpenAI-compatible API. + The API endpoint: https://api.groq.com/openai/v1 + """ + def __init__(self, root_path: str = None): + self.api_key = None + self.model = None + self.base_url = "https://api.groq.com/openai/v1" + + prompt_config = loadPromptConfig(root_path, "translation_groq.yml") + self.supported_languages = list(translation_lang["Groq_API"]["source"].keys()) + self.prompt_template = prompt_config["system_prompt"] + + self.groq_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.groq_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.groq_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 = "GROQ_API_KEY" + client = GroqClient() + 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")) diff --git a/src-python/models/translation/translation_lmstudio.py b/src-python/models/translation/translation_lmstudio.py index aa8509e8..16ff9545 100644 --- a/src-python/models/translation/translation_lmstudio.py +++ b/src-python/models/translation/translation_lmstudio.py @@ -11,7 +11,7 @@ except Exception: sys.path.append(os_path.dirname(os_path.abspath(__file__))) from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig - loadTranslationLanguages(path=".", force=True) + translation_lang = loadTranslationLanguages(path=".", force=True) def _authentication_check(base_url: str | None = None) -> bool: """Check if the provided API key is valid by attempting to list models. diff --git a/src-python/models/translation/translation_ollama.py b/src-python/models/translation/translation_ollama.py index ae4cc860..543e603a 100644 --- a/src-python/models/translation/translation_ollama.py +++ b/src-python/models/translation/translation_ollama.py @@ -10,7 +10,7 @@ except Exception: sys.path.append(os_path.dirname(os_path.abspath(__file__))) from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig - loadTranslationLanguages(path=".", force=True) + translation_lang = loadTranslationLanguages(path=".", force=True) def _authentication_check(base_url: str | None = None) -> bool: """Check authentication for Ollama API. diff --git a/src-python/models/translation/translation_openai.py b/src-python/models/translation/translation_openai.py index b7115e51..cc09a90e 100644 --- a/src-python/models/translation/translation_openai.py +++ b/src-python/models/translation/translation_openai.py @@ -9,8 +9,9 @@ except Exception: import sys from os import path as os_path sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) - from translation_languages import translation_lang + from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig + translation_lang = loadTranslationLanguages(path=".", force=True) 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. diff --git a/src-python/models/translation/translation_plamo.py b/src-python/models/translation/translation_plamo.py index ea54fd61..68d68754 100644 --- a/src-python/models/translation/translation_plamo.py +++ b/src-python/models/translation/translation_plamo.py @@ -9,8 +9,9 @@ except Exception: import sys from os import path as os_path sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) - from translation_languages import translation_lang + from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig + translation_lang = loadTranslationLanguages(path=".", force=True) BASE_URL = "https://api.platform.preferredai.jp/v1" diff --git a/src-python/models/translation/translation_translator.py b/src-python/models/translation/translation_translator.py index 175a84ba..71a69e64 100644 --- a/src-python/models/translation/translation_translator.py +++ b/src-python/models/translation/translation_translator.py @@ -15,6 +15,7 @@ try: from .translation_openai import OpenAIClient from .translation_lmstudio import LMStudioClient from .translation_ollama import OllamaClient + from .translation_groq import GroqClient except Exception: import sys sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) @@ -25,6 +26,7 @@ except Exception: from translation_openai import OpenAIClient from translation_lmstudio import LMStudioClient from translation_ollama import OllamaClient + from translation_groq import GroqClient import ctranslate2 import transformers @@ -50,6 +52,7 @@ class Translator: self.plamo_client: Optional[PlamoClient] = None self.gemini_client: Optional[GeminiClient] = None self.openai_client: Optional[OpenAIClient] = None + self.groq_client: Optional[GroqClient] = None self.lmstudio_client: LMStudioClient[LMStudioClient] = None self.ollama_client: OllamaClient[OllamaClient] = None self.ctranslate2_translator: Any = None @@ -176,6 +179,40 @@ class Translator: """Update the OpenAI client (fetch available models).""" self.openai_client.updateClient() + def authenticationGroqAuthKey(self, auth_key: str, root_path: str = None) -> bool: + """Authenticate Groq API with the provided key. + + Returns True on success, False on failure. + """ + self.groq_client = GroqClient(root_path=root_path) + if self.groq_client.setAuthKey(auth_key): + return True + else: + self.groq_client = None + return False + + def getGroqModelList(self) -> list[str]: + """Get available Groq models. + + Returns a list of model names, or an empty list on failure. + """ + if self.groq_client is None: + return [] + return self.groq_client.getModelList() + + def setGroqModel(self, model: str) -> bool: + """Change the Groq model used for translation. + + Returns True on success, False on failure. + """ + if self.groq_client is None: + return False + return self.groq_client.setModel(model) + + def updateGroqClient(self) -> None: + """Update the Groq client (fetch available models).""" + self.groq_client.updateClient() + def getLMStudioConnected(self) -> bool: """Get LM Studio connection status. @@ -409,6 +446,15 @@ class Translator: input_lang=source_language, output_lang=target_language, ) + case "Groq_API": + if self.groq_client is None: + result = False + else: + result = self.groq_client.translate( + message, + input_lang=source_language, + output_lang=target_language, + ) case "LMStudio": if self.lmstudio_client is None: result = False From 8b853f4c20122e17f5be6ad6c384b432cf943a1a Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:48:56 +0900 Subject: [PATCH 2/3] [Add] OpenRouter: Integrate OpenRouter API for authentication, model management, and translation capabilities. --- src-python/config.py | 6 + src-python/controller.py | 97 ++++++++++++ src-python/mainloop.py | 2 + src-python/model.py | 17 ++ .../translation/languages/languages.yml | 4 + .../prompt/translation_openrouter.yml | 7 + .../translation/translation_openrouter.py | 145 ++++++++++++++++++ .../translation/translation_translator.py | 46 ++++++ 8 files changed, 324 insertions(+) create mode 100644 src-python/models/translation/prompt/translation_openrouter.yml create mode 100644 src-python/models/translation/translation_openrouter.py diff --git a/src-python/config.py b/src-python/config.py index f3d05069..ad712aa4 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -639,6 +639,7 @@ class Config: SELECTABLE_GEMINI_MODEL_LIST = ManagedProperty('SELECTABLE_GEMINI_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_OPENAI_MODEL_LIST = ManagedProperty('SELECTABLE_OPENAI_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_GROQ_MODEL_LIST = ManagedProperty('SELECTABLE_GROQ_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) + SELECTABLE_OPENROUTER_MODEL_LIST = ManagedProperty('SELECTABLE_OPENROUTER_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_LMSTUDIO_MODEL_LIST = ManagedProperty('SELECTABLE_LMSTUDIO_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) SELECTABLE_OLLAMA_MODEL_LIST = ManagedProperty('SELECTABLE_OLLAMA_MODEL_LIST', type_=list, serialize=False, mutable_tracking=True) @@ -737,6 +738,7 @@ class Config: SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GEMINI_MODEL_LIST')) SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OPENAI_MODEL_LIST')) SELECTED_GROQ_MODEL = ManagedProperty('SELECTED_GROQ_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GROQ_MODEL_LIST')) + SELECTED_OPENROUTER_MODEL = ManagedProperty('SELECTED_OPENROUTER_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OPENROUTER_MODEL_LIST')) SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_LMSTUDIO_MODEL_LIST')) SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OLLAMA_MODEL_LIST')) @@ -818,6 +820,7 @@ class Config: self._SELECTABLE_GEMINI_MODEL_LIST = [] self._SELECTABLE_OPENAI_MODEL_LIST = [] self._SELECTABLE_GROQ_MODEL_LIST = [] + self._SELECTABLE_OPENROUTER_MODEL_LIST = [] self._SELECTABLE_LMSTUDIO_MODEL_LIST = [] self._SELECTABLE_OLLAMA_MODEL_LIST = [] @@ -943,6 +946,7 @@ class Config: "Gemini_API": None, "OpenAI_API": None, "Groq_API": None, + "OpenRouter_API": None, } self._USE_EXCLUDE_WORDS = True self._SELECTED_TRANSLATION_COMPUTE_DEVICE = copy.deepcopy(self.SELECTABLE_COMPUTE_DEVICE_LIST[0]) @@ -952,6 +956,7 @@ class Config: self._SELECTED_GEMINI_MODEL = None self._SELECTED_OPENAI_MODEL = None self._SELECTED_GROQ_MODEL = None + self._SELECTED_OPENROUTER_MODEL = None self._LMSTUDIO_URL = "http://127.0.0.1:1234/v1" self._SELECTED_LMSTUDIO_MODEL = None self._SELECTED_OLLAMA_MODEL = None @@ -1057,6 +1062,7 @@ class Config: ('SELECTED_GEMINI_MODEL', 'SELECTABLE_GEMINI_MODEL_LIST'), ('SELECTED_OPENAI_MODEL', 'SELECTABLE_OPENAI_MODEL_LIST'), ('SELECTED_GROQ_MODEL', 'SELECTABLE_GROQ_MODEL_LIST'), + ('SELECTED_OPENROUTER_MODEL', 'SELECTABLE_OPENROUTER_MODEL_LIST'), ('SELECTED_LMSTUDIO_MODEL', 'SELECTABLE_LMSTUDIO_MODEL_LIST'), ('SELECTED_OLLAMA_MODEL', 'SELECTABLE_OLLAMA_MODEL_LIST'), ] diff --git a/src-python/controller.py b/src-python/controller.py index 29d0896e..42a2266c 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1997,6 +1997,103 @@ class Controller: } return response + @staticmethod + def getOpenRouterAuthKey(*args, **kwargs) -> dict: + return {"status":200, "result":config.AUTH_KEYS["OpenRouter_API"]} + + def setOpenRouterAuthKey(self, data, *args, **kwargs) -> dict: + printLog("Set OpenRouter Auth Key", data) + translator_name = "OpenRouter_API" + try: + data = str(data) + if len(data) >= 20: # OpenRouter API key basic validation + result = model.authenticationTranslatorOpenRouterAuthKey(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_OPENROUTER_MODEL_LIST = model.getTranslatorOpenRouterModelList() + self.run(200, self.run_mapping["selectable_openrouter_model_list"], config.SELECTABLE_OPENROUTER_MODEL_LIST) + if config.SELECTED_OPENROUTER_MODEL not in config.SELECTABLE_OPENROUTER_MODEL_LIST: + config.SELECTED_OPENROUTER_MODEL = config.SELECTABLE_OPENROUTER_MODEL_LIST[0] + model.setTranslatorOpenRouterModel(model=config.SELECTED_OPENROUTER_MODEL) + self.run(200, self.run_mapping["selected_openrouter_model"], config.SELECTED_OPENROUTER_MODEL) + model.updateTranslatorOpenRouterClient() + self.updateTranslationEngineAndEngineList() + response = {"status":200, "result":config.AUTH_KEYS[translator_name]} + else: + response = { + "status":400, + "result":{ + "message":"Authentication failure of OpenRouter auth key", + "data": config.AUTH_KEYS[translator_name] + } + } + else: + response = { + "status":400, + "result":{ + "message":"OpenRouter auth key is not valid", + "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 delOpenRouterAuthKey(self, *args, **kwargs) -> dict: + translator_name = "OpenRouter_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 getOpenRouterModelList(self, *args, **kwargs) -> dict: + return {"status":200, "result": config.SELECTABLE_OPENROUTER_MODEL_LIST} + + def getOpenRouterModel(self, *args, **kwargs) -> dict: + return {"status":200, "result":config.SELECTED_OPENROUTER_MODEL} + + def setOpenRouterModel(self, data, *args, **kwargs) -> dict: + printLog("Set OpenRouter Model", data) + try: + data = str(data) + result = model.setTranslatorOpenRouterModel(model=data) + if result is True: + config.SELECTED_OPENROUTER_MODEL = data + model.setTranslatorOpenRouterModel(model=config.SELECTED_OPENROUTER_MODEL) + model.updateTranslatorOpenRouterClient() + response = {"status":200, "result":config.SELECTED_OPENROUTER_MODEL} + else: + response = { + "status":400, + "result":{ + "message":"OpenRouter model is not valid", + "data": config.SELECTED_OPENROUTER_MODEL + } + } + except Exception as e: + errorLogging() + response = { + "status":400, + "result":{ + "message":f"Error {e}", + "data": config.SELECTED_OPENROUTER_MODEL + } + } + return response + def getTranslatorLMStudioConnection(self, *args, **kwargs) -> dict: return {"status":200, "result":model.getTranslatorLMStudioConnected()} diff --git a/src-python/mainloop.py b/src-python/mainloop.py index 2da84cbe..d3e158bf 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -60,6 +60,8 @@ run_mapping = { "selected_openai_model":"/run/selected_openai_model", "selectable_groq_model_list":"/run/selectable_groq_model_list", "selected_groq_model":"/run/selected_groq_model", + "selectable_openrouter_model_list":"/run/selectable_openrouter_model_list", + "selected_openrouter_model":"/run/selected_openrouter_model", "selectable_lmstudio_model_list":"/run/selectable_lmstudio_model_list", "selected_lmstudio_model":"/run/selected_lmstudio_model", "selectable_ollama_model_list":"/run/selectable_ollama_model_list", diff --git a/src-python/model.py b/src-python/model.py index a9cc7b51..81733b4e 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -266,6 +266,23 @@ class Model: self.ensure_initialized() self.translator.updateGroqClient() + def authenticationTranslatorOpenRouterAuthKey(self, auth_key: str) -> bool: + result = self.translator.authenticationOpenRouterAuthKey(auth_key, root_path=config.PATH_LOCAL) + return result + + def getTranslatorOpenRouterModelList(self) -> list[str]: + self.ensure_initialized() + return self.translator.getOpenRouterModelList() + + def setTranslatorOpenRouterModel(self, model: str) -> bool: + self.ensure_initialized() + result = self.translator.setOpenRouterModel(model=model) + return result + + def updateTranslatorOpenRouterClient(self) -> None: + self.ensure_initialized() + self.translator.updateOpenRouterClient() + def getTranslatorLMStudioConnected(self) -> bool: self.ensure_initialized() return self.translator.getLMStudioConnected() diff --git a/src-python/models/translation/languages/languages.yml b/src-python/models/translation/languages/languages.yml index 66bdc612..04772215 100644 --- a/src-python/models/translation/languages/languages.yml +++ b/src-python/models/translation/languages/languages.yml @@ -773,3 +773,7 @@ Ollama: Groq_API: source: *openai_langs target: *openai_langs + +OpenRouter_API: + source: *openai_langs + target: *openai_langs diff --git a/src-python/models/translation/prompt/translation_openrouter.yml b/src-python/models/translation/prompt/translation_openrouter.yml new file mode 100644 index 00000000..bc256c3d --- /dev/null +++ b/src-python/models/translation/prompt/translation_openrouter.yml @@ -0,0 +1,7 @@ +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. diff --git a/src-python/models/translation/translation_openrouter.py b/src-python/models/translation/translation_openrouter.py new file mode 100644 index 00000000..cec7aee3 --- /dev/null +++ b/src-python/models/translation/translation_openrouter.py @@ -0,0 +1,145 @@ +from openai import OpenAI +from langchain_openai import ChatOpenAI +from pydantic import SecretStr + +try: + from .translation_languages import translation_lang + from .translation_utils import loadPromptConfig +except Exception: + import sys + from os import path as os_path + sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) + from translation_languages import translation_lang, loadTranslationLanguages + from translation_utils import loadPromptConfig + translation_lang = loadTranslationLanguages(path=".", force=True) + +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="https://openrouter.ai/api/v1", + ) + client.models.list() + return True + except Exception: + return False + +def _get_available_text_models(api_key: str) -> list[str]: + """Extract only OpenRouter models suitable for translation and chat applications. + """ + client = OpenAI( + api_key=api_key, + base_url="https://openrouter.ai/api/v1", + ) + res = client.models.list() + allowed_models = [] + + for model in res.data: + model_id = model.id + + # 除外対象のキーワード + exclude_keywords = [ + "whisper", # 音声認識 + "embedding", # 埋め込み + "image", # 画像生成 + "tts", # 音声合成 + "audio", # 音声系 + "search", # 検索補助モデル + "transcribe", # 音声→文字起こし + "diarize", # 話者分離 + "vision" # 画像入力系 + ] + + # 除外キーワードが含まれているモデルをスキップ + if any(kw in model_id.lower() for kw in exclude_keywords): + continue + + # テキスト処理用モデルのみ対象 + allowed_models.append(model_id) + + allowed_models.sort() + return allowed_models + +class OpenRouterClient: + """OpenRouter API Translation wrapper using OpenAI-compatible endpoint. + + OpenRouter provides access to various LLM models via a unified API. + The API endpoint: https://openrouter.ai/api/v1 + """ + def __init__(self, root_path: str = None): + self.api_key = None + self.model = None + self.base_url = "https://openrouter.ai/api/v1" + + prompt_config = loadPromptConfig(root_path, "translation_openrouter.yml") + self.supported_languages = list(translation_lang["OpenRouter_API"]["source"].keys()) + self.prompt_template = prompt_config["system_prompt"] + + self.openrouter_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.openrouter_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.openrouter_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 = input("OPENROUTER_API_KEY: ") + client = OpenRouterClient() + 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")) diff --git a/src-python/models/translation/translation_translator.py b/src-python/models/translation/translation_translator.py index 71a69e64..7efc58d6 100644 --- a/src-python/models/translation/translation_translator.py +++ b/src-python/models/translation/translation_translator.py @@ -16,6 +16,7 @@ try: from .translation_lmstudio import LMStudioClient from .translation_ollama import OllamaClient from .translation_groq import GroqClient + from .translation_openrouter import OpenRouterClient except Exception: import sys sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) @@ -27,6 +28,7 @@ except Exception: from translation_lmstudio import LMStudioClient from translation_ollama import OllamaClient from translation_groq import GroqClient + from translation_openrouter import OpenRouterClient import ctranslate2 import transformers @@ -53,6 +55,7 @@ class Translator: self.gemini_client: Optional[GeminiClient] = None self.openai_client: Optional[OpenAIClient] = None self.groq_client: Optional[GroqClient] = None + self.openrouter_client: Optional[OpenRouterClient] = None self.lmstudio_client: LMStudioClient[LMStudioClient] = None self.ollama_client: OllamaClient[OllamaClient] = None self.ctranslate2_translator: Any = None @@ -213,6 +216,40 @@ class Translator: """Update the Groq client (fetch available models).""" self.groq_client.updateClient() + def authenticationOpenRouterAuthKey(self, auth_key: str, root_path: str = None) -> bool: + """Authenticate OpenRouter API with the provided key. + + Returns True on success, False on failure. + """ + self.openrouter_client = OpenRouterClient(root_path=root_path) + if self.openrouter_client.setAuthKey(auth_key): + return True + else: + self.openrouter_client = None + return False + + def getOpenRouterModelList(self) -> list[str]: + """Get available OpenRouter models. + + Returns a list of model names, or an empty list on failure. + """ + if self.openrouter_client is None: + return [] + return self.openrouter_client.getModelList() + + def setOpenRouterModel(self, model: str) -> bool: + """Change the OpenRouter model used for translation. + + Returns True on success, False on failure. + """ + if self.openrouter_client is None: + return False + return self.openrouter_client.setModel(model) + + def updateOpenRouterClient(self) -> None: + """Update the OpenRouter client (fetch available models).""" + self.openrouter_client.updateClient() + def getLMStudioConnected(self) -> bool: """Get LM Studio connection status. @@ -455,6 +492,15 @@ class Translator: input_lang=source_language, output_lang=target_language, ) + case "OpenRouter_API": + if self.openrouter_client is None: + result = False + else: + result = self.openrouter_client.translate( + message, + input_lang=source_language, + output_lang=target_language, + ) case "LMStudio": if self.lmstudio_client is None: result = False From f327b8e4fa2d366ff821ceedba57d0f45c0ac116 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:28:38 +0900 Subject: [PATCH 3/3] [Add] Controller: Update model selection logic for Groq and OpenRouter, and add corresponding API endpoints in mainloop. --- .gitignore | 3 +- src-python/controller.py | 142 ++++++++++++++++++++++++++++++++++++++- src-python/mainloop.py | 14 ++++ 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6ff13fd6..81cf7dc1 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ dist-ssr # Customize /build -error.txt \ No newline at end of file +error.txt +config.json \ No newline at end of file diff --git a/src-python/controller.py b/src-python/controller.py index 42a2266c..ff91ea6e 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1637,6 +1637,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: + config.SELECTABLE_PLAMO_MODEL_LIST = [] + config.SELECTED_PLAMO_MODEL = None + self.run(200, self.run_mapping["selectable_plamo_model_list"], config.SELECTABLE_PLAMO_MODEL_LIST) + self.run(200, self.run_mapping["selected_plamo_model"], config.SELECTED_PLAMO_MODEL) response = { "status":400, "result":{ @@ -1645,6 +1649,10 @@ class Controller: } } else: + config.SELECTABLE_PLAMO_MODEL_LIST = [] + config.SELECTED_PLAMO_MODEL = None + self.run(200, self.run_mapping["selectable_plamo_model_list"], config.SELECTABLE_PLAMO_MODEL_LIST) + self.run(200, self.run_mapping["selected_plamo_model"], config.SELECTED_PLAMO_MODEL) response = { "status":400, "result":{ @@ -1654,6 +1662,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_PLAMO_MODEL_LIST = [] + config.SELECTED_PLAMO_MODEL = None + self.run(200, self.run_mapping["selectable_plamo_model_list"], config.SELECTABLE_PLAMO_MODEL_LIST) + self.run(200, self.run_mapping["selected_plamo_model"], config.SELECTED_PLAMO_MODEL) response = { "status":400, "result":{ @@ -1668,6 +1680,10 @@ class Controller: auth_keys = config.AUTH_KEYS auth_keys[translator_name] = None config.AUTH_KEYS = auth_keys + config.SELECTABLE_PLAMO_MODEL_LIST = [] + config.SELECTED_PLAMO_MODEL = None + self.run(200, self.run_mapping["selectable_plamo_model_list"], config.SELECTABLE_PLAMO_MODEL_LIST) + self.run(200, self.run_mapping["selected_plamo_model"], config.SELECTED_PLAMO_MODEL) config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} @@ -1733,6 +1749,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: + config.SELECTABLE_GEMINI_MODEL_LIST = [] + config.SELECTED_GEMINI_MODEL = None + self.run(200, self.run_mapping["selectable_gemini_model_list"], config.SELECTABLE_GEMINI_MODEL_LIST) + self.run(200, self.run_mapping["selected_gemini_model"], config.SELECTED_GEMINI_MODEL) response = { "status":400, "result":{ @@ -1741,6 +1761,10 @@ class Controller: } } else: + config.SELECTABLE_GEMINI_MODEL_LIST = [] + config.SELECTED_GEMINI_MODEL = None + self.run(200, self.run_mapping["selectable_gemini_model_list"], config.SELECTABLE_GEMINI_MODEL_LIST) + self.run(200, self.run_mapping["selected_gemini_model"], config.SELECTED_GEMINI_MODEL) response = { "status":400, "result":{ @@ -1750,6 +1774,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_GEMINI_MODEL_LIST = [] + config.SELECTED_GEMINI_MODEL = None + self.run(200, self.run_mapping["selectable_gemini_model_list"], config.SELECTABLE_GEMINI_MODEL_LIST) + self.run(200, self.run_mapping["selected_gemini_model"], config.SELECTED_GEMINI_MODEL) response = { "status":400, "result":{ @@ -1764,6 +1792,10 @@ class Controller: auth_keys = config.AUTH_KEYS auth_keys[translator_name] = None config.AUTH_KEYS = auth_keys + config.SELECTABLE_GEMINI_MODEL_LIST = [] + config.SELECTED_GEMINI_MODEL = None + self.run(200, self.run_mapping["selectable_gemini_model_list"], config.SELECTABLE_GEMINI_MODEL_LIST) + self.run(200, self.run_mapping["selected_gemini_model"], config.SELECTED_GEMINI_MODEL) config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} @@ -1830,6 +1862,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: + config.SELECTABLE_OPENAI_MODEL_LIST = [] + config.SELECTED_OPENAI_MODEL = None + self.run(200, self.run_mapping["selectable_openai_model_list"], config.SELECTABLE_OPENAI_MODEL_LIST) + self.run(200, self.run_mapping["selected_openai_model"], config.SELECTED_OPENAI_MODEL) response = { "status":400, "result":{ @@ -1838,6 +1874,10 @@ class Controller: } } else: + config.SELECTABLE_OPENAI_MODEL_LIST = [] + config.SELECTED_OPENAI_MODEL = None + self.run(200, self.run_mapping["selectable_openai_model_list"], config.SELECTABLE_OPENAI_MODEL_LIST) + self.run(200, self.run_mapping["selected_openai_model"], config.SELECTED_OPENAI_MODEL) response = { "status":400, "result":{ @@ -1847,6 +1887,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_OPENAI_MODEL_LIST = [] + config.SELECTED_OPENAI_MODEL = None + self.run(200, self.run_mapping["selectable_openai_model_list"], config.SELECTABLE_OPENAI_MODEL_LIST) + self.run(200, self.run_mapping["selected_openai_model"], config.SELECTED_OPENAI_MODEL) response = { "status":400, "result":{ @@ -1861,6 +1905,10 @@ class Controller: auth_keys = config.AUTH_KEYS auth_keys[translator_name] = None config.AUTH_KEYS = auth_keys + config.SELECTABLE_OPENAI_MODEL_LIST = [] + config.SELECTED_OPENAI_MODEL = None + self.run(200, self.run_mapping["selectable_openai_model_list"], config.SELECTABLE_OPENAI_MODEL_LIST) + self.run(200, self.run_mapping["selected_openai_model"], config.SELECTED_OPENAI_MODEL) config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} @@ -1909,7 +1957,7 @@ class Controller: translator_name = "Groq_API" try: data = str(data) - if data.startswith("gsk-") and len(data) >= 40: + if data.startswith("gsk") and len(data) >= 40: result = model.authenticationTranslatorGroqAuthKey(auth_key=data) if result is True: key = data @@ -1927,6 +1975,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: + config.SELECTABLE_GROQ_MODEL_LIST = [] + config.SELECTED_GROQ_MODEL = None + self.run(200, self.run_mapping["selectable_groq_model_list"], config.SELECTABLE_GROQ_MODEL_LIST) + self.run(200, self.run_mapping["selected_groq_model"], config.SELECTED_GROQ_MODEL) response = { "status":400, "result":{ @@ -1935,6 +1987,10 @@ class Controller: } } else: + config.SELECTABLE_GROQ_MODEL_LIST = [] + config.SELECTED_GROQ_MODEL = None + self.run(200, self.run_mapping["selectable_groq_model_list"], config.SELECTABLE_GROQ_MODEL_LIST) + self.run(200, self.run_mapping["selected_groq_model"], config.SELECTED_GROQ_MODEL) response = { "status":400, "result":{ @@ -1944,6 +2000,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_GROQ_MODEL_LIST = [] + config.SELECTED_GROQ_MODEL = None + self.run(200, self.run_mapping["selectable_groq_model_list"], config.SELECTABLE_GROQ_MODEL_LIST) + self.run(200, self.run_mapping["selected_groq_model"], config.SELECTED_GROQ_MODEL) response = { "status":400, "result":{ @@ -1958,6 +2018,10 @@ class Controller: auth_keys = config.AUTH_KEYS auth_keys[translator_name] = None config.AUTH_KEYS = auth_keys + config.SELECTABLE_GROQ_MODEL_LIST = [] + config.SELECTED_GROQ_MODEL = None + self.run(200, self.run_mapping["selectable_groq_model_list"], config.SELECTABLE_GROQ_MODEL_LIST) + self.run(200, self.run_mapping["selected_groq_model"], config.SELECTED_GROQ_MODEL) config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} @@ -2024,6 +2088,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.AUTH_KEYS[translator_name]} else: + config.SELECTABLE_OPENROUTER_MODEL_LIST = [] + config.SELECTED_OPENROUTER_MODEL = None + self.run(200, self.run_mapping["selectable_openrouter_model_list"], config.SELECTABLE_OPENROUTER_MODEL_LIST) + self.run(200, self.run_mapping["selected_openrouter_model"], config.SELECTED_OPENROUTER_MODEL) response = { "status":400, "result":{ @@ -2032,6 +2100,10 @@ class Controller: } } else: + config.SELECTABLE_OPENROUTER_MODEL_LIST = [] + config.SELECTED_OPENROUTER_MODEL = None + self.run(200, self.run_mapping["selectable_openrouter_model_list"], config.SELECTABLE_OPENROUTER_MODEL_LIST) + self.run(200, self.run_mapping["selected_openrouter_model"], config.SELECTED_OPENROUTER_MODEL) response = { "status":400, "result":{ @@ -2041,6 +2113,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_OPENROUTER_MODEL_LIST = [] + config.SELECTED_OPENROUTER_MODEL = None + self.run(200, self.run_mapping["selectable_openrouter_model_list"], config.SELECTABLE_OPENROUTER_MODEL_LIST) + self.run(200, self.run_mapping["selected_openrouter_model"], config.SELECTED_OPENROUTER_MODEL) response = { "status":400, "result":{ @@ -2055,6 +2131,10 @@ class Controller: auth_keys = config.AUTH_KEYS auth_keys[translator_name] = None config.AUTH_KEYS = auth_keys + config.SELECTABLE_OPENROUTER_MODEL_LIST = [] + config.SELECTED_OPENROUTER_MODEL = None + self.run(200, self.run_mapping["selectable_openrouter_model_list"], config.SELECTABLE_OPENROUTER_MODEL_LIST) + self.run(200, self.run_mapping["selected_openrouter_model"], config.SELECTED_OPENROUTER_MODEL) config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = False self.updateTranslationEngineAndEngineList() return {"status":200, "result":config.AUTH_KEYS[translator_name]} @@ -2116,6 +2196,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":True} else: + config.SELECTABLE_LMSTUDIO_MODEL_LIST = [] + config.SELECTED_LMSTUDIO_MODEL = None + self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) response = { "status":400, "result":{ @@ -2125,6 +2209,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_LMSTUDIO_MODEL_LIST = [] + config.SELECTED_LMSTUDIO_MODEL = None + self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) response = { "status":400, "result":{ @@ -2162,6 +2250,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.LMSTUDIO_URL} else: + config.SELECTABLE_LMSTUDIO_MODEL_LIST = [] + config.SELECTED_LMSTUDIO_MODEL = None + self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) response = { "status":400, "result":{ @@ -2171,6 +2263,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_LMSTUDIO_MODEL_LIST = [] + config.SELECTED_LMSTUDIO_MODEL = None + self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) response = { "status":400, "result":{ @@ -2238,6 +2334,10 @@ class Controller: self.updateTranslationEngineAndEngineList() response = {"status":200, "result":True} else: + config.SELECTABLE_OLLAMA_MODEL_LIST = [] + config.SELECTED_OLLAMA_MODEL = None + self.run(200, self.run_mapping["selectable_ollama_model_list"], config.SELECTABLE_OLLAMA_MODEL_LIST) + self.run(200, self.run_mapping["selected_ollama_model"], config.SELECTED_OLLAMA_MODEL) response = { "status":400, "result":{ @@ -2247,6 +2347,10 @@ class Controller: } except Exception as e: errorLogging() + config.SELECTABLE_OLLAMA_MODEL_LIST = [] + config.SELECTED_OLLAMA_MODEL = None + self.run(200, self.run_mapping["selectable_ollama_model_list"], config.SELECTABLE_OLLAMA_MODEL_LIST) + self.run(200, self.run_mapping["selected_ollama_model"], config.SELECTED_OLLAMA_MODEL) response = { "status":400, "result":{ @@ -3216,6 +3320,42 @@ class Controller: auth_keys[engine] = None config.AUTH_KEYS = auth_keys printLog("OpenAI API Key is invalid") + case "Groq_API": + printLog("Start check Groq API Key") + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False + if config.AUTH_KEYS[engine] is not None: + if model.authenticationTranslatorGroqAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: + config.SELECTABLE_GROQ_MODEL_LIST = model.getTranslatorGroqModelList() + if config.SELECTED_GROQ_MODEL not in config.SELECTABLE_GROQ_MODEL_LIST: + config.SELECTED_GROQ_MODEL = config.SELECTABLE_GROQ_MODEL_LIST[0] + model.setTranslatorGroqModel(config.SELECTED_GROQ_MODEL) + model.updateTranslatorGroqClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("Groq API Key is valid") + else: + # error update Auth key + auth_keys = config.AUTH_KEYS + auth_keys[engine] = None + config.AUTH_KEYS = auth_keys + printLog("Groq API Key is invalid") + case "OpenRouter_API": + printLog("Start check OpenRouter API Key") + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False + if config.AUTH_KEYS[engine] is not None: + if model.authenticationTranslatorOpenRouterAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: + config.SELECTABLE_OPENROUTER_MODEL_LIST = model.getTranslatorOpenRouterModelList() + if config.SELECTED_OPENROUTER_MODEL not in config.SELECTABLE_OPENROUTER_MODEL_LIST: + config.SELECTED_OPENROUTER_MODEL = config.SELECTABLE_OPENROUTER_MODEL_LIST[0] + model.setTranslatorOpenRouterModel(config.SELECTED_OPENROUTER_MODEL) + model.updateTranslatorOpenRouterClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("OpenRouter API Key is valid") + else: + # error update Auth key + auth_keys = config.AUTH_KEYS + auth_keys[engine] = None + config.AUTH_KEYS = auth_keys + printLog("OpenRouter API Key is invalid") case "LMStudio": printLog("Start check LMStudio Server") config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False diff --git a/src-python/mainloop.py b/src-python/mainloop.py index d3e158bf..2d6aa50a 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -211,6 +211,20 @@ mapping = { "/set/data/openai_auth_key": {"status": True, "variable":controller.setOpenAIAuthKey}, "/delete/data/openai_auth_key": {"status": True, "variable":controller.delOpenAIAuthKey}, + "/get/data/selectable_groq_model_list": {"status": True, "variable":controller.getGroqModelList}, + "/get/data/selected_groq_model": {"status": True, "variable":controller.getGroqModel}, + "/set/data/selected_groq_model": {"status": True, "variable":controller.setGroqModel}, + "/get/data/groq_auth_key": {"status": True, "variable":controller.getGroqAuthKey}, + "/set/data/groq_auth_key": {"status": True, "variable":controller.setGroqAuthKey}, + "/delete/data/groq_auth_key": {"status": True, "variable":controller.delGroqAuthKey}, + + "/get/data/selectable_openrouter_model_list": {"status": True, "variable":controller.getOpenRouterModelList}, + "/get/data/selected_openrouter_model": {"status": True, "variable":controller.getOpenRouterModel}, + "/set/data/selected_openrouter_model": {"status": True, "variable":controller.setOpenRouterModel}, + "/get/data/openrouter_auth_key": {"status": True, "variable":controller.getOpenRouterAuthKey}, + "/set/data/openrouter_auth_key": {"status": True, "variable":controller.setOpenRouterAuthKey}, + "/delete/data/openrouter_auth_key": {"status": True, "variable":controller.delOpenRouterAuthKey}, + "/get/data/connected_lmstudio": {"status": True, "variable":controller.getTranslatorLMStudioConnection}, "/run/lmstudio_connection": {"status": True, "variable":controller.checkTranslatorLMStudioConnection}, "/get/data/selectable_lmstudio_model_list": {"status": True, "variable":controller.getTranslatorLStudioModelList},