统一接口名称
This commit is contained in:
1 parent
59d1edc258
commit
52a8302959
14 files changed
+37
-28
No files matched your search
@@ -50,6 +50,7 @@ class AgentConfig:
|
||||
thinking: ThinkingMode = "disable"
|
||||
retry: int = 2
|
||||
system_proxy_enable: bool = False
|
||||
force_json: bool = False # 应输出json格式时强制ai输出json
|
||||
|
||||
|
||||
class TotalErrorCounter:
|
||||
@@ -267,7 +268,7 @@ class Agent:
|
||||
system_prompt: None | str = None,
|
||||
retry=True,
|
||||
retry_count=0,
|
||||
json_format=False,
|
||||
force_json=False,
|
||||
pre_send_handler: PreSendHandlerType = None,
|
||||
result_handler: ResultHandlerType = None,
|
||||
error_result_handler: ErrorResultHandlerType = None,
|
||||
@@ -279,7 +280,7 @@ class Agent:
|
||||
system_prompt, prompt = pre_send_handler(system_prompt, prompt)
|
||||
# print(f"system_prompt:\n{system_prompt}")
|
||||
# print(f"【测试】prompt:\n{prompt}")
|
||||
headers, data = self._prepare_request_data(prompt, system_prompt,json_format=json_format)
|
||||
headers, data = self._prepare_request_data(prompt, system_prompt, json_format=force_json)
|
||||
should_retry = False
|
||||
is_hard_error = False # 新增标志,用于区分是否为硬错误
|
||||
current_partial_result = None
|
||||
@@ -392,6 +393,7 @@ class Agent:
|
||||
system_prompt,
|
||||
retry=True,
|
||||
retry_count=retry_count + 1,
|
||||
force_json=force_json,
|
||||
pre_send_handler=pre_send_handler,
|
||||
result_handler=result_handler,
|
||||
error_result_handler=error_result_handler,
|
||||
@@ -419,7 +421,7 @@ class Agent:
|
||||
prompts: list[str],
|
||||
system_prompt: str | None = None,
|
||||
max_concurrent: int | None = None,
|
||||
json_format=False,
|
||||
force_json=False,
|
||||
pre_send_handler: PreSendHandlerType = None,
|
||||
result_handler: ResultHandlerType = None,
|
||||
error_result_handler: ErrorResultHandlerType = None,
|
||||
@@ -429,7 +431,7 @@ class Agent:
|
||||
)
|
||||
total = len(prompts)
|
||||
self.logger.info(
|
||||
f"base-url:{self.baseurl},model-id:{self.model_id},concurrent:{max_concurrent},temperature:{self.temperature},system_proxy:{self.system_proxy_enable},json_output:{json_format}"
|
||||
f"base-url:{self.baseurl},model-id:{self.model_id},concurrent:{max_concurrent},temperature:{self.temperature},system_proxy:{self.system_proxy_enable},json_output:{force_json}"
|
||||
)
|
||||
self.logger.info(f"预计发送{total}个请求,并发请求数:{max_concurrent}")
|
||||
self.total_error_counter.max_errors_count = (
|
||||
@@ -462,7 +464,7 @@ class Agent:
|
||||
client=client,
|
||||
prompt=p_text,
|
||||
system_prompt=system_prompt,
|
||||
json_format=json_format,
|
||||
force_json=force_json,
|
||||
pre_send_handler=pre_send_handler,
|
||||
result_handler=result_handler,
|
||||
error_result_handler=error_result_handler,
|
||||
@@ -503,7 +505,7 @@ class Agent:
|
||||
system_prompt: None | str = None,
|
||||
retry=True,
|
||||
retry_count=0,
|
||||
json_format=False,
|
||||
force_json=False,
|
||||
pre_send_handler=None,
|
||||
result_handler=None,
|
||||
error_result_handler=None,
|
||||
@@ -514,7 +516,7 @@ class Agent:
|
||||
if pre_send_handler:
|
||||
system_prompt, prompt = pre_send_handler(system_prompt, prompt)
|
||||
|
||||
headers, data = self._prepare_request_data(prompt, system_prompt,json_format=json_format)
|
||||
headers, data = self._prepare_request_data(prompt, system_prompt, json_format=force_json)
|
||||
should_retry = False
|
||||
is_hard_error = False # 新增标志,用于区分是否为硬错误
|
||||
current_partial_result = None
|
||||
@@ -621,6 +623,7 @@ class Agent:
|
||||
system_prompt,
|
||||
retry=True,
|
||||
retry_count=retry_count + 1,
|
||||
force_json=force_json,
|
||||
pre_send_handler=pre_send_handler,
|
||||
result_handler=result_handler,
|
||||
error_result_handler=error_result_handler,
|
||||
@@ -648,7 +651,7 @@ class Agent:
|
||||
client: httpx.Client,
|
||||
prompt: str,
|
||||
system_prompt: None | str,
|
||||
json_format,
|
||||
force_json,
|
||||
count: PromptsCounter,
|
||||
pre_send_handler,
|
||||
result_handler,
|
||||
@@ -658,7 +661,7 @@ class Agent:
|
||||
client,
|
||||
prompt,
|
||||
system_prompt,
|
||||
json_format=json_format,
|
||||
force_json=force_json,
|
||||
pre_send_handler=pre_send_handler,
|
||||
result_handler=result_handler,
|
||||
error_result_handler=error_result_handler,
|
||||
|
||||
@@ -66,16 +66,18 @@ def get_target_segments(result: str):
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(kw_only=True)
|
||||
class GlossaryAgentConfig(AgentConfig):
|
||||
to_lang: str
|
||||
custom_prompt: str = None
|
||||
force_json: bool = False
|
||||
|
||||
|
||||
class GlossaryAgent(Agent):
|
||||
def __init__(self, config: GlossaryAgentConfig):
|
||||
super().__init__(config)
|
||||
self.to_lang = config.to_lang
|
||||
self.force_json=config.force_json
|
||||
self.system_prompt = f"""
|
||||
# Role
|
||||
You are a professional glossary extractor
|
||||
@@ -114,7 +116,7 @@ You are a professional glossary extractor
|
||||
result = {}
|
||||
indexed_originals, chunks, merged_indices_list = segments2json_chunks(segments, chunk_size)
|
||||
prompts = [generate_prompt(json.dumps(chunk, ensure_ascii=False), self.to_lang) for chunk in chunks]
|
||||
translated_chunks = super().send_prompts(prompts=prompts,
|
||||
translated_chunks = super().send_prompts(prompts=prompts, json_format=self.force_json,
|
||||
result_handler=self._result_handler,
|
||||
error_result_handler=self._error_result_handler)
|
||||
for chunk in translated_chunks:
|
||||
@@ -138,7 +140,7 @@ You are a professional glossary extractor
|
||||
indexed_originals, chunks, merged_indices_list = await asyncio.to_thread(segments2json_chunks, segments,
|
||||
chunk_size)
|
||||
prompts = [generate_prompt(json.dumps(chunk, ensure_ascii=False), self.to_lang) for chunk in chunks]
|
||||
translated_chunks = await super().send_prompts_async(prompts=prompts,
|
||||
translated_chunks = await super().send_prompts_async(prompts=prompts, force_json=self.force_json,
|
||||
result_handler=self._result_handler,
|
||||
error_result_handler=self._error_result_handler)
|
||||
for chunk in translated_chunks:
|
||||
|
||||
@@ -90,19 +90,19 @@ def get_target_segments(result: str):
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(kw_only=True)
|
||||
class SegmentsTranslateAgentConfig(AgentConfig):
|
||||
to_lang: str
|
||||
custom_prompt: str | None = None
|
||||
glossary_dict: dict[str, str] | None = None
|
||||
json_format:bool = False
|
||||
force_json:bool = False
|
||||
|
||||
|
||||
class SegmentsTranslateAgent(Agent):
|
||||
def __init__(self, config: SegmentsTranslateAgentConfig):
|
||||
super().__init__(config)
|
||||
self.to_lang = config.to_lang
|
||||
self.json_format = config.json_format
|
||||
self.force_json = config.force_json
|
||||
self.system_prompt = f"""
|
||||
# Role
|
||||
- You are a professional, authentic machine translation engine.
|
||||
@@ -199,7 +199,7 @@ class SegmentsTranslateAgent(Agent):
|
||||
def send_segments(self, segments: list[str], chunk_size: int) -> list[str]:
|
||||
indexed_originals, chunks, merged_indices_list = segments2json_chunks(segments, chunk_size)
|
||||
prompts = [generate_prompt(json.dumps(chunk, ensure_ascii=False, indent=0), self.to_lang) for chunk in chunks]
|
||||
translated_chunks = super().send_prompts(prompts=prompts, json_format=self.json_format,
|
||||
translated_chunks = super().send_prompts(prompts=prompts, json_format=self.force_json,
|
||||
pre_send_handler=self._pre_send_handler,
|
||||
result_handler=self._result_handler,
|
||||
error_result_handler=self._error_result_handler)
|
||||
@@ -238,7 +238,7 @@ class SegmentsTranslateAgent(Agent):
|
||||
chunk_size)
|
||||
prompts = [generate_prompt(json.dumps(chunk, ensure_ascii=False, indent=0), self.to_lang) for chunk in chunks]
|
||||
|
||||
translated_chunks = await super().send_prompts_async(prompts=prompts, json_format=self.json_format,
|
||||
translated_chunks = await super().send_prompts_async(prompts=prompts, force_json=self.force_json,
|
||||
pre_send_handler=self._pre_send_handler,
|
||||
result_handler=self._result_handler,
|
||||
error_result_handler=self._error_result_handler)
|
||||
|
||||
@@ -207,7 +207,7 @@ async def lifespan(app: FastAPI):
|
||||
global_logger.propagate = False
|
||||
global_logger.setLevel(logging.INFO)
|
||||
print("应用启动完成,多任务状态已初始化。")
|
||||
print(f"服务接口文档: http://1227.0.0.1:{app.state.port_to_use}/docs")
|
||||
print(f"服务接口文档: http://127.0.0.1:{app.state.port_to_use}/docs")
|
||||
print(f"请用浏览器访问 http://127.0.0.1:{app.state.port_to_use}\n")
|
||||
yield
|
||||
# 清理任何可能残留的临时目录
|
||||
@@ -311,6 +311,9 @@ class GlossaryAgentConfigPayload(BaseModel):
|
||||
custom_prompt: Optional[str] = Field(
|
||||
default=None, description="生成术语表的用户自定义提示词"
|
||||
)
|
||||
force_json: bool = Field(
|
||||
default=False, description="强制Agent输出JSON格式的术语表。"
|
||||
)
|
||||
|
||||
|
||||
# 1. 定义所有工作流共享的基础参数
|
||||
@@ -736,6 +739,7 @@ class TranslateServiceRequest(BaseModel):
|
||||
"timeout": default_params["timeout"],
|
||||
"thinking": "default",
|
||||
"retry": default_params["retry"],
|
||||
"force_json": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html lang="zh-CN" data-bs-theme="auto">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n-title="pageTitle">DocuTranslate - 交互式文档翻译</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<!-- Bootstrap CSS -->
<link href="/static/bootstrap.css" rel="stylesheet" crossorigin="anonymous">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="/static/bootstrap-icons.css">
<style>
body {
background-color: var(--bs-body-bg);
}
button:focus {
outline: none !important;
box-shadow: none !important;
}
.main-container {
display: flex;
flex-direction: column;
height: 100vh;
padding-top: 1rem;
padding-bottom: 1rem;
}
.settings-panel {
height: calc(100vh - 2rem);
overflow-y: auto;
padding-right: 15px; /* for scrollbar */
}
.task-area {
height: calc(100vh - 2rem);
overflow-y: auto;
}
.task-card {
transition: all 0.3s ease-in-out;
}
.task-id-placeholder {
color: var(--bs-secondary-color);
font-style: italic;
}
.log-area {
height: 150px;
background-color: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
border-radius: .375rem;
font-family: monospace;
font-size: 0.8rem;
padding: 10px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
/* [NEW] Wrapper for log area to position the copy button */
.log-area-wrapper {
position: relative;
}
/* [NEW] Styles for the copy button */
.copy-log-btn {
position: absolute;
bottom: 5px;
right: 5px;
z-index: 10;
opacity: 0.5;
transition: opacity 0.2s ease-in-out, background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
padding: 0.1rem 0.4rem;
line-height: 1;
}
.copy-log-btn:hover {
opacity: 1;
}
.file-drop-area {
border: 2px dashed var(--bs-secondary-bg);
border-radius: .375rem;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease-in-out;
background-color: var(--bs-body-bg);
}
.file-drop-area.drag-over {
border-color: var(--bs-primary);
background-color: var(--bs-secondary-bg);
}
.file-drop-area.file-selected {
border-style: solid;
border-color: var(--bs-success);
background-color: var(--bs-success-bg-subtle);
}
.file-drop-area.input-error {
border-color: var(--bs-danger);
}
.file-name-display.input-error-text {
color: var(--bs-danger);
font-weight: bold;
}
#printFrame, #translatedPreviewFrame {
border: none;
width: 100%;
}
#previewOffcanvas {
--bs-offcanvas-width: 95vw;
max-width: 1600px;
}
.preview-split-container {
display: flex;
flex-direction: row;
height: 90%;
}
.preview-pane-wrapper {
display: flex;
flex-direction: column;
overflow: hidden; /* Important for split.js */
}
.preview-pane-wrapper h6 {
flex-shrink: 0;
padding: 0.25rem;
}
.preview-pane-wrapper .preview-pane {
flex-grow: 1; /* Make the inner pane grow */
border: 1px solid var(--bs-border-color);
border-radius: .375rem;
overflow: auto;
}
.gutter {
background-color: var(--bs-tertiary-bg);
background-repeat: no-repeat;
background-position: 50%;
}
.gutter.gutter-horizontal {
cursor: col-resize;
border-left: 1px solid var(--bs-border-color);
border-right: 1px solid var(--bs-border-color);
}
.gutter.gutter-vertical {
cursor: row-resize;
border-top: 1px solid var(--bs-border-color);
border-bottom: 1px solid var(--bs-border-color);
}
.preview-pane iframe {
width: 100%;
height: 100%; /* Make iframe fill the pane completely */
border: none;
margin: 0;
padding: 0;
}
.preview-pane pre {
/* Let the content determine the width, the parent will scroll */
margin: 0;
padding: 1rem; /* Add someLine truncated
|
||||
<html lang="zh-CN" data-bs-theme="auto">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n-title="pageTitle">DocuTranslate - 交互式文档翻译</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<!-- Bootstrap CSS -->
<link href="/static/bootstrap.css" rel="stylesheet" crossorigin="anonymous">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="/static/bootstrap-icons.css">
<style>
body {
background-color: var(--bs-body-bg);
}
button:focus {
outline: none !important;
box-shadow: none !important;
}
.main-container {
display: flex;
flex-direction: column;
height: 100vh;
padding-top: 1rem;
padding-bottom: 1rem;
}
.settings-panel {
height: calc(100vh - 2rem);
overflow-y: auto;
padding-right: 15px; /* for scrollbar */
}
.task-area {
height: calc(100vh - 2rem);
overflow-y: auto;
}
.task-card {
transition: all 0.3s ease-in-out;
}
.task-id-placeholder {
color: var(--bs-secondary-color);
font-style: italic;
}
.log-area {
height: 150px;
background-color: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
border-radius: .375rem;
font-family: monospace;
font-size: 0.8rem;
padding: 10px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
/* [NEW] Wrapper for log area to position the copy button */
.log-area-wrapper {
position: relative;
}
/* [NEW] Styles for the copy button */
.copy-log-btn {
position: absolute;
bottom: 5px;
right: 5px;
z-index: 10;
opacity: 0.5;
transition: opacity 0.2s ease-in-out, background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
padding: 0.1rem 0.4rem;
line-height: 1;
}
.copy-log-btn:hover {
opacity: 1;
}
.file-drop-area {
border: 2px dashed var(--bs-secondary-bg);
border-radius: .375rem;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease-in-out;
background-color: var(--bs-body-bg);
}
.file-drop-area.drag-over {
border-color: var(--bs-primary);
background-color: var(--bs-secondary-bg);
}
.file-drop-area.file-selected {
border-style: solid;
border-color: var(--bs-success);
background-color: var(--bs-success-bg-subtle);
}
.file-drop-area.input-error {
border-color: var(--bs-danger);
}
.file-name-display.input-error-text {
color: var(--bs-danger);
font-weight: bold;
}
#printFrame, #translatedPreviewFrame {
border: none;
width: 100%;
}
#previewOffcanvas {
--bs-offcanvas-width: 95vw;
max-width: 1600px;
}
.preview-split-container {
display: flex;
flex-direction: row;
height: 90%;
}
.preview-pane-wrapper {
display: flex;
flex-direction: column;
overflow: hidden; /* Important for split.js */
}
.preview-pane-wrapper h6 {
flex-shrink: 0;
padding: 0.25rem;
}
.preview-pane-wrapper .preview-pane {
flex-grow: 1; /* Make the inner pane grow */
border: 1px solid var(--bs-border-color);
border-radius: .375rem;
overflow: auto;
}
.gutter {
background-color: var(--bs-tertiary-bg);
background-repeat: no-repeat;
background-position: 50%;
}
.gutter.gutter-horizontal {
cursor: col-resize;
border-left: 1px solid var(--bs-border-color);
border-right: 1px solid var(--bs-border-color);
}
.gutter.gutter-vertical {
cursor: row-resize;
border-top: 1px solid var(--bs-border-color);
border-bottom: 1px solid var(--bs-border-color);
}
.preview-pane iframe {
width: 100%;
height: 100%; /* Make iframe fill the pane completely */
border: none;
margin: 0;
padding: 0;
}
.preview-pane pre {
/* Let the content determine the width, the parent will scroll */
margin: 0;
padding: 1rem; /* Add someLine truncated
|
||||
@@ -40,7 +40,7 @@ class AssTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -26,7 +26,6 @@ class AiTranslatorConfig(TranslatorConfig, AgentConfig):
|
||||
glossary_generate_enable: bool = False
|
||||
glossary_agent_config: GlossaryAgentConfig | None = None
|
||||
skip_translate: bool = False # 当skip_translate为False时base_url、model_id为必填项
|
||||
force_json:bool=False # 应输出json格式时强制ai输出json
|
||||
|
||||
T = TypeVar("T", bound=Document)
|
||||
|
||||
@@ -64,6 +63,7 @@ class AiTranslator(Translator[T]):
|
||||
logger=self.logger,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
force_json=config.force_json,
|
||||
)
|
||||
self.glossary_agent = GlossaryAgent(glossary_agent_config)
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class DocxTranslator(AiTranslator):
|
||||
api_key=config.api_key, model_id=config.model_id, temperature=config.temperature,
|
||||
thinking=config.thinking, concurrent=config.concurrent, timeout=config.timeout,
|
||||
logger=self.logger, glossary_dict=config.glossary_dict, retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,json_format=config.force_json
|
||||
system_proxy_enable=config.system_proxy_enable, force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -51,7 +51,7 @@ class EpubTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -69,7 +69,7 @@ class HtmlTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -37,7 +37,7 @@ class JsonTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.json_paths = config.json_paths
|
||||
|
||||
@@ -42,7 +42,7 @@ class SrtTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -74,7 +74,7 @@ class TXTTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
@@ -46,7 +46,7 @@ class XlsxTranslator(AiTranslator):
|
||||
glossary_dict=config.glossary_dict,
|
||||
retry=config.retry,
|
||||
system_proxy_enable=config.system_proxy_enable,
|
||||
json_format=config.force_json
|
||||
force_json=config.force_json
|
||||
)
|
||||
self.translate_agent = SegmentsTranslateAgent(agent_config)
|
||||
self.insert_mode = config.insert_mode
|
||||
|
||||
Reference in new issue
Block a user