first usable type

This commit is contained in:
2025-06-04 23:09:08 +08:00
parent 7e3fbb1865
commit 6e477db0b2
9 changed files with 3206 additions and 13 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
__pycache__
.ruff_cache
.venv

View File

@ -2,8 +2,14 @@
本來使用[LibreTranslate](https://github.com/LibreTranslate/LibreTranslate)作為Google翻譯的替代方案但使用體驗真的是不佳至少以正體中文與英文互翻的狀況下有時候翻譯結果超ㄎ一ㄤ的所以就自己DIY寫一個吧🥵
## Requirements
* MacOS 14
* Hardware
* MacbookPro14 2021
* Software
* MacOS 14
* [Ollama](https://ollama.com/)
## Dirs
## Files
* **tests**
* unittest codes by Pytest
* **translator**
* app source codes

3060
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,12 @@ authors = [
{name = "deng",email = "deng@guineapig.love"}
]
readme = "README.md"
requires-python = ">=3.13"
requires-python = "~=3.13"
dependencies = [
"streamlit>=1.45.0,<2.0.0",
"langchain>=0.3.0,<0.4.0",
"langchain-openai>=0.3.0,<0.4.0",
"langchain-community>=0.3.0,<0.4.0",
]
[build-system]

3
tests/test_config.yaml Normal file
View File

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

17
tests/test_utils.py Normal file
View File

@ -0,0 +1,17 @@
# test_utils.py
#
# author: deng
# date : 20250604
from translator.utils import parse_config
class TestUtils:
def test_parse_config(self) -> None:
config = parse_config('tests/test_config.yaml')
assert isinstance(config, dict)
assert 'test' in config
assert 'key1' in config['test']
assert 'key2' in config['test']
assert config['test']['key1'] == 'value1'
assert config['test']['key2'] == 'value2'

94
translator/app.py Normal file
View File

@ -0,0 +1,94 @@
# app.py
#
# author: deng
# date : 20250604
import streamlit as st
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.chat_models import ChatOllama
from utils import parse_config
class TranslatorApp:
"""Streamlit App for Language Translation"""
def __init__(self, config_path: str = 'config.yaml'):
self._config = parse_config(config_path)
self._lang_directions = {
'英->中': ('英文', '正體中文', '台灣地區用語'),
'中->英': ('正體中文', '英文', '自然通順'),
'中->日': ('正體中文', '日文', '自然通順'),
'日->中': ('日文', '正體中文', '自然通順')
}
self._chain = self._prepare_chain()
def _prepare_chain(self) -> LLMChain:
"""Prepare the chain for translation"""
template = (
'你是專業的翻譯人員,請判斷這段句子「{input_text}」的語言是否為{source_lang},若非的'
'話則請回傳一模一樣的句子,若是的話則請將該句翻譯成{target_lang},並且符合{rule}(僅回'
'傳翻譯結果即可)。'
)
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'],
stop=None
)
prompt = PromptTemplate(
input_variables=['input_text', 'source_lang', 'target_lang', 'rule'],
template=template
)
return LLMChain(llm=llm, prompt=prompt)
def run(self) -> None:
""" Run the Streamlit app """
st.title(body='跨過語言的黑水溝')
direction = st.radio(
label='Direction',
options=list(self._lang_directions.keys()),
index=0,
key='lang_choice',
horizontal=True
)
input_text = st.text_area(
label='Input',
placeholder='請輸入文字',
key='input_text'
)
output_container = st.empty()
_ = output_container.text_area(
label='Output',
placeholder='',
disabled=True
)
if st.button('Go'):
if not input_text.strip():
st.warning("請輸入要翻譯的文字")
return
with st.spinner('翻譯中...'):
source_lang, target_lang, rule = self._lang_directions[direction]
result = self._chain.run({
'input_text': input_text,
'source_lang': source_lang,
'target_lang': target_lang,
'rule': rule
}).strip()
output_container.text_area(
label='Output',
value=result,
disabled=True
)
if __name__ == '__main__':
app = TranslatorApp()
app.run()

6
translator/config.yaml Normal file
View File

@ -0,0 +1,6 @@
app:
ollama_url: http://localhost:11434
ollama_model_name: gemma3:12b-it-qat
ollama_max_tokens: 256
ollama_temperature: 0.2
ollama_top_p: 0.9

20
translator/utils.py Normal file
View File

@ -0,0 +1,20 @@
# utils.py
#
# author: deng
# date : 20250604
import yaml
def parse_config(config_path: str) -> dict:
"""Config parser
Args:
config_path (str): path of config yaml.
Returns:
dict: configuration dictionary
"""
with open(config_path, 'r', encoding='utf-8') as file:
config = yaml.safe_load(file)
return config