AiTranslatorConfig添加skip_translate参数

This commit is contained in:
xunbu
2025-08-28 20:35:36 +08:00
parent 7e1fb345c6
commit 7d71e6382c
11 changed files with 237 additions and 150 deletions

View File

@@ -52,7 +52,7 @@
"chunkSizeLabel": "分块大小",
"resetBtn": "重置",
"concurrentLabel": "并发数",
"glossaryGenTitle": "5. 术语表",
"glossaryGenTitle": "术语表",
"glossaryLabel": "术语表 (可选)",
"glossaryHelp": "选择一个或多个CSV文件。文件需包含'src'和'dst'两列标题,分别代表原文和译文。",
"viewGlossaryBtn": "查看术语表",
@@ -206,7 +206,7 @@
"chunkSizeLabel": "Chunk Size",
"resetBtn": "Reset",
"concurrentLabel": "Concurrency",
"glossaryGenTitle": "5. Glossary",
"glossaryGenTitle": "Glossary",
"glossaryLabel": "Glossary (Optional)",
"glossaryHelp": "Select one or more CSV files. Files must have 'src' and 'dst' headers, representing source and destination text respectively.",
"viewGlossaryBtn": "View Glossary",

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
from abc import abstractmethod
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import TypeVar
from docutranslate.agents.agent import ThinkingMode
@@ -10,10 +10,10 @@ from docutranslate.translator.base import Translator, TranslatorConfig
@dataclass(kw_only=True)
class AiTranslatorConfig(TranslatorConfig):
base_url: str
api_key: str
model_id: str
to_lang: str
base_url: str | None = field(default=None,metadata={"description": "OpenAI兼容地址当skip_translate为False时为必填项"})
api_key: str | None = field(default=None,metadata={"description": "当skip_translate为False时为必填项"})
model_id: str | None = field(default=None,metadata={"description": "当skip_translate为False时为必填项"})
to_lang: str = "简体中文"
custom_prompt: str | None = None
temperature: float = 0.7
thinking: ThinkingMode = "default"
@@ -23,6 +23,7 @@ class AiTranslatorConfig(TranslatorConfig):
glossary_dict: dict[str:str] | None = None
glossary_generate_enable: bool = False
glossary_agent_config: GlossaryAgentConfig | None = None
skip_translate: bool = False
T = TypeVar('T', bound=Document)
@@ -35,8 +36,12 @@ class AiTranslator(Translator[T]):
def __init__(self, config: AiTranslatorConfig):
super().__init__(config=config)
self.skip_translate = config.skip_translate
self.glossary_agent = None
self.glossary_dict_gen = None
if not self.skip_translate and (config.base_url is None or config.api_key is None or config.model_id is None):
raise ValueError("skip_translate不为false时base_url、api_key、model_id为必填项")
if config.glossary_generate_enable:
if config.glossary_agent_config:
self.glossary_agent = GlossaryAgent(config.glossary_agent_config)
@@ -53,6 +58,7 @@ class AiTranslator(Translator[T]):
logger=self.logger,
)
self.glossary_agent = GlossaryAgent(glossary_agent_config)
@abstractmethod
def translate(self, document: T) -> Document:
...

View File

