允许跳过翻译

This commit is contained in:
xunbu
2025-08-28 21:28:04 +08:00
parent b5f769a758
commit 977a6e2c08
2 changed files with 31 additions and 13 deletions

View File

@@ -18,7 +18,7 @@ from fastapi import FastAPI, HTTPException, APIRouter, Body, Path as FastApiPath
from fastapi.openapi.docs import get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, get_redoc_html
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, Field, field_validator, model_validator
from docutranslate import __version__
from docutranslate.agents.agent import ThinkingMode
@@ -235,9 +235,13 @@ class GlossaryAgentConfigPayload(BaseModel):
# 1. 定义所有工作流共享的基础参数
class BaseWorkflowParams(BaseModel):
base_url: str = Field(..., description="LLM API的基础URL。", examples=["https://api.openai.com/v1"])
api_key: str = Field(..., description="LLM API的密钥。", examples=["sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"])
model_id: str = Field(..., description="要使用的LLM模型ID。", examples=["gpt-4o"])
skip_translate: bool = Field(default=False, description="是否跳过翻译步骤。如果为True则仅执行文档解析和格式转换。")
base_url: Optional[str] = Field(default=None, description="LLM API的基础URL。当 `skip_translate` 为 `False` 时必填。",
examples=["https://api.openai.com/v1"])
api_key: Optional[str] = Field(default=None, description="LLM API的密钥。当 `skip_translate` 为 `False` 时必填。",
examples=["sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"])
model_id: Optional[str] = Field(default=None, description="要使用的LLM模型ID。当 `skip_translate` 为 `False` 时必填。",
examples=["gpt-4o"])
to_lang: str = Field(default="中文", description="目标翻译语言。", examples=["简体中文", "English"])
chunk_size: int = Field(default=default_params["chunk_size"], description="文本分割的块大小(字符)。")
concurrent: int = Field(default=default_params["concurrent"], description="并发请求数。")
@@ -256,6 +260,20 @@ class BaseWorkflowParams(BaseModel):
raise ValueError("当 `glossary_generate_enable` 为 `True` 时, `glossary_agent_config` 字段是必须的。")
return v
@model_validator(mode='before')
@classmethod
def check_translation_fields(cls, values):
# 如果不跳过翻译 (值为False或字段不存在),则验证相关字段必须存在且不为空
if not values.get('skip_translate'):
if not values.get('base_url'):
raise ValueError("当 `skip_translate` 为 `False` 时, `base_url` 字段是必须的。")
if not values.get('api_key'):
raise ValueError("当 `skip_translate` 为 `False` 时, `api_key` 字段是必须的。")
if not values.get('model_id'):
raise ValueError("当 `skip_translate` 为 `False` 时, `model_id` 字段是必须的。")
# 如果跳过翻译,则不进行任何检查,允许 base_url 等字段为空
return values
# 2. 为每个工作流创建独立的参数模型
class MarkdownWorkflowParams(BaseWorkflowParams):
@@ -566,7 +584,7 @@ async def _perform_translation(
if isinstance(payload, MarkdownWorkflowParams):
task_logger.info("构建 MarkdownBasedWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent', 'glossary_dict'
}, exclude_none=True)
translator_args['glossary_generate_enable'] = payload.glossary_generate_enable
@@ -592,7 +610,7 @@ async def _perform_translation(
elif isinstance(payload, TextWorkflowParams):
task_logger.info("构建 TXTWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent', 'glossary_dict'
}, exclude_none=True)
translator_args['glossary_generate_enable'] = payload.glossary_generate_enable
@@ -609,7 +627,7 @@ async def _perform_translation(
elif isinstance(payload, JsonWorkflowParams):
task_logger.info("构建 JsonWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent', 'glossary_dict',
'json_paths'
}, exclude_none=True)
@@ -627,7 +645,7 @@ async def _perform_translation(
elif isinstance(payload, XlsxWorkflowParams):
task_logger.info("构建 XlsxWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent',
'insert_mode', 'separator', 'translate_regions', 'glossary_dict'
}, exclude_none=True)
@@ -646,7 +664,7 @@ async def _perform_translation(
elif isinstance(payload, DocxWorkflowParams):
task_logger.info("构建 DocxWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent',
'insert_mode', 'separator', 'glossary_dict'
}, exclude_none=True)
@@ -665,7 +683,7 @@ async def _perform_translation(
elif isinstance(payload, SrtWorkflowParams):
task_logger.info("构建 SrtWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent',
'insert_mode', 'separator', 'glossary_dict'
}, exclude_none=True)
@@ -684,7 +702,7 @@ async def _perform_translation(
elif isinstance(payload, EpubWorkflowParams):
task_logger.info("构建 EpubWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent',
'insert_mode', 'separator', 'glossary_dict'
}, exclude_none=True)
@@ -704,7 +722,7 @@ async def _perform_translation(
elif isinstance(payload, HtmlWorkflowParams):
task_logger.info("构建 HtmlWorkflow 配置。")
translator_args = payload.model_dump(include={
'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'skip_translate', 'base_url', 'api_key', 'model_id', 'to_lang', 'custom_prompt',
'temperature', 'thinking', 'chunk_size', 'concurrent',
'insert_mode', 'separator', 'glossary_dict'
}, exclude_none=True)