1) replace gpt4.1 to gpt5.4, 2) accelerate loading speed

This commit is contained in:
deng
2026-05-25 11:26:50 +08:00
parent d299564c5c
commit 8b296ed979
7 changed files with 92 additions and 73 deletions

3
tests/test_config.toml Normal file
View File

@ -0,0 +1,3 @@
[test]
key1 = "value1"
key2 = "value2"

View File

@ -1,3 +0,0 @@
test:
key1: value1
key2: value2

View File

@ -8,7 +8,7 @@ from translator.utils import parse_config
class TestUtils: class TestUtils:
def test_parse_config(self) -> None: def test_parse_config(self) -> None:
config = parse_config('tests/test_config.yaml') config = parse_config('tests/test_config.toml')
assert isinstance(config, dict) assert isinstance(config, dict)
assert 'test' in config assert 'test' in config
assert 'key1' in config['test'] assert 'key1' in config['test']

View File

@ -3,56 +3,69 @@
# author: deng # author: deng
# date : 20250604 # date : 20250604
import threading
import streamlit as st import streamlit as st
from langchain.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama
from langchain_openai import ChatOpenAI
from utils import parse_config from utils import parse_config
class TranslatorApp: class TranslatorApp:
"""Streamlit App for Language Translation""" """Streamlit App for Language Translation"""
def __init__(self, config_path: str = 'config.yaml'): def __init__(self, config_path: str = 'assets/config.toml'):
self._config = parse_config(config_path) self._config = parse_config(config_path)
self._chain = None self._chain = None
def _prepare_chain(self) -> None: # Start pre-warming imports and LLM client in a background thread
"""Prepare the chain for translation""" self._lock = threading.Lock()
threading.Thread(target=self.prepare_chain, daemon=True).start()
def prepare_chain(self) -> None:
"""Prepare the chain for translation.
Thread-safe: safe to call from both background thread and main thread.
"""
if self._chain is not None: if self._chain is not None:
return return
system_template = ( with self._lock:
'你是專業的翻譯人員,請判斷接下來句子的語言是否為{source_lang},若是的話則請將該句翻譯成{target_lang}' if self._chain is not None:
'並且符合{description}(僅回傳翻譯結果即可),若非的話則請回傳一模一樣的句子。' return
)
user_template = '{input_text}'
if self._config['app']['llm_mode'] == 'ollama': from langchain.prompts import ChatPromptTemplate
llm = ChatOllama(
base_url=self._config['app']['ollama']['url'],
model=self._config['app']['ollama']['model_name'],
temperature=self._config['app']['ollama']['temperature'],
max_tokens=self._config['app']['ollama']['max_tokens'],
top_p=self._config['app']['ollama']['top_p'],
keep_alive=self._config['app']['ollama']['keep_alive'],
stop=None
)
elif self._config['app']['llm_mode'] == 'openai':
llm = ChatOpenAI(
model=self._config['app']['openai']['model_name'],
temperature=self._config['app']['openai']['temperature'],
max_tokens=self._config['app']['openai']['max_tokens'],
top_p=self._config['app']['openai']['top_p']
)
else:
raise ValueError(f'Unsupported llm model: {self._config['app']['llm_mode']}')
prompt = ChatPromptTemplate.from_messages([ system_template = (
('system', system_template), '你是專業的翻譯人員,請判斷接下來句子的語言是否為{source_lang},若是的話則請將該句翻譯成{target_lang}'
('human', user_template) '並且符合{description}(僅回傳翻譯結果即可),若非的話則請回傳一模一樣的句子。'
]) )
self._chain = prompt | llm user_template = '{input_text}'
if self._config['app']['llm_mode'] == 'ollama':
from langchain_ollama import ChatOllama
llm = ChatOllama(
base_url=self._config['app']['ollama']['url'],
model=self._config['app']['ollama']['model_name'],
temperature=self._config['app']['ollama']['temperature'],
max_tokens=self._config['app']['ollama']['max_tokens'],
top_p=self._config['app']['ollama']['top_p'],
keep_alive=self._config['app']['ollama']['keep_alive'],
stop=None
)
elif self._config['app']['llm_mode'] == 'openai':
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model=self._config['app']['openai']['model_name'],
temperature=self._config['app']['openai']['temperature'],
max_tokens=self._config['app']['openai']['max_tokens'],
top_p=self._config['app']['openai']['top_p']
)
else:
raise ValueError(f"Unsupported llm model: {self._config['app']['llm_mode']}")
prompt = ChatPromptTemplate.from_messages([
('system', system_template),
('human', user_template)
])
self._chain = prompt | llm
def run(self) -> None: def run(self) -> None:
""" Run the Streamlit app """ """ Run the Streamlit app """
@ -79,14 +92,12 @@ class TranslatorApp:
translate_button = st.button('翻譯') translate_button = st.button('翻譯')
output_container = st.empty() output_container = st.empty()
# Prepare chain
self._prepare_chain()
# Action # Action
if input_text or translate_button: if input_text or translate_button:
if not input_text.strip(): if not input_text.strip():
st.warning("請輸入要翻譯的文字") st.warning('請輸入要翻譯的文字')
else: else:
self.prepare_chain()
with st.spinner('翻譯中...'): with st.spinner('翻譯中...'):
result = self._chain.stream({ result = self._chain.stream({
'input_text': input_text, 'input_text': input_text,
@ -99,6 +110,11 @@ class TranslatorApp:
) )
@st.cache_resource(show_spinner=False, max_entries=1)
def get_app_instance() -> TranslatorApp:
return TranslatorApp()
if __name__ == '__main__': if __name__ == '__main__':
app = TranslatorApp() app = get_app_instance()
app.run() app.run()

View File

@ -0,0 +1,28 @@
[app]
page_title = "橫渡語言的黑水溝"
page_favicon_path = "./assets/favicon.png"
llm_mode = "openai"
[app.ollama]
url = "http://localhost:11434"
model_name = "gemma3:12b-it-qat"
max_tokens = 512
temperature = 0.2
top_p = 0.9
keep_alive = "2m"
[app.openai]
model_name = "gpt-5.4-nano"
max_tokens = 1024
temperature = 0.2
top_p = 0.9
[app.lang_directions."英->中"]
source_lang = "英文"
target_lang = "正體中文"
description = "台灣地區用語"
[app.lang_directions."中->英"]
source_lang = "中文"
target_lang = "英文"
description = "自然通順"

View File

@ -1,25 +0,0 @@
app:
page_title: 橫渡語言的黑水溝
page_favicon_path: ./assets/favicon.png
llm_mode: openai
ollama:
url: http://localhost:11434
model_name: gemma3:12b-it-qat
max_tokens: 512
temperature: 0.2
top_p: 0.9
keep_alive: 2m
openai:
model_name: gpt-4.1-nano
max_tokens: 1024
temperature: 0.2
top_p: 0.9
lang_directions:
'英->中':
source_lang: '英文'
target_lang: '正體中文'
description: '台灣地區用語'
'中->英':
source_lang: '中文'
target_lang: '英文'
description: '自然通順'

View File

@ -3,18 +3,18 @@
# author: deng # author: deng
# date : 20250604 # date : 20250604
import yaml import tomllib
def parse_config(config_path: str) -> dict: def parse_config(config_path: str) -> dict:
"""Config parser """Config parser
Args: Args:
config_path (str): path of config yaml. config_path (str): path of config toml.
Returns: Returns:
dict: configuration dictionary dict: configuration dictionary
""" """
with open(config_path, 'r', encoding='utf-8') as file: with open(config_path, 'rb') as file:
config = yaml.safe_load(file) config = tomllib.load(file)
return config return config