notionary 0.1.19__py3-none-any.whl → 0.1.21__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.
- notionary/__init__.py +2 -2
- notionary/database/notion_database.py +3 -1
- notionary/elements/audio_element.py +7 -11
- notionary/elements/bookmark_element.py +17 -29
- notionary/elements/bulleted_list_element.py +69 -0
- notionary/elements/callout_element.py +18 -80
- notionary/elements/code_block_element.py +15 -10
- notionary/elements/column_element.py +39 -26
- notionary/elements/divider_element.py +8 -25
- notionary/elements/embed_element.py +10 -18
- notionary/elements/heading_element.py +10 -12
- notionary/elements/image_element.py +9 -15
- notionary/elements/mention_element.py +6 -15
- notionary/elements/notion_block_element.py +12 -11
- notionary/elements/numbered_list_element.py +68 -0
- notionary/elements/paragraph_element.py +11 -7
- notionary/elements/prompts/element_prompt_content.py +20 -0
- notionary/elements/prompts/synthax_prompt_builder.py +92 -0
- notionary/elements/qoute_element.py +20 -89
- notionary/elements/registry/block_element_registry.py +90 -0
- notionary/elements/{block_element_registry_builder.py → registry/block_element_registry_builder.py} +15 -124
- notionary/elements/table_element.py +4 -16
- notionary/elements/text_inline_formatter.py +15 -78
- notionary/elements/todo_lists.py +14 -18
- notionary/elements/toggle_element.py +19 -1
- notionary/elements/video_element.py +10 -19
- notionary/notion_client.py +2 -2
- notionary/page/content/page_content_manager.py +2 -3
- notionary/page/markdown_to_notion_converter.py +3 -3
- notionary/page/notion_page.py +3 -3
- notionary/page/notion_to_markdown_converter.py +3 -3
- notionary/page/relations/notion_page_relation_manager.py +24 -24
- notionary/page/relations/notion_page_title_resolver.py +1 -2
- {notionary-0.1.19.dist-info → notionary-0.1.21.dist-info}/METADATA +3 -3
- notionary-0.1.21.dist-info/RECORD +58 -0
- {notionary-0.1.19.dist-info → notionary-0.1.21.dist-info}/WHEEL +1 -1
- notionary/elements/block_element_registry.py +0 -233
- notionary/elements/list_element.py +0 -130
- notionary/util/singleton_decorator.py +0 -20
- notionary-0.1.19.dist-info/RECORD +0 -56
- {notionary-0.1.19.dist-info → notionary-0.1.21.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.19.dist-info → notionary-0.1.21.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,8 @@
|
|
1
|
-
# File: elements/dividers.py
|
2
|
-
|
3
|
-
from typing import Dict, Any, Optional
|
4
|
-
from typing_extensions import override
|
5
1
|
import re
|
2
|
+
from typing import Dict, Any, Optional
|
6
3
|
|
7
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
8
6
|
|
9
7
|
|
10
8
|
class DividerElement(NotionBlockElement):
|
@@ -17,19 +15,16 @@ class DividerElement(NotionBlockElement):
|
|
17
15
|
|
18
16
|
PATTERN = re.compile(r"^\s*-{3,}\s*$")
|
19
17
|
|
20
|
-
@override
|
21
18
|
@staticmethod
|
22
19
|
def match_markdown(text: str) -> bool:
|
23
20
|
"""Check if text is a markdown divider."""
|
24
21
|
return bool(DividerElement.PATTERN.match(text))
|
25
22
|
|
26
|
-
@override
|
27
23
|
@staticmethod
|
28
24
|
def match_notion(block: Dict[str, Any]) -> bool:
|
29
25
|
"""Check if block is a Notion divider."""
|
30
26
|
return block.get("type") == "divider"
|
31
27
|
|
32
|
-
@override
|
33
28
|
@staticmethod
|
34
29
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
35
30
|
"""Convert markdown divider to Notion divider block."""
|
@@ -38,7 +33,6 @@ class DividerElement(NotionBlockElement):
|
|
38
33
|
|
39
34
|
return {"type": "divider", "divider": {}}
|
40
35
|
|
41
|
-
@override
|
42
36
|
@staticmethod
|
43
37
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
44
38
|
"""Convert Notion divider block to markdown divider."""
|
@@ -47,27 +41,16 @@ class DividerElement(NotionBlockElement):
|
|
47
41
|
|
48
42
|
return "---"
|
49
43
|
|
50
|
-
@override
|
51
44
|
@staticmethod
|
52
45
|
def is_multiline() -> bool:
|
53
46
|
return False
|
54
47
|
|
55
48
|
@classmethod
|
56
|
-
def get_llm_prompt_content(cls) ->
|
57
|
-
"""
|
58
|
-
Returns a dictionary with all information needed for LLM prompts about this element.
|
59
|
-
Includes description, usage guidance, syntax options, and examples.
|
60
|
-
"""
|
49
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
50
|
+
"""Returns compact LLM prompt metadata for the divider element."""
|
61
51
|
return {
|
62
|
-
"description": "Creates a horizontal divider line
|
63
|
-
"when_to_use": "Use
|
64
|
-
"syntax":
|
65
|
-
"
|
66
|
-
"Dividers must be on their own line with no other content",
|
67
|
-
"Dividers work well when combined with headings to clearly separate major document sections",
|
68
|
-
],
|
69
|
-
"examples": [
|
70
|
-
"## Introduction\nThis is the introduction section of the document.\n\n---\n\n## Main Content\nThis is the main content section.",
|
71
|
-
"Task List:\n- Complete project proposal\n- Review feedback\n\n---\n\nMeeting Notes:\n- Discussed timeline\n- Assigned responsibilities",
|
72
|
-
],
|
52
|
+
"description": "Creates a horizontal divider line to visually separate sections of content.",
|
53
|
+
"when_to_use": "Use to create clear visual breaks between different sections without requiring headings.",
|
54
|
+
"syntax": "---",
|
55
|
+
"examples": ["## Section 1\nContent\n\n---\n\n## Section 2\nMore content"],
|
73
56
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
4
5
|
|
5
6
|
|
6
7
|
class EmbedElement(NotionBlockElement):
|
@@ -97,26 +98,17 @@ class EmbedElement(NotionBlockElement):
|
|
97
98
|
return result
|
98
99
|
|
99
100
|
@classmethod
|
100
|
-
def get_llm_prompt_content(cls) ->
|
101
|
-
"""
|
101
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
102
|
+
"""
|
103
|
+
Returns structured LLM prompt metadata for the embed element.
|
104
|
+
"""
|
102
105
|
return {
|
103
106
|
"description": "Embeds external content from websites, PDFs, Google Maps, and other sources directly in your document.",
|
104
|
-
"when_to_use":
|
105
|
-
|
106
|
-
"
|
107
|
-
|
108
|
-
|
109
|
-
"supported_sources": [
|
110
|
-
"Websites and web pages",
|
111
|
-
"PDFs and documents",
|
112
|
-
"Google Maps",
|
113
|
-
"Google Drive files",
|
114
|
-
"Twitter/X posts",
|
115
|
-
"GitHub repositories and code",
|
116
|
-
"Figma designs",
|
117
|
-
"Miro boards",
|
118
|
-
"Many other services supported by Notion's embed feature",
|
119
|
-
],
|
107
|
+
"when_to_use": (
|
108
|
+
"Use embeds when you want to include external content that isn't just a video or image. "
|
109
|
+
"Embeds are great for interactive content, reference materials, or live data sources."
|
110
|
+
),
|
111
|
+
"syntax": "<embed:Caption>(https://example.com)",
|
120
112
|
"examples": [
|
121
113
|
"<embed:Course materials>(https://drive.google.com/file/d/123456/view)",
|
122
114
|
"<embed:Our office location>(https://www.google.com/maps?q=San+Francisco)",
|
@@ -1,8 +1,8 @@
|
|
1
|
-
from typing import Dict, Any, Optional
|
2
|
-
from typing_extensions import override
|
3
1
|
import re
|
2
|
+
from typing import Dict, Any, Optional
|
4
3
|
|
5
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
6
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
7
7
|
|
8
8
|
|
@@ -11,20 +11,17 @@ class HeadingElement(NotionBlockElement):
|
|
11
11
|
|
12
12
|
PATTERN = re.compile(r"^(#{1,6})\s(.+)$")
|
13
13
|
|
14
|
-
@override
|
15
14
|
@staticmethod
|
16
15
|
def match_markdown(text: str) -> bool:
|
17
16
|
"""Check if text is a markdown heading."""
|
18
17
|
return bool(HeadingElement.PATTERN.match(text))
|
19
18
|
|
20
|
-
@override
|
21
19
|
@staticmethod
|
22
20
|
def match_notion(block: Dict[str, Any]) -> bool:
|
23
21
|
"""Check if block is a Notion heading."""
|
24
22
|
block_type: str = block.get("type", "")
|
25
23
|
return block_type.startswith("heading_") and block_type[-1] in "123456"
|
26
24
|
|
27
|
-
@override
|
28
25
|
@staticmethod
|
29
26
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
30
27
|
"""Convert markdown heading to Notion heading block."""
|
@@ -35,8 +32,6 @@ class HeadingElement(NotionBlockElement):
|
|
35
32
|
level = len(header_match.group(1))
|
36
33
|
content = header_match.group(2)
|
37
34
|
|
38
|
-
# Import here to avoid circular imports
|
39
|
-
|
40
35
|
return {
|
41
36
|
"type": f"heading_{level}",
|
42
37
|
f"heading_{level}": {
|
@@ -44,7 +39,6 @@ class HeadingElement(NotionBlockElement):
|
|
44
39
|
},
|
45
40
|
}
|
46
41
|
|
47
|
-
@override
|
48
42
|
@staticmethod
|
49
43
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
50
44
|
"""Convert Notion heading block to markdown heading."""
|
@@ -67,18 +61,22 @@ class HeadingElement(NotionBlockElement):
|
|
67
61
|
prefix = "#" * level
|
68
62
|
return f"{prefix} {text or ''}"
|
69
63
|
|
70
|
-
@override
|
71
64
|
@staticmethod
|
72
65
|
def is_multiline() -> bool:
|
73
66
|
return False
|
74
67
|
|
75
|
-
@override
|
76
68
|
@classmethod
|
77
|
-
def get_llm_prompt_content(cls) ->
|
69
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
78
70
|
"""
|
79
|
-
Returns
|
71
|
+
Returns structured LLM prompt metadata for the heading element.
|
80
72
|
"""
|
81
73
|
return {
|
82
74
|
"description": "Use Markdown headings (#, ##, ###, etc.) to structure content hierarchically.",
|
83
75
|
"when_to_use": "Use to group content into sections and define a visual hierarchy.",
|
76
|
+
"syntax": "## Your Heading Text",
|
77
|
+
"examples": [
|
78
|
+
"# Main Title",
|
79
|
+
"## Section Title",
|
80
|
+
"### Subsection Title",
|
81
|
+
],
|
84
82
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
from typing_extensions import override
|
4
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
5
5
|
|
6
6
|
|
7
7
|
class ImageElement(NotionBlockElement):
|
@@ -22,7 +22,6 @@ class ImageElement(NotionBlockElement):
|
|
22
22
|
+ r"\)$" # closing parenthesis
|
23
23
|
)
|
24
24
|
|
25
|
-
@override
|
26
25
|
@staticmethod
|
27
26
|
def match_markdown(text: str) -> bool:
|
28
27
|
"""Check if text is a markdown image."""
|
@@ -30,13 +29,11 @@ class ImageElement(NotionBlockElement):
|
|
30
29
|
ImageElement.PATTERN.match(text.strip())
|
31
30
|
)
|
32
31
|
|
33
|
-
@override
|
34
32
|
@staticmethod
|
35
33
|
def match_notion(block: Dict[str, Any]) -> bool:
|
36
34
|
"""Check if block is a Notion image."""
|
37
35
|
return block.get("type") == "image"
|
38
36
|
|
39
|
-
@override
|
40
37
|
@staticmethod
|
41
38
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
42
39
|
"""Convert markdown image to Notion image block."""
|
@@ -64,7 +61,6 @@ class ImageElement(NotionBlockElement):
|
|
64
61
|
|
65
62
|
return image_block
|
66
63
|
|
67
|
-
@override
|
68
64
|
@staticmethod
|
69
65
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
70
66
|
"""Convert Notion image block to markdown image."""
|
@@ -103,25 +99,23 @@ class ImageElement(NotionBlockElement):
|
|
103
99
|
result += text_obj.get("plain_text", "")
|
104
100
|
return result
|
105
101
|
|
106
|
-
@override
|
107
102
|
@staticmethod
|
108
103
|
def is_multiline() -> bool:
|
109
104
|
return False
|
110
105
|
|
111
106
|
@classmethod
|
112
|
-
def get_llm_prompt_content(cls) ->
|
107
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
113
108
|
"""
|
114
|
-
Returns
|
115
|
-
Includes description, usage guidance, syntax options, and examples.
|
109
|
+
Returns structured LLM prompt metadata for the image element.
|
116
110
|
"""
|
117
111
|
return {
|
118
112
|
"description": "Embeds an image from an external URL into your document.",
|
119
|
-
"when_to_use":
|
120
|
-
|
121
|
-
"
|
122
|
-
"
|
123
|
-
|
124
|
-
],
|
113
|
+
"when_to_use": (
|
114
|
+
"Use images to include visual content such as diagrams, screenshots, charts, photos, or illustrations "
|
115
|
+
"that enhance your document. Images can make complex information easier to understand, create visual interest, "
|
116
|
+
"or provide evidence for your points."
|
117
|
+
),
|
118
|
+
"syntax": "",
|
125
119
|
"examples": [
|
126
120
|
"",
|
127
121
|
"",
|
@@ -3,6 +3,7 @@ from typing import Dict, Any, Optional, List
|
|
3
3
|
from typing_extensions import override
|
4
4
|
|
5
5
|
from notionary.elements.notion_block_element import NotionBlockElement
|
6
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
7
|
|
7
8
|
|
8
9
|
class MentionElement(NotionBlockElement):
|
@@ -45,7 +46,6 @@ class MentionElement(NotionBlockElement):
|
|
45
46
|
},
|
46
47
|
}
|
47
48
|
|
48
|
-
@override
|
49
49
|
@staticmethod
|
50
50
|
def match_markdown(text: str) -> bool:
|
51
51
|
"""Check if text contains a markdown mention."""
|
@@ -54,7 +54,6 @@ class MentionElement(NotionBlockElement):
|
|
54
54
|
return True
|
55
55
|
return False
|
56
56
|
|
57
|
-
@override
|
58
57
|
@staticmethod
|
59
58
|
def match_notion(block: Dict[str, Any]) -> bool:
|
60
59
|
"""Check if block contains a mention."""
|
@@ -75,7 +74,6 @@ class MentionElement(NotionBlockElement):
|
|
75
74
|
|
76
75
|
return any(text_item.get("type") == "mention" for text_item in rich_text)
|
77
76
|
|
78
|
-
@override
|
79
77
|
@staticmethod
|
80
78
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
81
79
|
"""Convert markdown text with mentions to a Notion paragraph block."""
|
@@ -161,7 +159,6 @@ class MentionElement(NotionBlockElement):
|
|
161
159
|
"color": "default",
|
162
160
|
}
|
163
161
|
|
164
|
-
@override
|
165
162
|
@staticmethod
|
166
163
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
167
164
|
"""Extract mentions from Notion block and convert to markdown format."""
|
@@ -200,28 +197,22 @@ class MentionElement(NotionBlockElement):
|
|
200
197
|
|
201
198
|
return "".join(result)
|
202
199
|
|
203
|
-
@override
|
204
200
|
@staticmethod
|
205
201
|
def is_multiline() -> bool:
|
206
202
|
return False
|
207
203
|
|
208
204
|
@classmethod
|
209
|
-
def get_llm_prompt_content(cls) ->
|
210
|
-
"""
|
205
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
206
|
+
"""
|
207
|
+
Returns structured LLM prompt metadata for the mention element.
|
208
|
+
"""
|
211
209
|
return {
|
212
210
|
"description": "References to Notion pages, databases, or dates within text content.",
|
213
211
|
"when_to_use": "When you want to link to other Notion content within your text.",
|
214
|
-
"syntax": [
|
215
|
-
"@[page-id] - Reference to a Notion page",
|
216
|
-
"@date[YYYY-MM-DD] - Reference to a date",
|
217
|
-
"@db[database-id] - Reference to a Notion database",
|
218
|
-
],
|
212
|
+
"syntax": "@[page-id]",
|
219
213
|
"examples": [
|
220
214
|
"Check the meeting notes at @[1a6389d5-7bd3-80c5-9a87-e90b034989d0]",
|
221
215
|
"Deadline is @date[2023-12-31]",
|
222
216
|
"Use the structure in @db[1a6389d5-7bd3-80e9-b199-000cfb3fa0b3]",
|
223
217
|
],
|
224
|
-
"limitations": [
|
225
|
-
"Mentions require knowing the internal IDs of the pages or databases you want to reference"
|
226
|
-
],
|
227
218
|
}
|
@@ -2,6 +2,8 @@ import inspect
|
|
2
2
|
from typing import Dict, Any, Optional
|
3
3
|
from abc import ABC
|
4
4
|
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
|
+
|
5
7
|
|
6
8
|
class NotionBlockElement(ABC):
|
7
9
|
"""Base class for elements that can be converted between Markdown and Notion."""
|
@@ -37,15 +39,14 @@ class NotionBlockElement(ABC):
|
|
37
39
|
By default, returns the class docstring.
|
38
40
|
"""
|
39
41
|
|
42
|
+
@classmethod
|
43
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
44
|
+
"""
|
45
|
+
Returns a dictionary with information for LLM prompts about this element.
|
46
|
+
This default implementation extracts information from the class docstring.
|
47
|
+
Subclasses should override this method to provide more structured information.
|
40
48
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
This default implementation extracts information from the class docstring.
|
46
|
-
Subclasses should override this method to provide more structured information.
|
47
|
-
|
48
|
-
Returns:
|
49
|
-
Dictionary with documentation information
|
50
|
-
"""
|
51
|
-
return {"description": inspect.cleandoc(cls.__doc__ or ""), "examples": []}
|
49
|
+
Returns:
|
50
|
+
Dictionary with documentation information
|
51
|
+
"""
|
52
|
+
return {"description": inspect.cleandoc(cls.__doc__ or ""), "examples": []}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Any, Optional
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
5
|
+
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
6
|
+
|
7
|
+
|
8
|
+
class NumberedListElement(NotionBlockElement):
|
9
|
+
"""Class for converting between Markdown numbered lists and Notion numbered list items."""
|
10
|
+
|
11
|
+
@staticmethod
|
12
|
+
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
13
|
+
"""Convert markdown numbered list item to Notion block."""
|
14
|
+
pattern = re.compile(r"^\s*(\d+)\.\s+(.+)$")
|
15
|
+
numbered_match = pattern.match(text)
|
16
|
+
if not numbered_match:
|
17
|
+
return None
|
18
|
+
|
19
|
+
content = numbered_match.group(2)
|
20
|
+
|
21
|
+
# Use parse_inline_formatting to handle rich text
|
22
|
+
rich_text = TextInlineFormatter.parse_inline_formatting(content)
|
23
|
+
|
24
|
+
return {
|
25
|
+
"type": "numbered_list_item",
|
26
|
+
"numbered_list_item": {"rich_text": rich_text, "color": "default"},
|
27
|
+
}
|
28
|
+
|
29
|
+
@staticmethod
|
30
|
+
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
31
|
+
"""Convert Notion numbered list item block to markdown."""
|
32
|
+
if block.get("type") != "numbered_list_item":
|
33
|
+
return None
|
34
|
+
|
35
|
+
rich_text = block.get("numbered_list_item", {}).get("rich_text", [])
|
36
|
+
content = TextInlineFormatter.extract_text_with_formatting(rich_text)
|
37
|
+
|
38
|
+
return f"1. {content}"
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def match_markdown(text: str) -> bool:
|
42
|
+
"""Check if this element can handle the given markdown text."""
|
43
|
+
pattern = re.compile(r"^\s*\d+\.\s+(.+)$")
|
44
|
+
return bool(pattern.match(text))
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def match_notion(block: Dict[str, Any]) -> bool:
|
48
|
+
"""Check if this element can handle the given Notion block."""
|
49
|
+
return block.get("type") == "numbered_list_item"
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def is_multiline() -> bool:
|
53
|
+
return False
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
57
|
+
"""
|
58
|
+
Returns structured LLM prompt metadata for the numbered list element.
|
59
|
+
"""
|
60
|
+
return {
|
61
|
+
"description": "Creates numbered list items for ordered sequences.",
|
62
|
+
"when_to_use": "Use for lists where order matters, such as steps, rankings, or sequential items.",
|
63
|
+
"syntax": "1. Item text",
|
64
|
+
"examples": [
|
65
|
+
"1. First step\n2. Second step\n3. Third step",
|
66
|
+
"1. Gather materials\n2. Assemble parts\n3. Test the result",
|
67
|
+
],
|
68
|
+
}
|
@@ -2,6 +2,7 @@ from typing import Dict, Any, Optional
|
|
2
2
|
from typing_extensions import override
|
3
3
|
|
4
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
5
6
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
6
7
|
|
7
8
|
|
@@ -57,17 +58,20 @@ class ParagraphElement(NotionBlockElement):
|
|
57
58
|
return False
|
58
59
|
|
59
60
|
@classmethod
|
60
|
-
def get_llm_prompt_content(cls) ->
|
61
|
-
"""
|
61
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
62
|
+
"""
|
63
|
+
Returns structured LLM prompt metadata for the paragraph element.
|
64
|
+
"""
|
62
65
|
return {
|
63
66
|
"description": "Creates standard paragraph blocks for regular text content.",
|
64
|
-
"when_to_use":
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
"when_to_use": (
|
68
|
+
"Use paragraphs for normal text content. Paragraphs are the default block type and will be used "
|
69
|
+
"when no other specific formatting is applied."
|
70
|
+
),
|
71
|
+
"syntax": "Just write text normally without any special prefix",
|
69
72
|
"examples": [
|
70
73
|
"This is a simple paragraph with plain text.",
|
71
74
|
"This paragraph has **bold** and *italic* formatting.",
|
75
|
+
"You can also include [links](https://example.com) or `inline code`.",
|
72
76
|
],
|
73
77
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from typing import TypedDict, List
|
2
|
+
|
3
|
+
|
4
|
+
class ElementPromptContent(TypedDict):
|
5
|
+
"""
|
6
|
+
Typed dictionary defining the standardized structure for element prompt content.
|
7
|
+
This ensures consistent formatting across all Notion block elements.
|
8
|
+
"""
|
9
|
+
|
10
|
+
description: str
|
11
|
+
"""Concise explanation of what the element is and its purpose in Notion."""
|
12
|
+
|
13
|
+
syntax: str
|
14
|
+
"""The exact markdown syntax pattern used to create this element."""
|
15
|
+
|
16
|
+
examples: List[str]
|
17
|
+
"""List of practical usage examples showing the element in context."""
|
18
|
+
|
19
|
+
when_to_use: str
|
20
|
+
"""Guidelines explaining the appropriate scenarios for using this element."""
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from typing import Type, List
|
2
|
+
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
|
4
|
+
|
5
|
+
class MarkdownSyntaxPromptBuilder:
|
6
|
+
"""
|
7
|
+
Generator for LLM system prompts that describe Notion-Markdown syntax.
|
8
|
+
|
9
|
+
This class extracts information about supported Markdown patterns
|
10
|
+
and formats them optimally for LLMs.
|
11
|
+
"""
|
12
|
+
|
13
|
+
SYSTEM_PROMPT_TEMPLATE = """You are a knowledgeable assistant that helps users create content for Notion pages.
|
14
|
+
Notion supports standard Markdown with some special extensions for creating rich content.
|
15
|
+
|
16
|
+
{element_docs}
|
17
|
+
|
18
|
+
Important usage guidelines:
|
19
|
+
|
20
|
+
1. Do NOT start content with a level 1 heading (# Heading). In Notion, the page title is already displayed in the metadata, so starting with an H1 heading is redundant. Begin with H2 (## Heading) or lower for section headings.
|
21
|
+
|
22
|
+
2. The backtick code fence syntax (```) should ONLY be used when creating actual code blocks or diagrams.
|
23
|
+
Do not wrap examples or regular content in backticks unless you're showing code.
|
24
|
+
|
25
|
+
3. Use inline formatting (bold, italic, etc.) across all content to enhance readability.
|
26
|
+
Proper typography is essential for creating scannable, well-structured documents.
|
27
|
+
|
28
|
+
4. Notion's extensions to Markdown provide richer formatting options than standard Markdown
|
29
|
+
while maintaining the familiar Markdown syntax for basic elements.
|
30
|
+
|
31
|
+
5. Always structure content with clear headings, lists, and paragraphs to create visually appealing
|
32
|
+
and well-organized documents.
|
33
|
+
"""
|
34
|
+
|
35
|
+
@staticmethod
|
36
|
+
def generate_element_doc(element_class: Type[NotionBlockElement]) -> str:
|
37
|
+
"""
|
38
|
+
Generates documentation for a specific NotionBlockElement in a compact format.
|
39
|
+
Uses the element's get_llm_prompt_content method if available.
|
40
|
+
"""
|
41
|
+
class_name = element_class.__name__
|
42
|
+
element_name = class_name.replace("Element", "")
|
43
|
+
|
44
|
+
# Check if the class has the get_llm_prompt_content method
|
45
|
+
if not hasattr(element_class, "get_llm_prompt_content") or not callable(
|
46
|
+
getattr(element_class, "get_llm_prompt_content")
|
47
|
+
):
|
48
|
+
return f"## {element_name}"
|
49
|
+
|
50
|
+
# Get the element content
|
51
|
+
content = element_class.get_llm_prompt_content()
|
52
|
+
|
53
|
+
# Format the element documentation in a compact way
|
54
|
+
doc_parts = [
|
55
|
+
f"## {element_name}",
|
56
|
+
f"{content['description']}",
|
57
|
+
f"**Syntax:** {content['syntax']}",
|
58
|
+
f"**Example:** {content['examples'][0]}" if content["examples"] else "",
|
59
|
+
f"**When to use:** {content['when_to_use']}",
|
60
|
+
]
|
61
|
+
|
62
|
+
# Filter out any empty parts and join with newlines
|
63
|
+
return "\n".join([part for part in doc_parts if part])
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def generate_element_docs(
|
67
|
+
cls, element_classes: List[Type[NotionBlockElement]]
|
68
|
+
) -> str:
|
69
|
+
"""
|
70
|
+
Generates complete documentation for all provided element classes.
|
71
|
+
"""
|
72
|
+
docs = [
|
73
|
+
"# Markdown Syntax for Notion Blocks",
|
74
|
+
"The following Markdown patterns are supported for creating Notion blocks:",
|
75
|
+
]
|
76
|
+
|
77
|
+
# Generate docs for each element
|
78
|
+
for element in element_classes:
|
79
|
+
docs.append("\n" + cls.generate_element_doc(element))
|
80
|
+
|
81
|
+
return "\n".join(docs)
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def generate_system_prompt(
|
85
|
+
cls,
|
86
|
+
element_classes: List[Type[NotionBlockElement]],
|
87
|
+
) -> str:
|
88
|
+
"""
|
89
|
+
Generates a complete system prompt for LLMs.
|
90
|
+
"""
|
91
|
+
element_docs = cls.generate_element_docs(element_classes)
|
92
|
+
return cls.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
|