Files
dify-docs/plugin-dev-en/0432-develop-md-exporter.mdx
2025-09-30 16:47:06 +08:00

549 lines
16 KiB
Plaintext

---
title: 'Building a Markdown Exporter Plugin'
description: 'Learn how to create a plugin that exports conversations to different document formats'
language: en
standard_title: Building a Markdown Exporter Plugin
---
## What you'll build
In this guide, you'll learn how to build a practical Dify plugin that exports conversations into popular document formats. By the end, your plugin will:
- Convert markdown text to Word documents (.docx)
- Export conversations as PDF files
- Handle file creation with proper formatting
- Provide a clean user experience for document exports
<CardGroup cols={2}>
<Card title="Time required" icon="clock">
15 minutes
</Card>
<Card title="Prerequisites" icon="list-check">
Basic Python knowledge and familiarity with document manipulation libraries
</Card>
</CardGroup>
## Step 1: Set up your environment
<Steps>
<Step title="Install the Dify CLI">
<Tabs>
<Tab title="Mac">
```bash
brew tap langgenius/dify
brew install dify
```
</Tab>
<Tab title="Linux">
Get the latest Dify CLI from the [Dify GitHub releases page](https://github.com/langgenius/dify-plugin-daemon/releases)
```bash
# Download appropriate version
chmod +x dify-plugin-linux-amd64
mv dify-plugin-linux-amd64 dify
sudo mv dify /usr/local/bin/
```
</Tab>
</Tabs>
Verify installation:
```bash
dify version
```
</Step>
<Step title="Create the plugin project">
Initialize a new plugin project:
```bash
dify plugin init
```
Follow the prompts:
- Name: "md_exporter"
- Type: "tool"
- Complete other details as prompted
</Step>
</Steps>
## Step 2: Define plugin manifest
Create the `manifest.yaml` file to define your plugin's metadata:
```yaml
version: 0.0.4
type: plugin
author: your_username
label:
en_US: Markdown Exporter
zh_Hans: Markdown导出工具
created_at: "2025-09-30T00:00:00Z"
icon: icon.png
resource:
memory: 134217728 # 128MB
permission:
storage:
enabled: true # We need storage for temp files
plugins:
tools:
- word_export.yaml
- pdf_export.yaml
meta:
version: 0.0.1
arch:
- amd64
- arm64
runner:
language: python
version: 3.11
entrypoint: main
```
## Step 3: Define the Word export tool
Create a `word_export.yaml` file to define the Word document export tool:
```yaml
identity:
author: your_username
name: word_export
label:
en_US: Export to Word
zh_Hans: 导出为Word文档
description:
human:
en_US: Export conversation content to a Word document (.docx)
zh_Hans: 将对话内容导出为Word文档(.docx)
llm: >
A tool that converts markdown text to a Word document (.docx) format.
Use this tool when the user wants to save or export the conversation
content as a Word document. The input text should be in markdown format.
credential_schema: {} # No credentials needed
tool_schema:
markdown_content:
type: string
required: true
label:
en_US: Markdown Content
zh_Hans: Markdown内容
human_description:
en_US: The markdown content to convert to Word format
zh_Hans: 要转换为Word格式的Markdown内容
document_name:
type: string
required: false
label:
en_US: Document Name
zh_Hans: 文档名称
human_description:
en_US: Name for the exported document (without extension)
zh_Hans: 导出文档的名称(无需扩展名)
```
## Step 4: Define the PDF export tool
Create a `pdf_export.yaml` file for PDF exports:
```yaml
identity:
author: your_username
name: pdf_export
label:
en_US: Export to PDF
zh_Hans: 导出为PDF文档
description:
human:
en_US: Export conversation content to a PDF document
zh_Hans: 将对话内容导出为PDF文档
llm: >
A tool that converts markdown text to a PDF document.
Use this tool when the user wants to save or export the conversation
content as a PDF file. The input text should be in markdown format.
credential_schema: {} # No credentials needed
tool_schema:
markdown_content:
type: string
required: true
label:
en_US: Markdown Content
zh_Hans: Markdown内容
human_description:
en_US: The markdown content to convert to PDF format
zh_Hans: 要转换为PDF格式的Markdown内容
document_name:
type: string
required: false
label:
en_US: Document Name
zh_Hans: 文档名称
human_description:
en_US: Name for the exported document (without extension)
zh_Hans: 导出文档的名称(无需扩展名)
```
## Step 5: Install required dependencies
Create or update `requirements.txt` with the necessary libraries:
```text
python-docx>=0.8.11
markdown>=3.4.1
weasyprint>=59.0
beautifulsoup4>=4.12.2
```
## Step 6: Implement the Word export functionality
Create a utility module in `utils/docx_utils.py`:
<CodeGroup>
```python utils/docx_utils.py
import os
import tempfile
import uuid
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
import markdown
from bs4 import BeautifulSoup
def convert_markdown_to_docx(markdown_text, document_name=None):
"""
Convert markdown text to a Word document and return the file path
"""
if not document_name:
document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
# Convert markdown to HTML
html = markdown.markdown(markdown_text)
soup = BeautifulSoup(html, 'html.parser')
# Create a new Word document
doc = Document()
# Process HTML elements and add to document
for element in soup.find_all(['h1', 'h2', 'h3', 'h4', 'p', 'ul', 'ol']):
if element.name == 'h1':
heading = doc.add_heading(element.text.strip(), level=1)
heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
elif element.name == 'h2':
doc.add_heading(element.text.strip(), level=2)
elif element.name == 'h3':
doc.add_heading(element.text.strip(), level=3)
elif element.name == 'h4':
doc.add_heading(element.text.strip(), level=4)
elif element.name == 'p':
paragraph = doc.add_paragraph(element.text.strip())
elif element.name in ('ul', 'ol'):
for li in element.find_all('li'):
doc.add_paragraph(li.text.strip(), style='ListBullet')
# Create temp directory if it doesn't exist
temp_dir = tempfile.gettempdir()
if not os.path.exists(temp_dir):
os.makedirs(temp_dir)
# Save the document
file_path = os.path.join(temp_dir, f"{document_name}.docx")
doc.save(file_path)
return file_path
```
</CodeGroup>
## Step 7: Implement the PDF export functionality
Create a utility module in `utils/pdf_utils.py`:
<CodeGroup>
```python utils/pdf_utils.py
import os
import tempfile
import uuid
import markdown
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
def convert_markdown_to_pdf(markdown_text, document_name=None):
"""
Convert markdown text to a PDF document and return the file path
"""
if not document_name:
document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
# Convert markdown to HTML
html_content = markdown.markdown(markdown_text)
# Add basic styling
styled_html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{document_name}</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }}
h1 {{ text-align: center; color: #333; }}
h2, h3, h4 {{ color: #444; margin-top: 20px; }}
p {{ margin-bottom: 15px; }}
ul, ol {{ margin-left: 20px; }}
</style>
</head>
<body>
{html_content}
</body>
</html>
"""
# Create temp directory if it doesn't exist
temp_dir = tempfile.gettempdir()
if not os.path.exists(temp_dir):
os.makedirs(temp_dir)
# Output file path
file_path = os.path.join(temp_dir, f"{document_name}.pdf")
# Configure fonts
font_config = FontConfiguration()
# Render PDF
HTML(string=styled_html).write_pdf(
file_path,
stylesheets=[],
font_config=font_config
)
return file_path
```
</CodeGroup>
## Step 8: Create tool implementations
First, create the Word export tool in `tools/word_export.py`:
<CodeGroup>
```python tools/word_export.py
import os
import base64
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from utils.docx_utils import convert_markdown_to_docx
class WordExportTool(Tool):
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
# Extract parameters
markdown_content = tool_parameters.get("markdown_content", "")
document_name = tool_parameters.get("document_name", "exported_document")
if not markdown_content:
yield self.create_text_message("Error: No content provided for export.")
return
try:
# Convert markdown to Word
file_path = convert_markdown_to_docx(markdown_content, document_name)
# Read the file as binary
with open(file_path, 'rb') as file:
file_content = file.read()
# Encode as base64
file_base64 = base64.b64encode(file_content).decode('utf-8')
# Return success message and file
yield self.create_text_message(
f"Document exported successfully as Word (.docx) format."
)
yield self.create_file_message(
file_name=f"{document_name}.docx",
file_content=file_base64,
mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
)
except Exception as e:
yield self.create_text_message(f"Error exporting to Word: {str(e)}")
return
```
</CodeGroup>
Next, create the PDF export tool in `tools/pdf_export.py`:
<CodeGroup>
```python tools/pdf_export.py
import os
import base64
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from utils.pdf_utils import convert_markdown_to_pdf
class PDFExportTool(Tool):
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
# Extract parameters
markdown_content = tool_parameters.get("markdown_content", "")
document_name = tool_parameters.get("document_name", "exported_document")
if not markdown_content:
yield self.create_text_message("Error: No content provided for export.")
return
try:
# Convert markdown to PDF
file_path = convert_markdown_to_pdf(markdown_content, document_name)
# Read the file as binary
with open(file_path, 'rb') as file:
file_content = file.read()
# Encode as base64
file_base64 = base64.b64encode(file_content).decode('utf-8')
# Return success message and file
yield self.create_text_message(
f"Document exported successfully as PDF format."
)
yield self.create_file_message(
file_name=f"{document_name}.pdf",
file_content=file_base64,
mime_type="application/pdf"
)
except Exception as e:
yield self.create_text_message(f"Error exporting to PDF: {str(e)}")
return
```
</CodeGroup>
## Step 9: Create the entrypoint
Create a `main.py` file at the root of your project:
<CodeGroup>
```python main.py
from dify_plugin import PluginRunner
from tools.word_export import WordExportTool
from tools.pdf_export import PDFExportTool
plugin = PluginRunner(
tools=[
WordExportTool(),
PDFExportTool(),
],
providers=[] # No credential providers needed
)
```
</CodeGroup>
## Step 10: Test your plugin
<Steps>
<Step title="Set up your debug environment">
First, create your `.env` file from the template:
```bash
cp .env.example .env
```
Configure it with your Dify environment details:
```
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=your_debug_key
```
</Step>
<Step title="Install dependencies">
```bash
pip install -r requirements.txt
```
</Step>
<Step title="Start the plugin in debug mode">
```bash
python -m main
```
</Step>
</Steps>
## Step 11: Package for distribution
When you're ready to share your plugin:
```bash
dify plugin package ./
```
This creates a `plugin.difypkg` file for distribution.
## Creative use cases
<CardGroup cols={2}>
<Card title="Report generation" icon="file-lines">
Use this plugin to convert analysis summaries into professional reports for clients
</Card>
<Card title="Session documentation" icon="book">
Export coaching or consulting session notes as formatted documents
</Card>
</CardGroup>
## Beyond the basics
Here are some interesting ways to extend this plugin:
- **Custom templates**: Add company branding or personalized styles
- **Multi-format support**: Expand to export as HTML, Markdown, or other formats
- **Image handling**: Process and include images from conversations
- **Table support**: Implement proper formatting for data tables
- **Collaborative editing**: Add integration with Google Docs or similar platforms
<Accordion title="Technical insights">
The core challenge in document conversion is maintaining formatting and structure. The approach used in this plugin first converts markdown to HTML (an intermediate format), then processes that HTML into the target format.
This two-step process provides flexibility—you could extend it to support additional formats by simply adding new output modules that work with the HTML representation.
For PDF generation, WeasyPrint was chosen because it offers high-quality PDF rendering with CSS support. For Word documents, python-docx provides granular control over document structure.
</Accordion>
## Summary
You've built a practical plugin that adds real value to the Dify platform by enabling users to export conversations in professional document formats. This functionality bridges the gap between AI conversations and traditional document workflows.
<CardGroup cols={2}>
<Card title="Documentation" icon="book">
Write your README.md in English (en_US) describing functionality, setup, and usage examples
</Card>
<Card title="Localization" icon="language">
Create additional README files like `readme/README_zh_Hans.md` for other languages
</Card>
</CardGroup>
<CheckList>
<CheckListItem id="privacy">
Add a privacy policy (PRIVACY.md) if publishing your plugin
</CheckListItem>
<CheckListItem id="documentation">
Include comprehensive examples in documentation
</CheckListItem>
<CheckListItem id="testing">
Test thoroughly with various document sizes and formats
</CheckListItem>
</CheckList>
{/*
Contributing Section
DO NOT edit this section!
It will be automatically generated by the script.
*/}
---
[Edit this page](https://github.com/langgenius/dify-docs/edit/main/plugin-dev-en/0432-develop-md-exporter.mdx) | [Report an issue](https://github.com/langgenius/dify-docs/issues/new?template=docs.yml)