mirror of
https://github.com/langgenius/dify-docs.git
synced 2026-03-27 13:28:32 +07:00
549 lines
16 KiB
Plaintext
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)
|