From 47a3e9126a9e25622c302cdde7049c7ef90b7804 Mon Sep 17 00:00:00 2001 From: r-earth-or Date: Wed, 4 Mar 2026 15:28:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=87=AA=E5=8A=A8=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E5=A1=AB=E5=86=99=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E8=BE=93=E5=85=A5=E9=A1=B9=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=9C=AF=E8=AF=AD=E8=A1=A8=E4=B8=8E=E9=A2=86=E5=9F=9F=E7=9F=A5?= =?UTF-8?q?=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 29 ++ .gitignore | 5 +- docutranslate/agents/agent.py | 17 +- docutranslate/app.py | 19 ++ docutranslate/core/factory.py | 2 +- docutranslate/core/schemas.py | 3 + docutranslate/environment.py | 41 +++ docutranslate/global_values/__init__.py | 11 +- docutranslate/sdk.py | 2 + docutranslate/static/index.html | 285 ++---------------- .../translator/ai_translator/md_translator.py | 1 + pyproject.toml | 1 + uv.lock | 10 +- 13 files changed, 150 insertions(+), 276 deletions(-) create mode 100644 .env.example create mode 100644 docutranslate/environment.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a171cde --- /dev/null +++ b/.env.example @@ -0,0 +1,29 @@ +# DocuTranslate 环境变量配置示例 +# 复制此文件为 .env 并按需修改(需配合 python-dotenv 或容器环境变量使用) + +# --- 代理配置 --- +# 是否启用系统代理,设置为 true 开启 +# DOCUTRANSLATE_PROXY_ENABLED=true + +# --- 缓存配置 --- +# 任务缓存数量(默认 10) +# DOCUTRANSLATE_CACHE_NUM=10 + +# --- 翻译 API 默认配置 --- +# 前端"自定义接口-default"平台的默认值,留空则不预填 + +# API 地址(Base URL),例如 https://api.openai.com/v1 +DOCUTRANSLATE_BASE_URL= + +# API 密钥 +DOCUTRANSLATE_API_KEY= + +# 模型 ID,例如 qwen-mt-turbo +DOCUTRANSLATE_MODEL_ID= + +# --- 限流配置 --- +# RPM 限制(每分钟请求数),留空则不限制 +DOCUTRANSLATE_RPM= + +# TPM 限制(每分钟 Token 数),留空则不限制 +DOCUTRANSLATE_TPM= diff --git a/.gitignore b/.gitignore index fa4a038..22be5fe 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ docutranslate/output/ #idea .idea/ #claude -.claude/ \ No newline at end of file +.claude/ +/.omc/ +# Environment variables +.env diff --git a/docutranslate/agents/agent.py b/docutranslate/agents/agent.py index 762361b..9b2ceb5 100644 --- a/docutranslate/agents/agent.py +++ b/docutranslate/agents/agent.py @@ -59,6 +59,7 @@ class AgentConfig: rpm: int | None = None # 每分钟请求数限制 tpm: int | None = None # 每分钟Token数限制 provider: ProviderType | None = None + source_lang: str | None = None # qwen-mt: 源语言 class TotalErrorCounter: @@ -534,9 +535,10 @@ class Agent: self.provider = config.provider if config.provider is not None else get_provider_by_domain(self.domain) self.is_mt_mode = "mt" in self.model_id.lower() - self.mt_source_lang = getattr(config, "source_lang", "auto") + self.mt_source_lang = config.source_lang if config.source_lang else "auto" self.mt_target_lang = getattr(config, "to_lang", None) self.mt_domains = getattr(config, "custom_prompt", None) + self.mt_glossary_dict = getattr(config, "glossary_dict", None) def _estimate_tokens(self, text: str) -> int: """ @@ -588,7 +590,7 @@ class Agent: return _MT_LANG_ALIASES[key] return lang_text - def _build_mt_translation_options(self) -> dict: + def _build_mt_translation_options(self, prompt: str = "") -> dict: translation_options = {} source_lang = self._normalize_mt_lang(self.mt_source_lang) @@ -603,6 +605,15 @@ class Agent: if domains: translation_options["domains"] = domains + if self.mt_glossary_dict: + terminology_list = [ + {"source": src, "target": tgt} + for src, tgt in self.mt_glossary_dict.items() + if src and tgt and src.lower() in prompt.lower() + ] + if terminology_list: + translation_options["terms"] = terminology_list + return translation_options def _build_mt_user_prompt(self, prompt: str, system_prompt: str) -> str: @@ -627,7 +638,7 @@ class Agent: {"role": "user", "content": self._build_mt_user_prompt(prompt, system_prompt)}, ], } - translation_options = self._build_mt_translation_options() + translation_options = self._build_mt_translation_options(prompt=prompt) if translation_options: data["translation_options"] = translation_options return headers, data diff --git a/docutranslate/app.py b/docutranslate/app.py index fe943af..091d1d3 100644 --- a/docutranslate/app.py +++ b/docutranslate/app.py @@ -2472,6 +2472,25 @@ async def service_flat_translate( }) +@app.get("/api/config", tags=["Config"], summary="获取服务端环境变量默认配置") +async def get_config(): + """返回由服务端环境变量预设的前端默认配置,不含敏感信息(API key 仅返回是否存在)。""" + from docutranslate.environment import ( + DOCUTRANSLATE_BASE_URL, + DOCUTRANSLATE_API_KEY, + DOCUTRANSLATE_MODEL_ID, + DOCUTRANSLATE_RPM, + DOCUTRANSLATE_TPM, + ) + return JSONResponse({ + "base_url": DOCUTRANSLATE_BASE_URL, + "api_key": DOCUTRANSLATE_API_KEY, + "model_id": DOCUTRANSLATE_MODEL_ID, + "rpm": DOCUTRANSLATE_RPM, + "tpm": DOCUTRANSLATE_TPM, + }) + + @app.get("/", response_class=HTMLResponse, include_in_schema=False) async def main_page(): index_path = Path(STATIC_DIR) / "index.html" diff --git a/docutranslate/core/factory.py b/docutranslate/core/factory.py index 38bd7dc..79e3d19 100644 --- a/docutranslate/core/factory.py +++ b/docutranslate/core/factory.py @@ -60,7 +60,7 @@ def create_workflow_from_payload(payload: TranslatePayload, logger: logging.Logg # 1. Markdown Based Workflow if isinstance(payload, MarkdownWorkflowParams): translator_args = payload.model_dump( - include={"skip_translate", "base_url", "api_key", "model_id", "to_lang", "custom_prompt", + include={"skip_translate", "base_url", "api_key", "model_id", "to_lang", "source_lang", "custom_prompt", "temperature", "thinking", "chunk_size", "concurrent", "glossary_dict", "timeout", "retry", "system_proxy_enable", "force_json", "rpm", "tpm", "provider"}, exclude_none=True, diff --git a/docutranslate/core/schemas.py b/docutranslate/core/schemas.py index e734513..4b3ddf8 100644 --- a/docutranslate/core/schemas.py +++ b/docutranslate/core/schemas.py @@ -157,6 +157,9 @@ class BaseWorkflowParams(BaseModel): custom_prompt: Optional[str] = Field( default="", description="用户自定义的翻译Prompt。", alias="custom_prompt" ) + source_lang: Optional[str] = Field( + default=None, description="源语言(qwen-mt系列模型专用,如 'Chinese'、'English')。", examples=[None] + ) glossary_dict: Optional[Dict[str, str]] = Field( None, description="术语表字典,key为原文,value为译文。", examples=[None] ) diff --git a/docutranslate/environment.py b/docutranslate/environment.py new file mode 100644 index 0000000..d624630 --- /dev/null +++ b/docutranslate/environment.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2025 QinHan +# SPDX-License-Identifier: MPL-2.0 +""" +集中管理所有环境变量。 +所有 os.getenv() 调用应在此处统一声明,其他模块从这里导入。 +""" +import os + +from dotenv import load_dotenv + +load_dotenv() # 自动从项目根目录的 .env 文件加载环境变量(不覆盖已有的 shell 变量) + + +# --- 代理配置 --- +# 是否启用系统代理,设置为 "true" 开启 +DOCUTRANSLATE_PROXY_ENABLED: bool = ( + os.getenv("DOCUTRANSLATE_PROXY_ENABLED", "").lower() == "true" +) + +# --- 缓存配置 --- +# 任务缓存数量 +DOCUTRANSLATE_CACHE_NUM: int = int(os.getenv("DOCUTRANSLATE_CACHE_NUM", "10")) + +# --- 翻译 API 默认配置 --- +# 默认 API 地址 (自定义接口的 Base URL) +DOCUTRANSLATE_BASE_URL: str = os.getenv("DOCUTRANSLATE_BASE_URL", "") + +# 默认 API 密钥 +DOCUTRANSLATE_API_KEY: str = os.getenv("DOCUTRANSLATE_API_KEY", "") + +# 默认模型 ID +DOCUTRANSLATE_MODEL_ID: str = os.getenv("DOCUTRANSLATE_MODEL_ID", "") + +# --- 限流默认配置 --- +# 默认 RPM 限制 (Requests Per Minute),不设置则不限制 +_rpm_str = os.getenv("DOCUTRANSLATE_RPM", "") +DOCUTRANSLATE_RPM: int | None = int(_rpm_str) if _rpm_str.strip() else None + +# 默认 TPM 限制 (Tokens Per Minute),不设置则不限制 +_tpm_str = os.getenv("DOCUTRANSLATE_TPM", "") +DOCUTRANSLATE_TPM: int | None = int(_tpm_str) if _tpm_str.strip() else None diff --git a/docutranslate/global_values/__init__.py b/docutranslate/global_values/__init__.py index 3df60cf..c42c229 100644 --- a/docutranslate/global_values/__init__.py +++ b/docutranslate/global_values/__init__.py @@ -1,16 +1,9 @@ # SPDX-FileCopyrightText: 2025 QinHan # SPDX-License-Identifier: MPL-2.0 -import os +from docutranslate.environment import DOCUTRANSLATE_PROXY_ENABLED from .conditional_import import available_packages, conditional_import -USE_PROXY = ( - True - if ( - os.getenv("DOCUTRANSLATE_PROXY_ENABLED") - and os.getenv("DOCUTRANSLATE_PROXY_ENABLED").lower() == "true" - ) - else False -) +USE_PROXY = DOCUTRANSLATE_PROXY_ENABLED if USE_PROXY: print(f"USE_PROXY:{USE_PROXY}") diff --git a/docutranslate/sdk.py b/docutranslate/sdk.py index a354c8b..566054e 100644 --- a/docutranslate/sdk.py +++ b/docutranslate/sdk.py @@ -198,6 +198,7 @@ class Client: retry: Optional[int] = None, thinking: Optional[ThinkingMode] = None, custom_prompt: Optional[str] = None, + source_lang: Optional[str] = None, system_proxy_enable: Optional[bool] = None, force_json: Optional[bool] = None, rpm: Optional[int] = None, @@ -264,6 +265,7 @@ class Client: retry: Optional[int] = None, thinking: Optional[ThinkingMode] = None, custom_prompt: Optional[str] = None, + source_lang: Optional[str] = None, system_proxy_enable: Optional[bool] = None, force_json: Optional[bool] = None, rpm: Optional[int] = None, diff --git a/docutranslate/static/index.html b/docutranslate/static/index.html index 87de12d..4a307e8 100644 --- a/docutranslate/static/index.html +++ b/docutranslate/static/index.html @@ -596,6 +596,7 @@ +
@@ -673,7 +674,7 @@

