记住上传文件的名字
This commit is contained in:
@@ -38,6 +38,7 @@ def _create_default_task_state() -> Dict[str, Any]:
|
|||||||
"download_ready": False, "markdown_content": None, "markdown_zip_content": None,
|
"download_ready": False, "markdown_content": None, "markdown_zip_content": None,
|
||||||
"html_content": None, "original_filename_stem": None, "task_start_time": 0,
|
"html_content": None, "original_filename_stem": None, "task_start_time": 0,
|
||||||
"task_end_time": 0, "current_task_ref": None,
|
"task_end_time": 0, "current_task_ref": None,
|
||||||
|
"original_filename": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -180,6 +181,7 @@ async def _start_translation_task(
|
|||||||
"status_message": "任务初始化中...", "error_flag": False, "download_ready": False,
|
"status_message": "任务初始化中...", "error_flag": False, "download_ready": False,
|
||||||
"markdown_content": None, "md_zip_content": None, "html_content": None,
|
"markdown_content": None, "md_zip_content": None, "html_content": None,
|
||||||
"original_filename_stem": Path(original_filename).stem,
|
"original_filename_stem": Path(original_filename).stem,
|
||||||
|
"original_filename": original_filename,
|
||||||
"task_start_time": time.time(), "task_end_time": 0, "current_task_ref": None,
|
"task_start_time": time.time(), "task_end_time": 0, "current_task_ref": None,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -275,7 +277,8 @@ class TranslateServiceRequest(BaseModel):
|
|||||||
examples=["task-12345"]
|
examples=["task-12345"]
|
||||||
)
|
)
|
||||||
base_url: str = Field(..., description="LLM API的基础URL。", examples=["https://api.openai.com/v1"])
|
base_url: str = Field(..., description="LLM API的基础URL。", examples=["https://api.openai.com/v1"])
|
||||||
apikey: str = Field(..., description="LLM API的密钥。注意:请勿在不安全的环境中暴露此密钥。", examples=["sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"])
|
apikey: str = Field(..., description="LLM API的密钥。注意:请勿在不安全的环境中暴露此密钥。",
|
||||||
|
examples=["sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"])
|
||||||
model_id: str = Field(..., description="使用的模型ID。", examples=["gpt-4-turbo"])
|
model_id: str = Field(..., description="使用的模型ID。", examples=["gpt-4-turbo"])
|
||||||
to_lang: str = Field("中文", description="目标翻译语言。", examples=["中文", "英文", "English"])
|
to_lang: str = Field("中文", description="目标翻译语言。", examples=["中文", "英文", "English"])
|
||||||
formula_ocr: bool = Field(False, description="是否对公式进行OCR识别。")
|
formula_ocr: bool = Field(False, description="是否对公式进行OCR识别。")
|
||||||
@@ -286,9 +289,11 @@ class TranslateServiceRequest(BaseModel):
|
|||||||
chunk_size: int = Field(..., description="文本分块的大小。", examples=[2048])
|
chunk_size: int = Field(..., description="文本分块的大小。", examples=[2048])
|
||||||
concurrent: int = Field(..., description="并发请求的数量。", examples=[5])
|
concurrent: int = Field(..., description="并发请求的数量。", examples=[5])
|
||||||
temperature: float = Field(..., description="LLM的温度参数,控制生成文本的随机性。", examples=[0.7])
|
temperature: float = Field(..., description="LLM的温度参数,控制生成文本的随机性。", examples=[0.7])
|
||||||
custom_prompt_translate: Optional[str] = Field(None, description="用户自定义的翻译Prompt。", examples=["人名保持原文不翻译。"])
|
custom_prompt_translate: Optional[str] = Field(None, description="用户自定义的翻译Prompt。",
|
||||||
|
examples=["人名保持原文不翻译。"])
|
||||||
file_name: str = Field(..., description="上传的原始文件名,包含扩展名。", examples=["my_document.pdf"])
|
file_name: str = Field(..., description="上传的原始文件名,包含扩展名。", examples=["my_document.pdf"])
|
||||||
file_content: str = Field(..., description="Base64编码的文件内容。", examples=["JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAovVHlwZS..."])
|
file_content: str = Field(..., description="Base64编码的文件内容。",
|
||||||
|
examples=["JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAovVHlwZS..."])
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
json_schema_extra = {
|
json_schema_extra = {
|
||||||
@@ -357,7 +362,8 @@ async def service_translate(request: TranslateServiceRequest = Body(..., descrip
|
|||||||
summary="取消翻译任务",
|
summary="取消翻译任务",
|
||||||
description="根据任务ID取消一个正在进行的翻译任务。这是一个异步操作,发送取消请求后,任务不会立即停止,需要通过状态接口确认最终状态。"
|
description="根据任务ID取消一个正在进行的翻译任务。这是一个异步操作,发送取消请求后,任务不会立即停止,需要通过状态接口确认最终状态。"
|
||||||
)
|
)
|
||||||
async def service_cancel_translate(task_id: str = FastApiPath(..., description="要取消的任务的ID", example="task-12345")):
|
async def service_cancel_translate(
|
||||||
|
task_id: str = FastApiPath(..., description="要取消的任务的ID", example="task-12345")):
|
||||||
"""根据任务ID取消一个正在进行的翻译任务。"""
|
"""根据任务ID取消一个正在进行的翻译任务。"""
|
||||||
try:
|
try:
|
||||||
response_data = _cancel_translation_logic(task_id)
|
response_data = _cancel_translation_logic(task_id)
|
||||||
@@ -392,6 +398,7 @@ async def service_get_status(task_id: str = FastApiPath(..., description="要查
|
|||||||
"error_flag": task_state["error_flag"],
|
"error_flag": task_state["error_flag"],
|
||||||
"download_ready": task_state["download_ready"],
|
"download_ready": task_state["download_ready"],
|
||||||
"original_filename_stem": task_state["original_filename_stem"],
|
"original_filename_stem": task_state["original_filename_stem"],
|
||||||
|
"original_filename": task_state.get("original_filename"), # <--- MODIFIED
|
||||||
"task_start_time": task_state["task_start_time"],
|
"task_start_time": task_state["task_start_time"],
|
||||||
"task_end_time": task_state["task_end_time"],
|
"task_end_time": task_state["task_end_time"],
|
||||||
"downloads": {
|
"downloads": {
|
||||||
@@ -423,6 +430,8 @@ async def service_get_logs(task_id: str = FastApiPath(..., description="要获
|
|||||||
|
|
||||||
|
|
||||||
FileType = Literal["markdown", "markdown_zip", "html"]
|
FileType = Literal["markdown", "markdown_zip", "html"]
|
||||||
|
|
||||||
|
|
||||||
@service_router.get(
|
@service_router.get(
|
||||||
"/download/{task_id}/{file_type}",
|
"/download/{task_id}/{file_type}",
|
||||||
summary="下载翻译结果文件",
|
summary="下载翻译结果文件",
|
||||||
@@ -506,13 +515,15 @@ async def main_page_admin():
|
|||||||
summary="[内部] 临时同步翻译接口",
|
summary="[内部] 临时同步翻译接口",
|
||||||
description="一个简单的、同步的翻译接口,用于快速测试。不涉及后台任务、状态管理或多格式输出。**不建议在生产环境中使用。**",
|
description="一个简单的、同步的翻译接口,用于快速测试。不涉及后台任务、状态管理或多格式输出。**不建议在生产环境中使用。**",
|
||||||
tags=["Internal / UI"])
|
tags=["Internal / UI"])
|
||||||
async def temp_translate(base_url: str = Body(..., description="LLM API的基础URL。", example="https://api.openai.com/v1"),
|
async def temp_translate(
|
||||||
|
base_url: str = Body(..., description="LLM API的基础URL。", example="https://api.openai.com/v1"),
|
||||||
api_key: str = Body(..., description="LLM API的密钥。", example="sk-xxxxxxxxxx"),
|
api_key: str = Body(..., description="LLM API的密钥。", example="sk-xxxxxxxxxx"),
|
||||||
model_id: str = Body(..., description="使用的模型ID。", example="gpt-4-turbo"),
|
model_id: str = Body(..., description="使用的模型ID。", example="gpt-4-turbo"),
|
||||||
mineru_token: str = Body(..., description="Mineru引擎的Token。"),
|
mineru_token: str = Body(..., description="Mineru引擎的Token。"),
|
||||||
file_name: str = Body(..., description="原始文件名。", example="test.txt"),
|
file_name: str = Body(..., description="原始文件名。", example="test.txt"),
|
||||||
file_content: str = Body(..., description="文件内容,可以是纯文本或Base64编码的字符串。"),
|
file_content: str = Body(..., description="文件内容,可以是纯文本或Base64编码的字符串。"),
|
||||||
to_lang: str = Body("中文", description="目标语言。")
|
to_lang: str = Body("中文", description="目标语言。"),
|
||||||
|
concurrent: int = Body(30, description="ai翻译并发数")
|
||||||
):
|
):
|
||||||
def is_base64(s):
|
def is_base64(s):
|
||||||
try:
|
try:
|
||||||
@@ -525,6 +536,7 @@ async def temp_translate(base_url: str = Body(..., description="LLM API的基础
|
|||||||
key=api_key,
|
key=api_key,
|
||||||
model_id=model_id,
|
model_id=model_id,
|
||||||
mineru_token=mineru_token,
|
mineru_token=mineru_token,
|
||||||
|
concurrent=concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -401,7 +401,7 @@
|
|||||||
<li><a class="dropdown-item download-html-link" href="#"><i
|
<li><a class="dropdown-item download-html-link" href="#"><i
|
||||||
class="bi bi-filetype-html me-2"></i>HTML</a></li>
|
class="bi bi-filetype-html me-2"></i>HTML</a></li>
|
||||||
<li><a class="dropdown-item download-markdown-link" href="#"><i
|
<li><a class="dropdown-item download-markdown-link" href="#"><i
|
||||||
class="bi bi-markdown-fill me-2"></i>Markdown(嵌图)</a></li>
|
class="bi bi-markdown-fill me-2"></i>Markdown(嵌图)</a></li>
|
||||||
<li><a class="dropdown-item download-markdown-zip-link" href="#"><i
|
<li><a class="dropdown-item download-markdown-zip-link" href="#"><i
|
||||||
class="bi bi-file-zip-fill me-2"></i>Markdown压缩包</a></li>
|
class="bi bi-file-zip-fill me-2"></i>Markdown压缩包</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -667,7 +667,7 @@
|
|||||||
file: null,
|
file: null,
|
||||||
htmlUrl: null,
|
htmlUrl: null,
|
||||||
fileNameStem: null,
|
fileNameStem: null,
|
||||||
isSubmitted: false // MODIFICATION 1: Add isSubmitted flag
|
isSubmitted: false
|
||||||
},
|
},
|
||||||
intervals: {
|
intervals: {
|
||||||
log: null,
|
log: null,
|
||||||
@@ -791,7 +791,7 @@
|
|||||||
try {
|
try {
|
||||||
const fileContentBase64 = await fileToBase64(state.file);
|
const fileContentBase64 = await fileToBase64(state.file);
|
||||||
|
|
||||||
state.isSubmitted = true; // MODIFICATION 2: Mark task as submitted before sending request
|
state.isSubmitted = true;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
task_id: taskId,
|
task_id: taskId,
|
||||||
@@ -902,8 +902,6 @@
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(`/service/status/${taskId}`);
|
const response = await fetch(`/service/status/${taskId}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// MODIFICATION 3: Only remove task if it was already submitted
|
|
||||||
// An unsubmitted task will have `state.isSubmitted = false`, and will not be removed.
|
|
||||||
if (response.status === 404 && isRestore && state.isSubmitted) {
|
if (response.status === 404 && isRestore && state.isSubmitted) {
|
||||||
removeTask(taskId);
|
removeTask(taskId);
|
||||||
}
|
}
|
||||||
@@ -911,6 +909,20 @@
|
|||||||
}
|
}
|
||||||
const status = await response.json();
|
const status = await response.json();
|
||||||
|
|
||||||
|
// ==================== MODIFICATION START ====================
|
||||||
|
// Restore filename display from status
|
||||||
|
// This runs only if state.file is not present (e.g., after a page refresh)
|
||||||
|
// and the backend provides the original filename.
|
||||||
|
if (status.original_filename && !state.file) {
|
||||||
|
elements.fileNameDisplay.textContent = `已上传: ${status.original_filename}`;
|
||||||
|
elements.fileDropArea.classList.add('file-selected');
|
||||||
|
elements.fileDropPrompt.style.display = 'none';
|
||||||
|
// Also clear any potential error states from a previous failed attempt
|
||||||
|
elements.fileDropArea.classList.remove('input-error');
|
||||||
|
elements.fileNameDisplay.classList.remove('input-error-text');
|
||||||
|
}
|
||||||
|
// ==================== MODIFICATION END ====================
|
||||||
|
|
||||||
elements.statusMessage.textContent = status.status_message || '正在获取状态...';
|
elements.statusMessage.textContent = status.status_message || '正在获取状态...';
|
||||||
elements.statusMessage.className = `status-message small ${status.error_flag ? 'text-danger' : 'text-info'}`;
|
elements.statusMessage.className = `status-message small ${status.error_flag ? 'text-danger' : 'text-info'}`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user