翻訳APIの認証ロジックを改善し、YAMLファイルからプロンプト設定を読み込む機能を追加。新しいフォントファイルを追加し、データパスを更新。

This commit is contained in:
misyaguziya
2025-10-15 18:50:26 +09:00
parent 7d0f63c118
commit f8466bd6e4
15 changed files with 206 additions and 148 deletions

View File

@@ -5,7 +5,14 @@ a = Analysis(
['src-python\\mainloop.py'], ['src-python\\mainloop.py'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[('./fonts', 'fonts/'), ('.venv/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv/Lib/site-packages/openvr', 'openvr/'), ('.venv/Lib/site-packages/faster_whisper', 'faster_whisper/'), ('.venv/Lib/site-packages/hf_xet', 'hf_xet/')], datas=[
('./src-python/models/overlay/fonts', 'fonts/'),
('./src-python/models/translation/prompt', 'prompt/'),
('.venv/Lib/site-packages/zeroconf', 'zeroconf/'),
('.venv/Lib/site-packages/openvr', 'openvr/'),
('.venv/Lib/site-packages/faster_whisper', 'faster_whisper/'),
('.venv/Lib/site-packages/hf_xet', 'hf_xet/')
],
hiddenimports=[], hiddenimports=[],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

View File

@@ -5,7 +5,14 @@ a = Analysis(
['src-python\\mainloop.py'], ['src-python\\mainloop.py'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[('./fonts', 'fonts/'), ('.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv_cuda/Lib/site-packages/openvr', 'openvr/'), ('.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/'), ('.venv/Lib/site-packages/hf_xet', 'hf_xet/')], datas=[
('./src-python/models/overlay/fonts', 'fonts/'),
('./src-python/models/translation/prompt', 'prompt/'),
('.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'),
('.venv_cuda/Lib/site-packages/openvr', 'openvr/'),
('.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/'),
('.venv/Lib/site-packages/hf_xet', 'hf_xet/')
],
hiddenimports=[], hiddenimports=[],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

View File

@@ -2640,22 +2640,39 @@ class Controller:
if config.AUTH_KEYS[engine] is not None: if config.AUTH_KEYS[engine] is not None:
if model.authenticationTranslatorDeepLAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: if model.authenticationTranslatorDeepLAuthKey(auth_key=config.AUTH_KEYS[engine]) is True:
config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True
printLog("DeepL API Key is valid")
else: else:
# error update Auth key # error update Auth key
auth_keys = config.AUTH_KEYS auth_keys = config.AUTH_KEYS
auth_keys[engine] = None auth_keys[engine] = None
config.AUTH_KEYS = auth_keys config.AUTH_KEYS = auth_keys
printLog("DeepL API Key is invalid")
case "Plamo_API": case "Plamo_API":
printLog("Start check Plamo API Key") printLog("Start check Plamo API Key")
config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False
if config.AUTH_KEYS[engine] is not None: 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], model=config.PLAMO_MODEL) is True:
config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True
printLog("Plamo API Key is valid")
else: else:
# error update Auth key # error update Auth key
auth_keys = config.AUTH_KEYS auth_keys = config.AUTH_KEYS
auth_keys[engine] = None auth_keys[engine] = None
config.AUTH_KEYS = auth_keys config.AUTH_KEYS = auth_keys
printLog("Plamo API Key is invalid")
case "Gemini_API":
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:
config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True
printLog("Gemini API Key is valid")
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 _: case _:
if connected_network is True: if connected_network is True:
config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True

View File

@@ -199,11 +199,11 @@ class Model:
return result return result
def authenticationTranslatorPlamoAuthKey(self, auth_key: str, model: str) -> bool: def authenticationTranslatorPlamoAuthKey(self, auth_key: str, model: str) -> bool:
result = self.translator.authenticationPlamoAuthKey(auth_key, model=model) result = self.translator.authenticationPlamoAuthKey(auth_key, model=model, root_path=config.PATH_LOCAL)
return result return result
def authenticationTranslatorGeminiAuthKey(self, auth_key: str, model: str) -> bool: def authenticationTranslatorGeminiAuthKey(self, auth_key: str, model: str) -> bool:
result = self.translator.authenticationGeminiAuthKey(auth_key, model=model) result = self.translator.authenticationGeminiAuthKey(auth_key, model=model, root_path=config.PATH_LOCAL)
return result return result
def startLogger(self): def startLogger(self):

View File

@@ -26,10 +26,17 @@ class OverlayImage:
defaults to repository `fonts` directory. defaults to repository `fonts` directory.
""" """
self.message_log: List[dict] = [] self.message_log: List[dict] = []
if root_path is None: # PyInstallerでビルドされた場合のパス
self.root_path = os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts") if root_path and os_path.exists(os_path.join(root_path, "_internal", "fonts")):
else:
self.root_path = os_path.join(root_path, "_internal", "fonts") self.root_path = os_path.join(root_path, "_internal", "fonts")
# src-pythonフォルダから直接実行している場合のパス
elif os_path.exists(os_path.join(os_path.dirname(__file__), "models", "overlay", "fonts")):
self.root_path = os_path.join(os_path.dirname(__file__), "models", "overlay", "fonts")
# overlayフォルダから直接実行している場合のパス
elif os_path.exists(os_path.join(os_path.dirname(__file__), "fonts")):
self.root_path = os_path.join(os_path.dirname(__file__), "fonts")
else:
raise FileNotFoundError("Font directory not found.")
@staticmethod @staticmethod
def concatenateImagesVertically(img1: Image, img2: Image, margin: int = 0) -> Image: def concatenateImagesVertically(img1: Image, img2: Image, margin: int = 0) -> Image:
@@ -69,20 +76,8 @@ class OverlayImage:
img = Image.new("RGBA", (base_width, base_height), (0, 0, 0, 0)) img = Image.new("RGBA", (base_width, base_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
try:
font_path = os_path.join(self.root_path, font_family) font_path = os_path.join(self.root_path, font_family)
font = ImageFont.truetype(font_path, font_size) font = ImageFont.truetype(font_path, font_size)
except Exception:
# overlayフォルダから操作している場合
if os_path.exists(os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", font_family)):
font_path = os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", font_family)
font = ImageFont.truetype(font_path, font_size)
elif os_path.exists(os_path.join(os_path.dirname(__file__), "fonts", font_family)):
# src-pythonフォルダから操作している場合
font_path = os_path.join(os_path.dirname(__file__), "fonts", font_family)
font = ImageFont.truetype(font_path, font_size)
else:
raise FileNotFoundError(f"Font file not found: {font_family}")
text_width = draw.textlength(text, font) text_width = draw.textlength(text, font)
character_width = text_width // len(text) character_width = text_width // len(text)
@@ -180,18 +175,8 @@ class OverlayImage:
img = Image.new("RGBA", (0, 0), (0, 0, 0, 0)) img = Image.new("RGBA", (0, 0), (0, 0, 0, 0))
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
try:
font_path = os_path.join(self.root_path, font_family) font_path = os_path.join(self.root_path, font_family)
font = ImageFont.truetype(font_path, font_size) font = ImageFont.truetype(font_path, font_size)
except Exception:
if os_path.exists(os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", font_family)):
font_path = os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", font_family)
font = ImageFont.truetype(font_path, font_size)
elif os_path.exists(os_path.join(os_path.dirname(__file__), "fonts", font_family)):
font_path = os_path.join(os_path.dirname(__file__), "fonts", font_family)
font = ImageFont.truetype(font_path, font_size)
else:
raise FileNotFoundError(f"Font file not found: {font_family}")
# 改行を含んだtextの最大の文字数を計算する # 改行を含んだtextの最大の文字数を計算する
text_width = max(draw.textlength(line, font) for line in text.split("\n")) text_width = max(draw.textlength(line, font) for line in text.split("\n"))
@@ -221,20 +206,8 @@ class OverlayImage:
img = Image.new("RGBA", (0, 0), (0, 0, 0, 0)) img = Image.new("RGBA", (0, 0), (0, 0, 0, 0))
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
try:
font_path = os_path.join(self.root_path, self.LANGUAGES["Default"]) font_path = os_path.join(self.root_path, self.LANGUAGES["Default"])
font = ImageFont.truetype(font_path, font_size) font = ImageFont.truetype(font_path, font_size)
except Exception:
# overlayフォルダから操作している場合
if os_path.exists(os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", self.LANGUAGES["Default"])):
font_path = os_path.join(os_path.dirname(__file__), "..", "..", "..", "fonts", self.LANGUAGES["Default"])
font = ImageFont.truetype(font_path, font_size)
elif os_path.exists(os_path.join(os_path.dirname(__file__), "fonts", self.LANGUAGES["Default"])):
# src-pythonフォルダから操作している場合
font_path = os_path.join(os_path.dirname(__file__), "fonts", self.LANGUAGES["Default"])
font = ImageFont.truetype(font_path, font_size)
else:
raise FileNotFoundError(f"Font file not found: {self.LANGUAGES['Default']}")
text_height = font_size + ui_padding text_height = font_size + ui_padding
text_width = draw.textlength(date_time, font) text_width = draw.textlength(date_time, font)

View File

@@ -0,0 +1,45 @@
system_prompt: |
Please translate the following text from {input_lang} to {output_lang}.
Only provide the translated text as the output.
{text}
supported_languages: |
Arabic
Bengali
Bulgarian
Simplified Chinese
Traditional Chinese
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

View File

@@ -0,0 +1,39 @@
system_prompt: |
You are a translation assistant that uses the `plamo-translate` tool.
Translate the following text.Supported languages include:
{supported_languages}
Translate the following text from {input_lang} to {output_lang}.
output only the translated text without any additional commentary.
supported_languages: |
English
Japanese
Korean
French
German
Spanish
Portuguese
Russian
Italian
Dutch
Polish
Turkish
Arabic
Hindi
Thai
Vietnamese
Indonesian
Malay
Filipino
Swedish
Finnish
Danish
Norwegian
Romanian
Czech
Hungarian
Greek
Hebrew
Simplified Chinese
Traditional Chinese

View File

@@ -1,6 +1,8 @@
import logging import logging
from langchain_google_genai import ChatGoogleGenerativeAI from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import HumanMessage from langchain_core.messages import HumanMessage
import yaml
from os import path as os_path
logger = logging.getLogger("langchain_google_genai") logger = logging.getLogger("langchain_google_genai")
logger.setLevel(logging.ERROR) logger.setLevel(logging.ERROR)
@@ -17,19 +19,39 @@ _MODELS = [
] ]
class GeminiClient: class GeminiClient:
def __init__(self, api_key: str = "", model: str = "gemini-2.5-flash-lite"): def __init__(self, api_key: str = "", model: str = "gemini-2.5-flash-lite", root_path: str = None):
self.api_key = api_key self.api_key = api_key
self.model = model self.model = model
self.prompt_template = """
Please translate the following text from {input_lang} to {output_lang}. # プロンプト設定をYAMLファイルから読み込む
Only provide the translated text as the output. prompt_config = self._load_prompt_config(root_path)
{text} self.supported_languages = prompt_config["supported_languages"]
""" self.prompt_template = prompt_config["system_prompt"]
self.gemini_llm = ChatGoogleGenerativeAI( self.gemini_llm = ChatGoogleGenerativeAI(
model=self.model, model=self.model,
api_key=self.api_key, 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]: def getListModels(self) -> list[str]:
return _MODELS return _MODELS
@@ -112,50 +134,8 @@ if __name__ == "__main__":
# except Exception: # except Exception:
# print("Invalid API key. Please check your credentials.") # print("Invalid API key. Please check your credentials.")
# 外部ファイルから読み込んだサポート言語を使用
supported_languages = """ for lang in gemini_client.supported_languages.split("\n"):
Arabic
Bengali
Bulgarian
Simplified Chinese
Traditional Chinese
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
"""
for lang in supported_languages.split("\n"):
if lang == "": if lang == "":
continue continue
print (f"Translating to {lang}:") print (f"Translating to {lang}:")

View File

@@ -615,8 +615,8 @@ dict_plamo_languages = {
"Hungarian": "Hungarian", "Hungarian": "Hungarian",
"Greek": "Greek", "Greek": "Greek",
"Hebrew": "Hebrew", "Hebrew": "Hebrew",
"Simplified Chinese":"Simplified Chinese", "Chinese Simplified":"Simplified Chinese",
"Traditional Chinese":"Traditional Chinese" "Chinese Traditional":"Traditional Chinese"
} }
translation_lang["Plamo_API"] = {"source":dict_plamo_languages, "target":dict_plamo_languages} translation_lang["Plamo_API"] = {"source":dict_plamo_languages, "target":dict_plamo_languages}
@@ -625,8 +625,8 @@ dict_gemini_languages = {
"Arabic": "Arabic", "Arabic": "Arabic",
"Bengali": "Bengali", "Bengali": "Bengali",
"Bulgarian": "Bulgarian", "Bulgarian": "Bulgarian",
"Simplified Chinese": "Simplified Chinese", "Chinese Simplified": "Simplified Chinese",
"Traditional Chinese": "Traditional Chinese", "Chinese Traditional": "Traditional Chinese",
"Croatian": "Croatian", "Croatian": "Croatian",
"Czech": "Czech", "Czech": "Czech",
"Danish": "Danish", "Danish": "Danish",

View File

@@ -1,53 +1,23 @@
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from pydantic import SecretStr from pydantic import SecretStr
import yaml
from os import path as os_path
_MODELS = [ _MODELS = [
"plamo-2.0-prime" "plamo-2.0-prime"
] ]
class PlamoClient: class PlamoClient:
def __init__(self, api_key: str = "", model: str = "plamo-2.0-prime"): def __init__(self, api_key: str = "", model: str = "plamo-2.0-prime", root_path: str = None):
self.api_key = api_key self.api_key = api_key
self.base_url = "https://api.platform.preferredai.jp/v1" self.base_url = "https://api.platform.preferredai.jp/v1"
self.model = model self.model = model
self.supported_languages = """
English # プロンプト設定をYAMLファイルから読み込む
Japanese prompt_config = self._load_prompt_config(root_path)
Korean self.supported_languages = prompt_config["supported_languages"]
French self.prompt_template = prompt_config["system_prompt"]
German
Spanish
Portuguese
Russian
Italian
Dutch
Polish
Turkish
Arabic
Hindi
Thai
Vietnamese
Indonesian
Malay
Filipino
Swedish
Finnish
Danish
Norwegian
Romanian
Czech
Hungarian
Greek
Hebrew
Simplified Chinese
Traditional Chinese
"""
self.prompt_template = f"""
You are a translation assistant that uses the `plamo-translate` tool.
Translate the following text.Supported languages include:{self.supported_languages}
Translate the following text from {{input_lang}} to {{output_lang}}.
output only the translated text without any additional commentary.
"""
self.plamo_llm = ChatOpenAI( self.plamo_llm = ChatOpenAI(
base_url=self.base_url, base_url=self.base_url,
model=self.model, model=self.model,
@@ -55,6 +25,25 @@ class PlamoClient:
api_key=SecretStr(self.api_key), 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]: def getListModels(self) -> list[str]:
return _MODELS return _MODELS
@@ -78,13 +67,11 @@ class PlamoClient:
) )
return True return True
except Exception as e: except Exception as e:
print(f"Error setting AuthKey: {e}")
return False return False
def setModel(self, model: str) -> bool: def setModel(self, model: str) -> bool:
"""モデルを設定し、成功したかどうかを返す""" """モデルを設定し、成功したかどうかを返す"""
if model not in _MODELS: if model not in _MODELS:
print(f"Model {model} is not in the supported model list.")
return False return False
try: try:
@@ -105,7 +92,9 @@ class PlamoClient:
{ {
"role": "system", "role": "system",
"content": self.prompt_template.format( "content": self.prompt_template.format(
input_lang=input_lang, output_lang=output_lang supported_languages=self.supported_languages,
input_lang=input_lang,
output_lang=output_lang
), ),
}, },
{"role": "user", "content": text}, {"role": "user", "content": text},
@@ -130,7 +119,8 @@ class PlamoClient:
self.setModel(self.model) self.setModel(self.model)
self.translate("Hello World", input_lang="English", output_lang="Japanese") self.translate("Hello World", input_lang="English", output_lang="Japanese")
return True return True
except Exception: except Exception as e:
print(f"Error checking AuthKey: {e}")
return False return False
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -66,14 +66,14 @@ class Translator:
result = False result = False
return result return result
def authenticationPlamoAuthKey(self, auth_key: str, model: str) -> bool: def authenticationPlamoAuthKey(self, auth_key: str, model: str, root_path: str = None) -> bool:
"""Authenticate Plamo API with the provided key. """Authenticate Plamo API with the provided key.
Returns True on success, False on failure. Returns True on success, False on failure.
""" """
result = True result = True
try: try:
self.plamo_client = PlamoClient(auth_key, model=model) self.plamo_client = PlamoClient(auth_key, model=model, root_path=root_path)
self.plamo_client.checkAuthKey() self.plamo_client.checkAuthKey()
except Exception: except Exception:
errorLogging() errorLogging()
@@ -81,14 +81,14 @@ class Translator:
result = False result = False
return result return result
def authenticationGeminiAuthKey(self, auth_key: str, model: str) -> bool: def authenticationGeminiAuthKey(self, auth_key: str, model: str, root_path: str = None) -> bool:
"""Authenticate Gemini API with the provided key. """Authenticate Gemini API with the provided key.
Returns True on success, False on failure. Returns True on success, False on failure.
""" """
result = True result = True
try: try:
self.gemini_client = GeminiClient(auth_key, model=model) self.gemini_client = GeminiClient(auth_key, model=model, root_path=root_path)
self.gemini_client.checkAuthKey() self.gemini_client.checkAuthKey()
except Exception: except Exception:
errorLogging() errorLogging()