@@ -698,148 +699,6 @@

- -
- - -
- -
-
- - -
-
- -
- - - - -
-
- -
- - -
- - -
- -
-
- - - - - - - -
- - -
-
- - -
- -
- -
- - - - - - -
-
-
- - -
-
- - -
-
-
@@ -1447,25 +1306,6 @@ retry: 3, rpm: null, // New RPM tpm: null, // New TPM - glossary_generate_enable: false, - glossary_agent_custom_prompt: '', - glossary_agent_config_choice: 'same', - glossary_agent_platform: 'https://api.302.ai/v1', - glossary_agent_baseurl: '', - glossary_agent_key: '', - glossary_agent_model_id: '', - glossary_agent_provider: 'api.openai.com', // Default glossary provider - glossary_agent_to_lang: 'Simplified Chinese', - glossary_agent_custom_to_lang: '', - glossary_agent_chunk_size: 1000, - glossary_agent_concurrent: 5, - glossary_agent_temperature: 0.7, - glossary_agent_retry: 3, - glossary_agent_thinking: 'default', - glossary_agent_system_proxy_enable: false, - glossary_agent_force_json: false, - glossary_agent_rpm: null, // New Glossary Agent RPM - glossary_agent_tpm: null // New Glossary Agent TPM }); // Nested Params for specific workflows @@ -1497,7 +1337,7 @@ return (v === null || v === '' || v === 'null') ? null : Number(v); }; - form.workflow_type = get('translator_last_workflow', 'markdown_based'); + form.workflow_type = get('translator_last_workflow', 'docx'); form.auto_workflow_enabled = getBool('translator_auto_workflow_enabled', true); form.convert_engine = get('translator_convert_engin', 'mineru'); form.mineru_token = get('translator_mineru_token', ''); @@ -1516,12 +1356,12 @@ form.formula_ocr = getBool('translator_formula_ocr', true); form.code_ocr = getBool('translator_code_ocr', true); form.skip_translate = getBool('translator_skip_translate', false); - form.platform = get('translator_platform_last_platform', 'https://api.302.ai/v1'); + form.platform = get('translator_platform_last_platform', 'custom'); form.system_proxy_enable = getBool('translator_system_proxy_enable', false); form.force_json = getBool('translator_force_json', false); form.to_lang = get('translator_to_lang', 'Simplified Chinese'); form.custom_to_lang = get('translator_custom_to_lang', ''); - form.thinking = get('translator_thinking_mode', 'disable'); + form.thinking = get('translator_thinking_mode', 'default'); form.custom_prompt = get('custom_prompt', ''); form.chunk_size = getNum('chunk_size', 1000); form.concurrent = getNum('concurrent', 5); @@ -1539,30 +1379,6 @@ form.provider = platObj ? platObj.provider : ''; } - form.glossary_generate_enable = getBool('glossary_generate_enable', false); - form.glossary_agent_custom_prompt = get('glossary_agent_custom_prompt', ''); - form.glossary_agent_config_choice = get('glossary_agent_config_choice', 'same'); - form.glossary_agent_platform = get('glossary_agent_platform_last_platform', 'https://api.302.ai/v1'); - form.glossary_agent_to_lang = get('glossary_agent_to_lang', 'Simplified Chinese'); - form.glossary_agent_custom_to_lang = get('glossary_agent_custom_to_lang', ''); - form.glossary_agent_chunk_size = getNum('glossary_agent_chunk_size', 1000); - form.glossary_agent_concurrent = getNum('glossary_agent_concurrent', 5); - form.glossary_agent_temperature = getNum('glossary_agent_temperature', 0.7); - form.glossary_agent_retry = getNum('glossary_agent_retry', 3); - form.glossary_agent_thinking = get('glossary_agent_thinking_mode', 'default'); - form.glossary_agent_system_proxy_enable = getBool('glossary_agent_system_proxy_enable', false); - form.glossary_agent_force_json = getBool('glossary_agent_force_json', false); - form.glossary_agent_rpm = getNumOrNull('glossary_agent_rpm'); // Load Glossary RPM - form.glossary_agent_tpm = getNumOrNull('glossary_agent_tpm'); // Load Glossary TPM - - // Determine Glossary Provider - const gPlatObj = KNOWN_PLATFORMS.find(p => p.val === form.glossary_agent_platform); - if (form.glossary_agent_platform === 'custom') { - form.glossary_agent_provider = get('glossary_agent_platform_custom_provider', 'default'); - } else { - form.glossary_agent_provider = gPlatObj ? gPlatObj.provider : ''; - } - // Restore workflow specific params ['txt', 'xlsx', 'docx', 'srt', 'epub', 'html', 'ass', 'pptx'].forEach(t => { workflowParams[t].insert_mode = get(`translator_${t}_insert_mode`, 'replace'); @@ -1574,7 +1390,6 @@ // Trigger platform updates to load API keys/models updatePlatformParams(form.platform, 'translator_platform', form); - updatePlatformParams(form.glossary_agent_platform, 'glossary_agent_platform', form, true); }; // --- 新增:专门用于将当前 form 数据全部写入 localStorage 的函数 --- @@ -1626,30 +1441,7 @@ s('translator_provider', f.provider); if (f.platform === 'custom') s('translator_platform_custom_base_url', f.base_url); - // 2. 术语表相关 - s('glossary_generate_enable', f.glossary_generate_enable); - s('glossary_agent_custom_prompt', f.glossary_agent_custom_prompt); - s('glossary_agent_config_choice', f.glossary_agent_config_choice); - s('glossary_agent_platform_last_platform', f.glossary_agent_platform); - s('glossary_agent_to_lang', f.glossary_agent_to_lang); - s('glossary_agent_custom_to_lang', f.glossary_agent_custom_to_lang); - s('glossary_agent_chunk_size', f.glossary_agent_chunk_size); - s('glossary_agent_concurrent', f.glossary_agent_concurrent); - s('glossary_agent_temperature', f.glossary_agent_temperature); - s('glossary_agent_retry', f.glossary_agent_retry); - s('glossary_agent_thinking_mode', f.glossary_agent_thinking); - s('glossary_agent_system_proxy_enable', f.glossary_agent_system_proxy_enable); - s('glossary_agent_force_json', f.glossary_agent_force_json); - s('glossary_agent_rpm', f.glossary_agent_rpm || ''); - s('glossary_agent_tpm', f.glossary_agent_tpm || ''); - - // 术语表平台 Key - s(`glossary_agent_platform_${f.glossary_agent_platform}_apikey`, f.glossary_agent_key); - s(`glossary_agent_platform_${f.glossary_agent_platform}_model_id`, f.glossary_agent_model_id); - s('glossary_agent_provider', f.glossary_agent_provider); - if (f.glossary_agent_platform === 'custom') s('glossary_agent_platform_custom_base_url', f.glossary_agent_baseurl); - - // 3. 自动循环保存所有具体工作流参数 (txt, docx, xlsx...) + // 2. 自动循环保存所有具体工作流参数 (txt, docx, xlsx...) for (const [wfType, params] of Object.entries(workflowParams)) { for (const [key, val] of Object.entries(params)) { s(`translator_${wfType}_${key}`, val); @@ -1657,27 +1449,17 @@ } }; - const updatePlatformParams = (plat, prefix, target, isGlossary = false) => { + const updatePlatformParams = (plat, prefix, target) => { const get = (k) => localStorage.getItem(k) || ''; - if (isGlossary) { - target.glossary_agent_key = get(`${prefix}_${plat}_apikey`); - target.glossary_agent_model_id = get(`${prefix}_${plat}_model_id`); - target.glossary_agent_baseurl = plat === 'custom' ? get(`${prefix}_custom_base_url`) : plat; - } else { - target.api_key = get(`${prefix}_${plat}_apikey`); - target.model_id = get(`${prefix}_${plat}_model_id`); - target.base_url = plat === 'custom' ? get(`${prefix}_custom_base_url`) : plat; - } + target.api_key = get(`${prefix}_${plat}_apikey`); + target.model_id = get(`${prefix}_${plat}_model_id`); + target.base_url = plat === 'custom' ? get(`${prefix}_custom_base_url`) : plat; }; watch(() => form.platform, (n) => { updatePlatformParams(n, 'translator_platform', form); }); - watch(() => form.glossary_agent_platform, (n) => { - updatePlatformParams(n, 'glossary_agent_platform', form, true); - }); - const t = (k) => { const dict = i18nData.value[currentLang.value] || i18nData.value['zh'] || {}; return dict[k] || k; @@ -1751,12 +1533,11 @@ // Dynamic Step Numbering const stepMap = computed(() => { let step = 2; - const map = {specific: 0, parsing: 0, ai: 0, trans: 0, glossary: 0}; + const map = {specific: 0, parsing: 0, ai: 0, trans: 0}; if (currentWorkflowConfig.value) map.specific = step++; if (form.workflow_type === 'markdown_based') map.parsing = step++; map.ai = step++; if (!form.skip_translate) map.trans = step++; - map.glossary = step++; return map; }); @@ -1875,33 +1656,11 @@ glossary_dict: Object.keys(glossaryData.value).length ? glossaryData.value : null, system_proxy_enable: form.system_proxy_enable, force_json: form.force_json, - glossary_generate_enable: form.glossary_generate_enable, workflow_type: form.workflow_type, rpm: emptyToNull(form.rpm), tpm: emptyToNull(form.tpm) }; - // Agent Config - if (basePayload.glossary_generate_enable) { - const isCustom = form.glossary_agent_config_choice === 'custom'; - basePayload.glossary_agent_config = { - base_url: isCustom ? emptyToNull(form.glossary_agent_baseurl) : basePayload.base_url, - api_key: isCustom ? (form.glossary_agent_key || "") : basePayload.api_key, - model_id: isCustom ? emptyToNull(form.glossary_agent_model_id) : basePayload.model_id, - provider: isCustom ? emptyToNull(form.glossary_agent_provider) : basePayload.provider, // Add provider - to_lang: isCustom ? (form.glossary_agent_to_lang === 'custom' ? form.glossary_agent_custom_to_lang : form.glossary_agent_to_lang) : basePayload.to_lang, - custom_prompt: emptyToNull(form.glossary_agent_custom_prompt), - temperature: isCustom ? Number(form.glossary_agent_temperature) : basePayload.temperature, - concurrent: isCustom ? Number(form.glossary_agent_concurrent) : basePayload.concurrent, - retry: isCustom ? Number(form.glossary_agent_retry) : basePayload.retry, - thinking: isCustom ? form.glossary_agent_thinking : basePayload.thinking, - system_proxy_enable: isCustom ? form.glossary_agent_system_proxy_enable : basePayload.system_proxy_enable, - chunk_size: isCustom ? Number(form.glossary_agent_chunk_size) : basePayload.chunk_size, - force_json: isCustom ? form.glossary_agent_force_json : basePayload.force_json, - rpm: isCustom ? emptyToNull(form.glossary_agent_rpm) : basePayload.rpm, - tpm: isCustom ? emptyToNull(form.glossary_agent_tpm) : basePayload.tpm - }; - } // Specific Workflow Params if (form.workflow_type === 'markdown_based') { @@ -2377,13 +2136,31 @@ // Backend Metadata try { - const [metaRes, enginRes, paramsRes] = await Promise.all([ - fetch("/service/meta"), fetch('/service/engin-list'), fetch("/service/default-params") + const [metaRes, enginRes, paramsRes, configRes] = await Promise.all([ + fetch("/service/meta"), fetch('/service/engin-list'), fetch("/service/default-params"), + fetch("/api/config") ]); const meta = await metaRes.json(); version.value = meta.version; enginList.value = await enginRes.json(); Object.assign(defaultParams, await paramsRes.json()); + const envConfig = await configRes.json().catch(() => ({})); + // 将服务端环境变量作为 localStorage 未设置时的回退默认值 + if (envConfig.base_url && !localStorage.getItem('translator_platform_custom_base_url')) { + localStorage.setItem('translator_platform_custom_base_url', envConfig.base_url); + } + if (envConfig.api_key && !localStorage.getItem('translator_platform_custom_apikey')) { + localStorage.setItem('translator_platform_custom_apikey', envConfig.api_key); + } + if (envConfig.model_id && !localStorage.getItem('translator_platform_custom_model_id')) { + localStorage.setItem('translator_platform_custom_model_id', envConfig.model_id); + } + if (envConfig.rpm != null && !localStorage.getItem('rpm')) { + localStorage.setItem('rpm', String(envConfig.rpm)); + } + if (envConfig.tpm != null && !localStorage.getItem('tpm')) { + localStorage.setItem('tpm', String(envConfig.tpm)); + } } catch (e) { console.error("Backend init failed", e); } diff --git a/docutranslate/translator/ai_translator/md_translator.py b/docutranslate/translator/ai_translator/md_translator.py index 5b927eb..295c9f4 100644 --- a/docutranslate/translator/ai_translator/md_translator.py +++ b/docutranslate/translator/ai_translator/md_translator.py @@ -31,6 +31,7 @@ class MDTranslator(AiTranslator): if not self.skip_translate: agent_config = MDTranslateAgentConfig(custom_prompt=config.custom_prompt, to_lang=config.to_lang, + source_lang=config.source_lang, base_url=config.base_url, api_key=config.api_key, model_id=config.model_id, diff --git a/pyproject.toml b/pyproject.toml index 52a13b6..010ba91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "pypdf>=6.4.2", "regex>=2025.11.3", "charset-normalizer>=3.4.4", + "python-dotenv>=1.0.0", ] dynamic = ["version"] diff --git a/uv.lock b/uv.lock index ff2874d..402c3a1 100644 --- a/uv.lock +++ b/uv.lock @@ -383,6 +383,7 @@ dependencies = [ { name = "pypdf" }, { name = "pysubs2" }, { name = "python-docx" }, + { name = "python-dotenv" }, { name = "python-pptx" }, { name = "regex" }, { name = "srt" }, @@ -422,6 +423,7 @@ requires-dist = [ { name = "pypdf", specifier = ">=6.4.2" }, { name = "pysubs2", specifier = ">=1.8.0" }, { name = "python-docx", specifier = ">=1.2.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "python-pptx", specifier = ">=1.0.2" }, { name = "regex", specifier = ">=2025.11.3" }, { name = "srt", specifier = ">=3.5.3" }, @@ -1738,14 +1740,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906 }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607 }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769 }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980 }, { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865 }, { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256 },