lightpdf-aipdf-mcp 0.1.87__py3-none-any.whl → 0.1.88__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,7 @@ import mcp.types as types
19
19
  from .common import BaseResult, Logger, FileHandler
20
20
  from .converter import Converter, ConversionResult
21
21
  from .editor import Editor, EditResult
22
+ from .translator import Translator, TranslateResult
22
23
 
23
24
  # 加载环境变量
24
25
  load_dotenv()
@@ -277,9 +278,29 @@ async def process_tool_call(
277
278
  """
278
279
  file_handler = FileHandler(logger)
279
280
  editor = Editor(logger, file_handler)
280
-
281
+ # 新增:翻译操作分支
282
+ if operation_config.get("is_translate_operation"):
283
+ translator = Translator(logger, file_handler)
284
+ extra_params = operation_config.get("extra_params", {})
285
+
286
+ results = await process_batch_files(
287
+ file_objects,
288
+ logger,
289
+ lambda file_path, password, original_name: translator.translate_pdf(
290
+ file_path=file_path,
291
+ source=extra_params.get("source", "auto"),
292
+ target=extra_params.get("target"),
293
+ output_type=extra_params.get("output_type", "mono"),
294
+ password=password,
295
+ original_name=original_name
296
+ ),
297
+ "PDF翻译"
298
+ )
299
+
300
+ report_msg = generate_result_report(results)
301
+
281
302
  # 根据操作类型选择不同的处理逻辑
282
- if operation_config.get("is_edit_operation"):
303
+ elif operation_config.get("is_edit_operation"):
283
304
  # 编辑操作
284
305
  edit_type = operation_config.get("edit_type", "")
285
306
  extra_params = operation_config.get("extra_params")
@@ -397,7 +418,7 @@ async def handle_list_tools() -> list[types.Tool]:
397
418
  return [
398
419
  types.Tool(
399
420
  name="convert_document",
400
- description="Document format conversion tool.\n\nPDF can be converted to: DOCX/XLSX/PPTX/Images (including long images)/HTML/TXT (for text extraction)/CSV;\nOther formats can be converted to PDF: DOCX/XLSX/PPTX/Images/CAD/CAJ/OFD.\n\nDoes not support creating files from content",
421
+ description="Document format conversion tool.\n\nPDF can be converted to: DOCX/XLSX/PPTX/Images (including long images)/HTML/TXT (for text extraction)/CSV;\nOther formats can be converted to PDF: DOCX/XLSX/PPTX/Images/CAD/CAJ/OFD.\n\nDoes not support creating files from content.",
401
422
  inputSchema={
402
423
  "type": "object",
403
424
  "properties": {
@@ -408,20 +429,20 @@ async def handle_list_tools() -> list[types.Tool]:
408
429
  "properties": {
409
430
  "path": {
410
431
  "type": "string",
411
- "description": "File URL, must include protocol, supports http/https/oss"
432
+ "description": "File URL, must include protocol, supports http/https/oss."
412
433
  },
413
434
  "password": {
414
435
  "type": "string",
415
- "description": "Document password, required if the document is password-protected"
436
+ "description": "Document password, required if the document is password-protected."
416
437
  },
417
438
  "name": {
418
439
  "type": "string",
419
- "description": "Original filename of the document"
440
+ "description": "Original filename of the document."
420
441
  }
421
442
  },
422
443
  "required": ["path"]
423
444
  },
424
- "description": "List of files to convert, each containing path and optional password"
445
+ "description": "List of files to convert, each containing path and optional password."
425
446
  },
426
447
  "format": {
427
448
  "type": "string",
@@ -432,7 +453,7 @@ async def handle_list_tools() -> list[types.Tool]:
432
453
  "type": "integer",
433
454
  "enum": [0, 1],
434
455
  "default": 0,
435
- "description": "Whether to merge results: 1 = merge all, 0 = separate. Only valid for: PDF to Excel (1: all pages to one sheet, 0: each page to a sheet), PDF to Image (1: merge to long image, 0: each page to an image), Image to PDF (1: all images to one PDF, 0: each image to a PDF)"
456
+ "description": "Whether to merge results: 1 = merge all, 0 = separate. Only valid for: PDF to Excel (1: all pages to one sheet, 0: each page to a sheet), PDF to Image (1: merge to long image, 0: each page to an image), Image to PDF (1: all images to one PDF, 0: each image to a PDF)."
436
457
  }
437
458
  },
438
459
  "required": ["files", "format"]
@@ -964,7 +985,7 @@ async def handle_list_tools() -> list[types.Tool]:
964
985
  ),
965
986
  types.Tool(
966
987
  name="flatten_pdf",
967
- description="Flatten PDF files (convert editable elements such as text, form fields, annotations, and layers into non-editable static content or fixed content)",
988
+ description="Flatten PDF files (convert editable elements such as text, form fields, annotations, and layers into non-editable static content or fixed content).",
968
989
  inputSchema={
969
990
  "type": "object",
970
991
  "properties": {
@@ -1025,7 +1046,56 @@ async def handle_list_tools() -> list[types.Tool]:
1025
1046
  },
1026
1047
  "required": ["files"]
1027
1048
  }
1028
- )
1049
+ ),
1050
+ types.Tool(
1051
+ name="translate_pdf",
1052
+ description="Translate PDF documents from a source language to a target language. Supports mono (target only) or dual (source/target bilingual) output.",
1053
+ inputSchema={
1054
+ "type": "object",
1055
+ "properties": {
1056
+ "files": {
1057
+ "type": "array",
1058
+ "items": {
1059
+ "type": "object",
1060
+ "properties": {
1061
+ "path": {
1062
+ "type": "string",
1063
+ "description": "PDF file URL, must include protocol, supports http/https/oss."
1064
+ },
1065
+ "password": {
1066
+ "type": "string",
1067
+ "description": "PDF document password, required if the document is password-protected."
1068
+ },
1069
+ "name": {
1070
+ "type": "string",
1071
+ "description": "Original filename of the document."
1072
+ }
1073
+ },
1074
+ "required": ["path"]
1075
+ },
1076
+ "description": "List of PDF files to translate, each containing path and optional password."
1077
+ },
1078
+ "source": {
1079
+ "type": "string",
1080
+ "description": "Source language. Supports 'auto' for automatic detection.",
1081
+ "enum": ["auto", "ar", "bg", "cz", "da", "de", "el", "en", "es", "fi", "fr", "hbs", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "ru", "sl", "sv", "th", "tr", "vi", "zh", "zh-tw"],
1082
+ "default": "auto"
1083
+ },
1084
+ "target": {
1085
+ "type": "string",
1086
+ "description": "Target language. Must be specified.",
1087
+ "enum": ["ar", "bg", "cz", "da", "de", "el", "en", "es", "fi", "fr", "hbs", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "ru", "sl", "sv", "th", "tr", "vi", "zh", "zh-tw"]
1088
+ },
1089
+ "output_type": {
1090
+ "type": "string",
1091
+ "description": "Output type: 'mono' for target language only, 'dual' for source/target bilingual output.",
1092
+ "enum": ["mono", "dual"],
1093
+ "default": "mono"
1094
+ }
1095
+ },
1096
+ "required": ["files", "target"]
1097
+ }
1098
+ ),
1029
1099
  ]
1030
1100
 
1031
1101
  @app.call_tool()
@@ -1105,6 +1175,10 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
1105
1175
  "is_edit_operation": True,
1106
1176
  "param_keys": [] # 不暴露provider
1107
1177
  },
1178
+ "translate_pdf": {
1179
+ "is_translate_operation": True,
1180
+ "param_keys": ["source", "target", "output_type"]
1181
+ },
1108
1182
  }
1109
1183
 
1110
1184
  DEFAULTS = {
@@ -0,0 +1,94 @@
1
+ from dataclasses import dataclass
2
+ import os
3
+ import httpx
4
+ from typing import Optional, Dict, Any
5
+ from .common import Logger, BaseResult, FileHandler, BaseApiClient
6
+
7
+ @dataclass
8
+ class TranslateResult(BaseResult):
9
+ """翻译结果数据类"""
10
+ pass
11
+
12
+ class Translator(BaseApiClient):
13
+ """PDF文档翻译器"""
14
+ def __init__(self, logger: Logger, file_handler: FileHandler):
15
+ super().__init__(logger, file_handler)
16
+ api_endpoint = os.getenv("API_ENDPOINT", "techsz.aoscdn.com/api")
17
+ self.api_base_url = f"https://{api_endpoint}/tasks/document/transdocument-local"
18
+
19
+ async def translate_pdf(self, file_path: str, source: str, target: str, output_type: str = "mono", password: Optional[str] = None, original_name: Optional[str] = None) -> TranslateResult:
20
+ if not self.api_key:
21
+ await self.logger.error("未找到API_KEY。请在客户端配置API_KEY环境变量。")
22
+ return TranslateResult(success=False, file_path=file_path, error_message="未找到API_KEY", original_name=original_name)
23
+
24
+ # 构建API参数
25
+ extra_params = {
26
+ "source": source or "auto",
27
+ "target": target,
28
+ "output_type": output_type or "mono"
29
+ }
30
+ if password:
31
+ extra_params["password"] = password
32
+ if original_name:
33
+ extra_params["filename"] = os.path.splitext(original_name)[0]
34
+
35
+ async with httpx.AsyncClient(timeout=3600.0) as client:
36
+ task_id = None
37
+ try:
38
+ # 创建翻译任务
39
+ task_id = await self._create_task(client, file_path, extra_params)
40
+ # 等待任务完成
41
+ download_url = await self._wait_for_task(client, task_id, "翻译")
42
+
43
+ await self.logger.log("info", "翻译完成。可通过下载链接获取结果文件。")
44
+ return TranslateResult(
45
+ success=True,
46
+ file_path=file_path,
47
+ error_message=None,
48
+ download_url=download_url,
49
+ original_name=original_name,
50
+ task_id=task_id
51
+ )
52
+ except Exception as e:
53
+ return TranslateResult(
54
+ success=False,
55
+ file_path=file_path,
56
+ error_message=str(e),
57
+ download_url=None,
58
+ original_name=original_name,
59
+ task_id=task_id
60
+ )
61
+
62
+ async def _create_task(self, client: httpx.AsyncClient, file_path: str, extra_params: dict = None) -> str:
63
+ await self.logger.log("info", "正在提交翻译任务...")
64
+ headers = {"X-API-KEY": self.api_key}
65
+ data = {}
66
+ if extra_params:
67
+ data.update(extra_params)
68
+ # 检查是否为OSS路径
69
+ if self.file_handler.is_oss_id(file_path):
70
+ data["resource_id"] = file_path.split("oss_id://")[1]
71
+ headers["Content-Type"] = "application/json"
72
+ response = await client.post(
73
+ self.api_base_url,
74
+ json=data,
75
+ headers=headers
76
+ )
77
+ elif self.file_handler.is_url(file_path):
78
+ data["url"] = file_path
79
+ headers["Content-Type"] = "application/json"
80
+ response = await client.post(
81
+ self.api_base_url,
82
+ json=data,
83
+ headers=headers
84
+ )
85
+ else:
86
+ with open(file_path, "rb") as f:
87
+ files = {"file": f}
88
+ response = await client.post(
89
+ self.api_base_url,
90
+ files=files,
91
+ data=data,
92
+ headers=headers
93
+ )
94
+ return await self._handle_api_response(response, "创建翻译任务")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lightpdf-aipdf-mcp
3
- Version: 0.1.87
3
+ Version: 0.1.88
4
4
  Summary: MCP Server for LightPDF AI-PDF
5
5
  Author: LightPDF Team
6
6
  License: Proprietary
@@ -0,0 +1,10 @@
1
+ lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
2
+ lightpdf_aipdf_mcp/common.py,sha256=_UO1f6S9Qr_3k6u5iBpdVDpvTK5U-tHEpu9KsDGqV8Y,6635
3
+ lightpdf_aipdf_mcp/converter.py,sha256=f0gS8tAQlJ8uwJUVUmd9nAA4O9m558e9lAT2B_MxmIo,15135
4
+ lightpdf_aipdf_mcp/editor.py,sha256=9teOqi2y2JbjcCI-kUhYpSXL-F75i7Mfr9E20KKyZP0,29909
5
+ lightpdf_aipdf_mcp/server.py,sha256=khv6gJNWx8nhH6gpOu4jdeeL8p7xMPONEv7mAf8oWXQ,59952
6
+ lightpdf_aipdf_mcp/translator.py,sha256=FACnFcnz1zNDdndR3tAgTfDDkfk1rJRRgWorFbiiEUk,3834
7
+ lightpdf_aipdf_mcp-0.1.88.dist-info/METADATA,sha256=AOlJAa2HvSrrcN2I264tvQ5FR3-Gl4Nz7jqo1Ai4Wjs,8119
8
+ lightpdf_aipdf_mcp-0.1.88.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ lightpdf_aipdf_mcp-0.1.88.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
10
+ lightpdf_aipdf_mcp-0.1.88.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
2
- lightpdf_aipdf_mcp/common.py,sha256=_UO1f6S9Qr_3k6u5iBpdVDpvTK5U-tHEpu9KsDGqV8Y,6635
3
- lightpdf_aipdf_mcp/converter.py,sha256=f0gS8tAQlJ8uwJUVUmd9nAA4O9m558e9lAT2B_MxmIo,15135
4
- lightpdf_aipdf_mcp/editor.py,sha256=9teOqi2y2JbjcCI-kUhYpSXL-F75i7Mfr9E20KKyZP0,29909
5
- lightpdf_aipdf_mcp/server.py,sha256=rn9QvHr5E-IaCx2V1pXuKtiXKf1-b3zns-VoxH6x2UE,56220
6
- lightpdf_aipdf_mcp-0.1.87.dist-info/METADATA,sha256=4WUxVWAPL13KBXsEKfI3iHn4qBxof-GMBNVcXZtLmkQ,8119
7
- lightpdf_aipdf_mcp-0.1.87.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- lightpdf_aipdf_mcp-0.1.87.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
9
- lightpdf_aipdf_mcp-0.1.87.dist-info/RECORD,,