feat(file-tool): add text markdown xml html actions
This commit is contained in:
@@ -60,6 +60,12 @@ Implemented in actual NODE1 stack (`services/router/*` + gateway):
|
|||||||
- `pdf_merge`
|
- `pdf_merge`
|
||||||
- `pdf_split`
|
- `pdf_split`
|
||||||
- `pdf_fill`
|
- `pdf_fill`
|
||||||
|
- `text_create`
|
||||||
|
- `text_update`
|
||||||
|
- `markdown_create`
|
||||||
|
- `markdown_update`
|
||||||
|
- `xml_export`
|
||||||
|
- `html_export`
|
||||||
|
|
||||||
### Standard output contract
|
### Standard output contract
|
||||||
For file-producing tool calls, router now propagates:
|
For file-producing tool calls, router now propagates:
|
||||||
@@ -93,6 +99,8 @@ For file-producing tool calls, router now propagates:
|
|||||||
## Smoke Tests
|
## Smoke Tests
|
||||||
Run inside `dagi-router-node1` to validate actions deterministically:
|
Run inside `dagi-router-node1` to validate actions deterministically:
|
||||||
- Excel create/update
|
- Excel create/update
|
||||||
|
- Text/Markdown create/update
|
||||||
|
- XML/HTML export
|
||||||
- CSV create/update
|
- CSV create/update
|
||||||
- JSON/YAML export
|
- JSON/YAML export
|
||||||
- ZIP bundle
|
- ZIP bundle
|
||||||
@@ -108,6 +116,7 @@ Also verify infer endpoint still works:
|
|||||||
- `rollback_backups/file_tool_step3_tool_manager.py.bak_20260215_012200`
|
- `rollback_backups/file_tool_step3_tool_manager.py.bak_20260215_012200`
|
||||||
- `rollback_backups/file_tool_step4_tool_manager.py.bak_20260215_012309`
|
- `rollback_backups/file_tool_step4_tool_manager.py.bak_20260215_012309`
|
||||||
- `services/router/tool_manager.py.bak_20260215_020902`
|
- `services/router/tool_manager.py.bak_20260215_020902`
|
||||||
|
- `services/router/tool_manager.py.bak_20260215_112313`
|
||||||
|
|
||||||
## Rollback (NODE1)
|
## Rollback (NODE1)
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -325,7 +325,9 @@ TOOL_DEFINITIONS = [
|
|||||||
"enum": [
|
"enum": [
|
||||||
"excel_create", "excel_update", "docx_create", "docx_update",
|
"excel_create", "excel_update", "docx_create", "docx_update",
|
||||||
"csv_create", "csv_update", "pdf_fill", "pdf_merge", "pdf_split",
|
"csv_create", "csv_update", "pdf_fill", "pdf_merge", "pdf_split",
|
||||||
"json_export", "yaml_export", "zip_bundle"
|
"json_export", "yaml_export", "zip_bundle",
|
||||||
|
"text_create", "text_update", "markdown_create", "markdown_update",
|
||||||
|
"xml_export", "html_export"
|
||||||
],
|
],
|
||||||
"description": "Дія file tool"
|
"description": "Дія file tool"
|
||||||
},
|
},
|
||||||
@@ -608,6 +610,18 @@ class ToolManager:
|
|||||||
return self._file_csv_create(args)
|
return self._file_csv_create(args)
|
||||||
if action == "csv_update":
|
if action == "csv_update":
|
||||||
return self._file_csv_update(args)
|
return self._file_csv_update(args)
|
||||||
|
if action == "text_create":
|
||||||
|
return self._file_text_create(args)
|
||||||
|
if action == "text_update":
|
||||||
|
return self._file_text_update(args)
|
||||||
|
if action == "markdown_create":
|
||||||
|
return self._file_markdown_create(args)
|
||||||
|
if action == "markdown_update":
|
||||||
|
return self._file_markdown_update(args)
|
||||||
|
if action == "xml_export":
|
||||||
|
return self._file_xml_export(args)
|
||||||
|
if action == "html_export":
|
||||||
|
return self._file_html_export(args)
|
||||||
if action == "json_export":
|
if action == "json_export":
|
||||||
return self._file_json_export(args)
|
return self._file_json_export(args)
|
||||||
if action == "yaml_export":
|
if action == "yaml_export":
|
||||||
@@ -711,6 +725,107 @@ class ToolManager:
|
|||||||
file_mime="application/json",
|
file_mime="application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _stringify_text_payload(args: Dict[str, Any], key: str = "text") -> str:
|
||||||
|
value = args.get(key)
|
||||||
|
if value is None and key != "content":
|
||||||
|
value = args.get("content")
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
if isinstance(value, (dict, list)):
|
||||||
|
return json.dumps(value, ensure_ascii=False, indent=2)
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
def _file_text_create(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "note.txt", force_ext=".txt")
|
||||||
|
text = self._stringify_text_payload(args, key="text")
|
||||||
|
data = text.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"Text file created: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="text/plain",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _file_text_update(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
src_b64 = args.get("file_base64")
|
||||||
|
if not src_b64:
|
||||||
|
return ToolResult(success=False, result=None, error="file_base64 is required for text_update")
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "updated.txt", force_ext=".txt")
|
||||||
|
operation = str(args.get("operation") or "append").strip().lower()
|
||||||
|
if operation not in {"append", "replace"}:
|
||||||
|
return ToolResult(success=False, result=None, error="operation must be append|replace")
|
||||||
|
|
||||||
|
incoming = self._stringify_text_payload(args, key="text")
|
||||||
|
existing = self._bytes_from_b64(src_b64).decode("utf-8")
|
||||||
|
updated = incoming if operation == "replace" else f"{existing}{incoming}"
|
||||||
|
data = updated.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"Text file updated: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="text/plain",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _file_markdown_create(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "document.md", force_ext=".md")
|
||||||
|
text = self._stringify_text_payload(args, key="text")
|
||||||
|
data = text.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"Markdown created: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="text/markdown",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _file_markdown_update(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
src_b64 = args.get("file_base64")
|
||||||
|
if not src_b64:
|
||||||
|
return ToolResult(success=False, result=None, error="file_base64 is required for markdown_update")
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "updated.md", force_ext=".md")
|
||||||
|
operation = str(args.get("operation") or "append").strip().lower()
|
||||||
|
if operation not in {"append", "replace"}:
|
||||||
|
return ToolResult(success=False, result=None, error="operation must be append|replace")
|
||||||
|
|
||||||
|
incoming = self._stringify_text_payload(args, key="text")
|
||||||
|
existing = self._bytes_from_b64(src_b64).decode("utf-8")
|
||||||
|
updated = incoming if operation == "replace" else f"{existing}{incoming}"
|
||||||
|
data = updated.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"Markdown updated: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="text/markdown",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _file_xml_export(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "export.xml", force_ext=".xml")
|
||||||
|
xml = self._stringify_text_payload(args, key="xml")
|
||||||
|
data = xml.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"XML exported: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="application/xml",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _file_html_export(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
|
file_name = self._sanitize_file_name(args.get("file_name"), "export.html", force_ext=".html")
|
||||||
|
html = self._stringify_text_payload(args, key="html")
|
||||||
|
data = html.encode("utf-8")
|
||||||
|
return ToolResult(
|
||||||
|
success=True,
|
||||||
|
result={"message": f"HTML exported: {file_name}"},
|
||||||
|
file_base64=self._b64_from_bytes(data),
|
||||||
|
file_name=file_name,
|
||||||
|
file_mime="text/html",
|
||||||
|
)
|
||||||
|
|
||||||
def _file_yaml_export(self, args: Dict[str, Any]) -> ToolResult:
|
def _file_yaml_export(self, args: Dict[str, Any]) -> ToolResult:
|
||||||
file_name = self._sanitize_file_name(args.get("file_name"), "export.yaml", force_ext=".yaml")
|
file_name = self._sanitize_file_name(args.get("file_name"), "export.yaml", force_ext=".yaml")
|
||||||
content = args.get("content")
|
content = args.get("content")
|
||||||
|
|||||||
Reference in New Issue
Block a user