@@ -37,6 +37,8 @@ class DocxTranslator(AiTranslator):
def __init__(self, config: DocxTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
@@ -160,10 +162,14 @@ class DocxTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# 调用翻译 agent
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# 将翻译结果写回文档
document.content = self._after_translate(doc, elements_to_translate, translated_texts, original_texts)
@@ -184,11 +190,14 @@ class DocxTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# 异步调用翻译 agent
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# 将翻译结果写回文档
document.content = await asyncio.to_thread(self._after_translate, doc, elements_to_translate, translated_texts,
original_texts)

View File

@@ -28,11 +28,19 @@ class EpubTranslator(AiTranslator):
def __init__(self, config: EpubTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt, to_lang=config.to_lang,
baseurl=config.base_url, key=config.api_key, model_id=config.model_id,
temperature=config.temperature, thinking=config.thinking,
max_concurrent=config.concurrent, timeout=config.timeout, logger=self.logger,
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
baseurl=config.base_url,
key=config.api_key,
model_id=config.model_id,
temperature=config.temperature,
thinking=config.thinking,
max_concurrent=config.concurrent,
timeout=config.timeout,
logger=self.logger,
glossary_dict=config.glossary_dict
)
self.translate_agent = SegmentsTranslateAgent(agent_config)
@@ -175,8 +183,12 @@ class EpubTranslator(AiTranslator):
return self
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
document.content = self._after_translate(
all_files, items_to_translate, translated_texts, original_texts
)
@@ -195,11 +207,14 @@ class EpubTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(
original_texts, self.chunk_size
)
else:
translated_texts = original_texts
document.content = await asyncio.to_thread(
self._after_translate, all_files, items_to_translate, translated_texts, original_texts
)

View File

@@ -85,6 +85,8 @@ class HtmlTranslator(AiTranslator):
def __init__(self, config: HtmlTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
@@ -199,9 +201,12 @@ class HtmlTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
document.content = self._after_translate(soup, translatable_items, translated_texts, original_texts)
return self
@@ -218,9 +223,12 @@ class HtmlTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(original_texts, self.chunk_size)
else:
translated_texts = original_texts
document.content = await asyncio.to_thread(
self._after_translate, soup, translatable_items, translated_texts, original_texts
)

View File

@@ -18,7 +18,10 @@ class JsonTranslator(AiTranslator):
def __init__(self, config: JsonTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
agent_config = SegmentsTranslateAgentConfig(custom_prompt=config.custom_prompt,
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
baseurl=config.base_url,
key=config.api_key,
@@ -28,7 +31,8 @@ class JsonTranslator(AiTranslator):
max_concurrent=config.concurrent,
timeout=config.timeout,
logger=self.logger,
glossary_dict=config.glossary_dict)
glossary_dict=config.glossary_dict
)
self.translate_agent = SegmentsTranslateAgent(agent_config)
self.jsonpaths = config.json_paths
@@ -76,10 +80,13 @@ class JsonTranslator(AiTranslator):
original_texts = [match.value for match in all_matches]
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# 步骤 2: 批量翻译提取出的文本
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# 健壮性检查:确保翻译回来的项目数量与发送的一致
if len(original_texts) != len(translated_texts):
raise ValueError("翻译服务返回的项目数量与发送的数量不匹配。")
@@ -107,11 +114,14 @@ class JsonTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# 步骤 2: 批量翻译提取出的文本
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# 健壮性检查:确保翻译回来的项目数量与发送的一致
if len(original_texts) != len(translated_texts):
raise ValueError("翻译服务返回的项目数量与发送的数量不匹配。")

View File

@@ -19,6 +19,8 @@ class MDTranslator(AiTranslator):
def __init__(self, config: MDTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent = None
if not self.skip_translate:
agent_config = MDTranslateAgentConfig(custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
baseurl=config.base_url,
@@ -38,9 +40,13 @@ class MDTranslator(AiTranslator):
chunks: list[str] = split_markdown_text(document.content.decode(), self.chunk_size)
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(chunks, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
self.logger.info(f"markdown分为{len(chunks)}")
if self.translate_agent:
result: list[str] = self.translate_agent.send_chunks(chunks)
else:
result = chunks
content = join_markdown_texts(result)
# 做一些加强鲁棒性的操作
content = content.replace(r'\', r'\(')
@@ -57,10 +63,14 @@ class MDTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(chunks, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
self.logger.info(f"markdown分为{len(chunks)}")
if self.translate_agent:
result: list[str] = await self.translate_agent.send_chunks_async(chunks)
else:
result = chunks
def run():
content = join_markdown_texts(result)

View File

@@ -24,6 +24,8 @@ class SrtTranslator(AiTranslator):
def __init__(self, config: SrtTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
@@ -109,10 +111,13 @@ class SrtTranslator(AiTranslator):
return self
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# --- 步骤 2: 调用翻译Agent ---
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# --- 步骤 3: 后处理并更新文档内容 ---
document.content = self._after_translate(subtitles, translated_texts, original_texts)
return self
@@ -130,11 +135,14 @@ class SrtTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# --- 步骤 2: 调用翻译Agent (异步) ---
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(original_texts, self.chunk_size)
else:
translated_texts = original_texts
# --- 步骤 3: 后处理并更新文档内容 (I/O密集型) ---
document.content = await asyncio.to_thread(
self._after_translate, subtitles, translated_texts, original_texts

View File

@@ -16,6 +16,8 @@ class TXTTranslator(AiTranslator):
def __init__(self, config: TXTTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
self.translate_agent =None
if not self.skip_translate:
agent_config = TXTTranslateAgentConfig(custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
baseurl=config.base_url,
@@ -34,9 +36,13 @@ class TXTTranslator(AiTranslator):
chunks: list[str] = split_markdown_text(document.content.decode(), max_block_size=self.chunk_size)
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(chunks, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
self.logger.info(f"txt分为{len(chunks)}")
if self.translate_agent:
result: list[str] = self.translate_agent.send_chunks(chunks)
else:
result=chunks
content = "\n".join(result)
document.content = content.encode()
self.logger.info("翻译完成")
@@ -48,10 +54,14 @@ class TXTTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(chunks, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
self.logger.info(f"txt分为{len(chunks)}")
if self.translate_agent:
result: list[str] = await self.translate_agent.send_chunks_async(chunks)
else:
result=chunks
content = "\n".join(result)
document.content = content.encode()
self.logger.info("翻译完成")

View File

@@ -26,7 +26,10 @@ class XlsxTranslator(AiTranslator):
def __init__(self, config: XlsxTranslatorConfig):
super().__init__(config=config)
self.chunk_size = config.chunk_size
agent_config = SegmentsTranslateAgentConfig(custom_prompt=config.custom_prompt,
self.translate_agent = None
if not self.skip_translate:
agent_config = SegmentsTranslateAgentConfig(
custom_prompt=config.custom_prompt,
to_lang=config.to_lang,
baseurl=config.base_url,
key=config.api_key,
@@ -36,7 +39,8 @@ class XlsxTranslator(AiTranslator):
max_concurrent=config.concurrent,
timeout=config.timeout,
logger=self.logger,
glossary_dict=config.glossary_dict)
glossary_dict=config.glossary_dict
)
self.translate_agent = SegmentsTranslateAgent(agent_config)
self.insert_mode = config.insert_mode
self.separator = config.separator
@@ -156,9 +160,13 @@ class XlsxTranslator(AiTranslator):
return self
if self.glossary_agent:
self.glossary_dict_gen = self.glossary_agent.send_segments(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# --- 步骤 2: 调用翻译函数 ---
if self.translate_agent:
translated_texts = self.translate_agent.send_segments(original_texts, self.chunk_size)
else:
translated_texts = original_texts
document.content = self._after_translate(workbook, cells_to_translate, translated_texts, original_texts)
return self
@@ -173,11 +181,14 @@ class XlsxTranslator(AiTranslator):
if self.glossary_agent:
self.glossary_dict_gen = await self.glossary_agent.send_segments_async(original_texts, self.chunk_size)
if self.translate_agent:
self.translate_agent.update_glossary_dict(self.glossary_dict_gen)
# --- 步骤 2: 调用翻译函数 ---
if self.translate_agent:
translated_texts = await self.translate_agent.send_segments_async(original_texts, self.chunk_size)
else:
translated_texts = original_texts
document.content = await asyncio.to_thread(self._after_translate, workbook, cells_to_translate,
translated_texts, original_texts)
return self